1 /*
2 * Copyright (c) 2020 - 2023, Intel Corporation
3 *
4 * Permission is hereby granted, free of charge, to any person obtaining a
5 * copy of this software and associated documentation files (the "Software"),
6 * to deal in the Software without restriction, including without limitation
7 * the rights to use, copy, modify, merge, publish, distribute, sublicense,
8 * and/or sell copies of the Software, and to permit persons to whom the
9 * Software is furnished to do so, subject to the following conditions:
10 *
11 * The above copyright notice and this permission notice shall be included
12 * in all copies or substantial portions of the Software.
13 *
14 * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS
15 * OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
16 * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL
17 * THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR
18 * OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE,
19 * ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR
20 * OTHER DEALINGS IN THE SOFTWARE.
21 */
22 //!
23 //! \file     encode_av1_superres.cpp
24 //! \brief    Super-res feature
25 //!
26 
27 #include "encode_av1_superres.h"
28 #include "encode_av1_tile.h"
29 
30 namespace encode
31 {
Av1SuperRes(MediaFeatureManager * featureManager,EncodeAllocator * allocator,void * constSettings)32 Av1SuperRes::Av1SuperRes(MediaFeatureManager *featureManager,
33     EncodeAllocator                          *allocator,
34     void                                     *constSettings):
35     MediaFeature(constSettings)
36 {
37     ENCODE_FUNC_CALL();
38 
39     ENCODE_CHK_NULL_NO_STATUS_RETURN(featureManager);
40     m_featureManager = featureManager;
41     ENCODE_CHK_NULL_NO_STATUS_RETURN(m_featureManager);
42 
43     m_allocator = allocator;
44     ENCODE_CHK_NULL_NO_STATUS_RETURN(m_allocator);
45 }
46 
Init(void * setting)47 MOS_STATUS Av1SuperRes::Init(void *setting)
48 {
49     ENCODE_FUNC_CALL();
50     ENCODE_CHK_NULL_RETURN(setting);
51 
52     m_basicFeature = dynamic_cast<Av1BasicFeature *>(m_featureManager->GetFeature(Av1FeatureIDs::basicFeature));
53     ENCODE_CHK_NULL_RETURN(m_basicFeature);
54 
55     m_trackedBuf = m_basicFeature->m_trackedBuf;
56     ENCODE_CHK_NULL_RETURN(m_trackedBuf);
57 
58     return MOS_STATUS_SUCCESS;
59 }
60 
Update(void * params)61 MOS_STATUS Av1SuperRes::Update(void *params)
62 {
63     ENCODE_FUNC_CALL();
64 
65     EncoderParams *encodeParams = (EncoderParams *)params;
66     ENCODE_CHK_NULL_RETURN(encodeParams);
67 
68     auto av1SeqParams = static_cast<PCODEC_AV1_ENCODE_SEQUENCE_PARAMS>(encodeParams->pSeqParams);
69     ENCODE_CHK_NULL_RETURN(av1SeqParams);
70 
71     auto av1PicParams = static_cast<PCODEC_AV1_ENCODE_PICTURE_PARAMS>(encodeParams->pPicParams);
72     ENCODE_CHK_NULL_RETURN(av1SeqParams);
73 
74     m_oriFrameHeight       = av1PicParams->frame_height_minus1 + 1;
75     m_oriAlignedFrameWidth = av1PicParams->frame_width_minus1 + 1;
76 
77     uint16_t picHeightInMb = (uint16_t)CODECHAL_GET_HEIGHT_IN_MACROBLOCKS(m_oriFrameHeight);
78     m_frameHeight          = picHeightInMb * CODECHAL_MACROBLOCK_HEIGHT;
79 
80     m_enabled     = av1SeqParams->CodingToolFlags.fields.enable_superres;
81     m_useSuperRes = av1PicParams->PicFlags.fields.use_superres;
82 
83     ENCODE_CHK_COND_RETURN(!m_enabled && m_useSuperRes == true, "Super-res disabled in SPS, but enabled in PPS!");
84     ENCODE_CHK_COND_RETURN(m_enabled && !m_useSuperRes && av1PicParams->superres_scale_denominator != av1ScaleNumerator, "Super-res not used in current frame, but scale denominator is not 8!");
85 
86     if (m_enabled && m_useSuperRes)
87     {
88         m_superResDenom = av1PicParams->superres_scale_denominator;
89         ENCODE_CHK_COND_RETURN(m_superResDenom < 9 || m_superResDenom > 16, "Wrong value of super-res denominator, should be in range [9, 16]!");
90 
91         // HW restriction of SuperRes denominator for SuperRes + LoopRestoration
92         ENCODE_CHK_COND_RETURN(av1SeqParams->CodingToolFlags.fields.enable_restoration && m_superResDenom % 2 == 1, "When both SuperRes and LoopRestoration are enabled, only valid SuperRes denominator values are 10, 12, 14 and 16");
93 
94         m_frameWidthDs = ((m_oriAlignedFrameWidth << 3) + (m_superResDenom >> 1)) / m_superResDenom;
95 
96         // update DDI width so that subsequent features will use downscaled width
97         av1PicParams->frame_width_minus1 = m_frameWidthDs - 1;
98     }
99     else
100     {
101         m_frameWidthDs  = m_oriAlignedFrameWidth;
102         m_superResDenom = av1ScaleNumerator;
103     }
104 
105     int8_t subsamplingX = /*SequenceChromaSubSamplingFormat != chromaIdc444 ? 1 : 0*/ 1;
106     m_subsamplingX[0]   = 0;
107     m_subsamplingX[1]   = subsamplingX;
108     m_subsamplingX[2]   = subsamplingX;
109 
110     ENCODE_CHK_STATUS_RETURN(m_basicFeature->m_ref.UpdateRefFrameSize(m_oriAlignedFrameWidth, av1PicParams->frame_height_minus1 + 1));
111 
112     if (m_enabled)
113     {
114         m_basicFeature->m_ref.SetPostCdefAsEncRef(true);
115 
116         m_widthChanged = m_prevDsWidth != av1PicParams->frame_width_minus1 + 1;
117 
118         ENCODE_CHK_NULL_RETURN(encodeParams->psRawSurface);
119         m_raw.resource = encodeParams->psRawSurface;
120         m_allocator->GetSurfaceInfo(m_raw.resource);
121 
122         ENCODE_CHK_STATUS_RETURN(PrepareRawSurface());
123 
124         PrepareVeSfcDownscalingParam(m_raw, m_rawDs, m_downScalingParams);
125 
126         m_prevDsWidth = m_frameWidthDs;
127     }
128 
129     return MOS_STATUS_SUCCESS;
130 }
131 
InitMMCState(EncodeMemComp * mmcState)132 MOS_STATUS Av1SuperRes::InitMMCState(EncodeMemComp *mmcState)
133 {
134     ENCODE_CHK_NULL_RETURN(mmcState);
135     m_mmcState = mmcState;
136     return MOS_STATUS_SUCCESS;
137 }
138 
SetSurfaceMMCParams(EncodeMemComp & mmcState,MOS_SURFACE & surf)139 static inline MOS_STATUS SetSurfaceMMCParams(EncodeMemComp &mmcState, MOS_SURFACE &surf)
140 {
141     ENCODE_CHK_STATUS_RETURN(mmcState.SetSurfaceMmcMode(&surf));
142     ENCODE_CHK_STATUS_RETURN(mmcState.SetSurfaceMmcState(&surf));
143     ENCODE_CHK_STATUS_RETURN(mmcState.SetSurfaceMmcFormat(&surf));
144     surf.bIsCompressed = surf.CompressionMode != MOS_MMC_DISABLED;
145 
146     return MOS_STATUS_SUCCESS;
147 }
148 
PrepareRawSurface()149 MOS_STATUS Av1SuperRes::PrepareRawSurface()
150 {
151     ENCODE_FUNC_CALL();
152 
153     if (m_enabled)
154     {
155         m_raw.unalignedWidth  = GetUpscaledWidth();
156         m_raw.unalignedHeight = m_oriFrameHeight;
157 
158         ENCODE_CHK_NULL_RETURN(m_mmcState);
159 
160         if (m_widthChanged)
161         {
162             if (!Mos_ResourceIsNull(&m_rawDs.resource->OsResource))
163             {
164                 m_allocator->DestroySurface(m_rawDs.resource);
165             }
166 
167             MOS_ALLOC_GFXRES_PARAMS allocParamsForBuffer2D;
168             MOS_ZeroMemory(&allocParamsForBuffer2D, sizeof(MOS_ALLOC_GFXRES_PARAMS));
169 
170             allocParamsForBuffer2D.Type         = MOS_GFXRES_2D;
171             allocParamsForBuffer2D.TileType     = MOS_TILE_Y;
172             allocParamsForBuffer2D.Format       = m_raw.resource->Format;
173             allocParamsForBuffer2D.dwWidth      = MOS_ALIGN_CEIL(m_frameWidthDs, av1SuperBlockWidth);
174             allocParamsForBuffer2D.dwHeight     = MOS_ALIGN_CEIL(m_frameHeight, av1SuperBlockHeight);
175             allocParamsForBuffer2D.ResUsageType = MOS_HW_RESOURCE_USAGE_ENCODE_INTERNAL_READ_WRITE_CACHE;
176             allocParamsForBuffer2D.pBufName     = "superResEncRawSurface";
177 
178             if (m_mmcState->IsMmcEnabled())
179             {
180                 allocParamsForBuffer2D.CompressionMode = MOS_MMC_MC;
181                 allocParamsForBuffer2D.bIsCompressible = true;
182             }
183 
184             m_rawDs.resource = m_allocator->AllocateSurface(allocParamsForBuffer2D, false);
185             ENCODE_CHK_NULL_RETURN(m_rawDs.resource);
186             ENCODE_CHK_STATUS_RETURN(m_allocator->GetSurfaceInfo(m_rawDs.resource));
187 
188             m_rawDs.unalignedWidth  = m_frameWidthDs;
189             m_rawDs.unalignedHeight = m_oriFrameHeight;
190         }
191 
192         if (m_mmcState->IsMmcEnabled())
193         {
194             SetSurfaceMMCParams(*m_mmcState, *m_raw.resource);
195             SetSurfaceMMCParams(*m_mmcState, *m_rawDs.resource);
196         }
197     }
198 
199     return MOS_STATUS_SUCCESS;
200 }
201 
PrepareVeSfcDownscalingParam(const SURFACE & inSurf,const SURFACE & outSurf,VEBOX_SFC_PARAMS & params)202 MOS_STATUS Av1SuperRes::PrepareVeSfcDownscalingParam(const SURFACE &inSurf, const SURFACE &outSurf, VEBOX_SFC_PARAMS &params)
203 {
204     ENCODE_FUNC_CALL();
205 
206     params.input.surface      = inSurf.resource;
207     params.input.chromaSiting = 0;
208     params.input.rcSrc        = {0, 0, (long)inSurf.unalignedWidth, (long)inSurf.unalignedHeight};
209     params.input.rotation     = ROTATION_IDENTITY;
210 
211     params.output.surface      = outSurf.resource;
212     params.output.chromaSiting = 0;
213     params.output.rcDst        = {0, 0, (long)outSurf.unalignedWidth, (long)outSurf.unalignedHeight};
214 
215     switch (inSurf.resource->Format)
216     {
217     case Format_NV12:
218     case Format_P010:
219         params.input.colorSpace  = CSpace_Any;
220         params.output.colorSpace = CSpace_Any;
221         break;
222     case Format_A8R8G8B8:
223     case Format_A8B8G8R8:
224         params.input.colorSpace  = CSpace_sRGB;
225         params.output.colorSpace = CSpace_sRGB;
226         break;
227     default:
228         params.input.colorSpace  = CSpace_Any;
229         params.output.colorSpace = CSpace_Any;
230         break;
231     }
232 
233     return MOS_STATUS_SUCCESS;
234 }
235 
MHW_SETPAR_DECL_SRC(AVP_PIC_STATE,Av1SuperRes)236 MHW_SETPAR_DECL_SRC(AVP_PIC_STATE, Av1SuperRes)
237 {
238     params.enableSuperres = m_useSuperRes;
239 
240     return MOS_STATUS_SUCCESS;
241 }
242 
243 static constexpr int32_t RS_SUBPEL_BITS       = 6;
244 static constexpr int32_t RS_SCALE_SUBPEL_BITS = 14;
245 static constexpr int32_t RS_SCALE_EXTRA_BITS  = RS_SCALE_SUBPEL_BITS - RS_SUBPEL_BITS;
246 static constexpr int32_t RS_SCALE_EXTRA_OFF   = 1 << (RS_SCALE_EXTRA_BITS - 1);
247 static constexpr int32_t RS_SCALE_SUBPEL_MASK = (1 << RS_SCALE_SUBPEL_BITS) - 1;
248 
GetUpscaleConvolveStep(int32_t inLength,int32_t outLength)249 static inline int32_t GetUpscaleConvolveStep(int32_t inLength, int32_t outLength)
250 {
251     return ((inLength << RS_SCALE_SUBPEL_BITS) + outLength / 2) / outLength;
252 }
253 
GetUpscaleConvolveX0(int32_t inLength,int32_t outLength,int32_t xStepQn)254 static inline int32_t GetUpscaleConvolveX0(int32_t inLength, int32_t outLength, int32_t xStepQn)
255 {
256     const int32_t err = outLength * xStepQn - (inLength << RS_SCALE_SUBPEL_BITS);
257     const int32_t x0  = (-((outLength - inLength) << (RS_SCALE_SUBPEL_BITS - 1)) + outLength / 2) / outLength +
258                        RS_SCALE_EXTRA_OFF - err / 2;
259 
260     return static_cast<int32_t>(static_cast<uint32_t>(x0) & RS_SCALE_SUBPEL_MASK);
261 }
262 
MHW_SETPAR_DECL_SRC(AVP_INLOOP_FILTER_STATE,Av1SuperRes)263 MHW_SETPAR_DECL_SRC(AVP_INLOOP_FILTER_STATE, Av1SuperRes)
264 {
265     params.superresUpscaledWidthMinus1 = m_oriAlignedFrameWidth - 1;
266     params.superresDenom               = m_superResDenom;
267 
268     int32_t xStepQn[3];
269     int32_t x0Qn[3];
270 
271     if (m_enabled && m_useSuperRes)
272     {
273         for (int32_t plane = 0; plane < 2; plane++)
274         {
275             int32_t downscaledPlaneWidth = ROUND_POWER_OF_TWO(m_frameWidthDs, m_subsamplingX[plane]);
276             int32_t upscaledPlaneWidth   = ROUND_POWER_OF_TWO(m_oriAlignedFrameWidth, m_subsamplingX[plane]);
277 
278             xStepQn[plane] = GetUpscaleConvolveStep(downscaledPlaneWidth, upscaledPlaneWidth);
279             x0Qn[plane]    = GetUpscaleConvolveX0(downscaledPlaneWidth, upscaledPlaneWidth, xStepQn[plane]);
280         }
281 
282         xStepQn[2] = xStepQn[1];
283         x0Qn[2]    = x0Qn[1];
284 
285     params.lumaPlaneXStepQn   = xStepQn[0];
286     params.chromaPlaneXStepQn = xStepQn[1];
287     }
288 
289     auto tileFeature = dynamic_cast<Av1EncodeTile *>(m_featureManager->GetFeature(Av1FeatureIDs::encodeTile));
290     ENCODE_CHK_NULL_RETURN(tileFeature);
291 
292     EncodeTileData tileData = {};
293     tileFeature->GetCurrentTile(tileData);
294 
295     if (m_enabled && m_useSuperRes)
296     {
297         /* This calculation is the same with CModel but will cause hang issue for multi-tile tests on EMU
298         params.lumaPlaneX0Qn   = x0Qn[0] + params.lumaPlaneXStepQn * lumaXPos * m_superResDenom / av1ScaleNumerator;
299         params.chromaPlaneX0Qn = x0Qn[1] + params.chromaPlaneXStepQn * chromaXPos * m_superResDenom / av1ScaleNumerator;
300         */
301         params.lumaPlaneX0Qn   = x0Qn[0];
302         params.chromaPlaneX0Qn = x0Qn[1];
303     }
304 
305     return MOS_STATUS_SUCCESS;
306 }
307 }  // namespace encode
308