[FFmpeg-devel] [PATCH v08.01 2/3] fbtile tile/detile, hwcontext_drm detile NonLinear

hanishkvc hanishkvc at gmail.com
Sun Jul 12 20:21:01 EEST 2020


** fbtile cpu based framebuffer tile/detile helpers

Add helper routines which can be used to tile/detile framebuffer
layouts between linear and specified tile layout, using the cpu.

Currently it supports Legacy Intel Tile-X, Legacy Intel Tile-Y and
Newer Intel Tile-Yf tiled layouts.

Currently supported pixel format is 32bit RGB.

It internally contains fbtile_generic logic, which can be easily
configured to support different kinds of tiling layouts, at the
expense of some additional processing cycles, compared to developing
custom (de)tiling logic. One requires to provide the tile walking
parameters for the new tile layout to be supported. Once it is done,
both tiling and detiling of the new tile layout can be handled
automatically.

Its basic functionality is exposed as a simple ff_fbtile_frame_copy
function along with few related funcs. This is inturn used by
hwcontext_drm.

** hwcontext_drm detile non linear layout, if possible

If the framebuffer is a tiled layout, use the fbtile helper routines
to try and detile it into linear layout, if supported by fbtile.

It uses the format_modifier associated with the framebuffer to decide
whether to apply detiling or not and inturn which specific detiling
to apply.

If user is using kmsgrab, they will have to use -format_modifer option
of kmsgrab to force a specific detile logic, in case they dont want to
use the original format_modifier related detiling. Or they could even
use -format_modifier 0 to make hwcontext_drm bypass this detiling.

** NOTE This depends on the related KMSGrab GetFB2 patch, for enhanced
functionality, where detiling is automatically applied where possible.
---
 Changelog                 |   2 +
 libavutil/Makefile        |   2 +
 libavutil/fbtile.c        | 482 ++++++++++++++++++++++++++++++++++++++
 libavutil/fbtile.h        | 134 +++++++++++
 libavutil/hwcontext_drm.c |  38 ++-
 5 files changed, 657 insertions(+), 1 deletion(-)
 create mode 100644 libavutil/fbtile.c
 create mode 100644 libavutil/fbtile.h

diff --git a/Changelog b/Changelog
index 20ba03ae8b..0b48858da7 100644
--- a/Changelog
+++ b/Changelog
@@ -6,6 +6,8 @@ version <next>:
 - MacCaption demuxer
 - PGX decoder
 - kmsgrab GetFB2 format_modifier, if user doesnt specify
+- fbtile cpu based framebuffer tile/detile helpers (Intel TileX|Y|Yf)
+- hwcontext_drm detiles non linear layouts, if possible
 
 
 version 4.3:
diff --git a/libavutil/Makefile b/libavutil/Makefile
index 9b08372eb2..9b58ac5980 100644
--- a/libavutil/Makefile
+++ b/libavutil/Makefile
@@ -84,6 +84,7 @@ HEADERS = adler32.h                                                     \
           xtea.h                                                        \
           tea.h                                                         \
           tx.h                                                          \
+          fbtile.h                                                      \
 
 HEADERS-$(CONFIG_LZO)                   += lzo.h
 
@@ -169,6 +170,7 @@ OBJS = adler32.o                                                        \
        tx_float.o                                                       \
        tx_double.o                                                      \
        tx_int32.o                                                       \
+       fbtile.o                                                         \
        video_enc_params.o                                               \
 
 
diff --git a/libavutil/fbtile.c b/libavutil/fbtile.c
new file mode 100644
index 0000000000..f741e1aeca
--- /dev/null
+++ b/libavutil/fbtile.c
@@ -0,0 +1,482 @@
+/*
+ * CPU based Framebuffer Generic Tile DeTile logic
+ * Copyright (c) 2020 C Hanish Menon <HanishKVC>
+ *
+ * This file is part of FFmpeg.
+ *
+ * FFmpeg is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public
+ * License as published by the Free Software Foundation; either
+ * version 2.1 of the License, or (at your option) any later version.
+ *
+ * FFmpeg is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public
+ * License along with FFmpeg; if not, write to the Free Software
+ * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
+ */
+
+#include "config.h"
+#include "avutil.h"
+#include "common.h"
+#include "fbtile.h"
+#if CONFIG_LIBDRM
+#include <drm_fourcc.h>
+#endif
+
+
+/**
+ * Ok return value
+ */
+#define FBT_OK 0
+
+
+enum FFFBTileLayout ff_fbtile_getlayoutid(enum FFFBTileFamily family, uint64_t familyTileType)
+{
+    enum FFFBTileLayout layout = FF_FBTILE_UNKNOWN;
+
+    switch(family) {
+    case FF_FBTILE_FAMILY_DRM:
+#if CONFIG_LIBDRM
+        switch(familyTileType) {
+        case DRM_FORMAT_MOD_LINEAR:
+            layout = FF_FBTILE_NONE;
+            break;
+        case I915_FORMAT_MOD_X_TILED:
+            layout = FF_FBTILE_INTEL_XGEN9;
+            break;
+        case I915_FORMAT_MOD_Y_TILED:
+            layout = FF_FBTILE_INTEL_YGEN9;
+            break;
+        case I915_FORMAT_MOD_Yf_TILED:
+            layout = FF_FBTILE_INTEL_YF;
+            break;
+        default:
+            layout = FF_FBTILE_UNKNOWN;
+            break;
+        }
+#else
+        av_log(NULL, AV_LOG_WARNING, "fbtile:getlayoutid: family[%d] familyTileType[%ld]\n", family, familyTileType);
+#endif
+        break;
+    default:
+        av_log(NULL, AV_LOG_WARNING, "fbtile:getlayoutid: unknown family[%d] familyTileType[%ld]\n", family, familyTileType);
+    }
+    av_log(NULL, AV_LOG_VERBOSE, "fbtile:getlayoutid: family[%d] familyTileType[%ld] maps to layoutid[%d]\n", family, familyTileType, layout);
+    return layout;
+}
+
+
+/**
+ * Supported pixel formats
+ * Currently only RGB based 32bit formats are specified
+ * TODO: Technically the logic is transparent to 16bit RGB formats also to a great extent
+ */
+const enum AVPixelFormat fbtilePixFormats[] = {
+                                        AV_PIX_FMT_RGB0, AV_PIX_FMT_0RGB, AV_PIX_FMT_BGR0, AV_PIX_FMT_0BGR,
+                                        AV_PIX_FMT_RGBA, AV_PIX_FMT_ARGB, AV_PIX_FMT_BGRA, AV_PIX_FMT_ABGR,
+                                        AV_PIX_FMT_NONE};
+
+int ff_fbtile_checkpixformats(const enum AVPixelFormat srcPixFormat, const enum AVPixelFormat dstPixFormat)
+{
+    int errSrc = 1;
+    int errDst = 1;
+    for (int i = 0; fbtilePixFormats[i] != AV_PIX_FMT_NONE; i++) {
+        if (fbtilePixFormats[i] == srcPixFormat)
+            errSrc = 0;
+        if (fbtilePixFormats[i] == dstPixFormat)
+            errDst = 0;
+    }
+    return (errSrc | errDst);
+}
+
+
+/*
+ * Generic tile/detile logic
+ * The tile layout data is assumed to be tightly packed, with no gaps inbetween.
+ * However the logic does try to accomodate a src/dst linear layout memory,
+ * where there is possibly some additional bytes beyond the width in each line
+ * of pixel data.
+ */
+
+
+/**
+ * TileWalk Direction Change Entry
+ * Used to specify the tile walking of subtiles within a tile.
+ */
+struct FBTWDirChange {
+    int posOffset;
+    int xDelta;
+    int yDelta;
+};
+
+
+/**
+ * TileWalk, Contains info required for a given tile walking.
+ *
+ * @field bytesPerPixel the bytes per pixel for the image
+ * @field subTileWidth the width of subtile within the tile, in pixels
+ * @field subTileHeight the height of subtile within the tile, in pixels
+ * @field tileWidth the width of the tile, in pixels
+ * @field tileHeight the height of the tile, in pixels
+ * @field numDirChanges the number of dir changes involved in tile walk
+ * @field dirChanges the array of dir changes for the tile walk required
+ */
+struct FBTileWalk {
+    int bytesPerPixel;
+    int subTileWidth, subTileHeight;
+    int tileWidth, tileHeight;
+    int numDirChanges;
+    struct FBTWDirChange dirChanges[];
+};
+
+
+/**
+ * Settings for Intel Tile-Yf framebuffer layout.
+ * May need to swap the 4 pixel wide subtile, have to check doc bit more
+ */
+static struct FBTileWalk tyfTileWalk = {
+                    .bytesPerPixel = 4,
+                    .subTileWidth = 4, .subTileHeight = 8,
+                    .tileWidth = 32, .tileHeight = 32,
+                    .numDirChanges = 6,
+                    .dirChanges = { {8, 4, 0}, {16, -4, 8}, {32, 4, -8}, {64, -12, 8}, {128, 4, -24}, {256, 4, -24} }
+                };
+
+/**
+ * Setting for Intel Tile-X framebuffer layout
+ */
+static struct FBTileWalk txTileWalk = {
+                    .bytesPerPixel = 4,
+                    .subTileWidth = 128, .subTileHeight = 8,
+                    .tileWidth = 128, .tileHeight = 8,
+                    .numDirChanges = 1,
+                    .dirChanges = { {8, 128, 0} }
+                };
+
+/**
+ * Setting for Intel Tile-Y framebuffer layout
+ * Even thou a simple generic detiling logic doesnt require the
+ * dummy 256 posOffset entry. The pseudo parallel detiling based
+ * opti logic requires to know about the Tile boundry.
+ */
+static struct FBTileWalk tyTileWalk = {
+                    .bytesPerPixel = 4,
+                    .subTileWidth = 4, .subTileHeight = 32,
+                    .tileWidth = 32, .tileHeight = 32,
+                    .numDirChanges = 2,
+                    .dirChanges = { {32, 4, 0}, {256, 4, 0} }
+                };
+
+
+/**
+ * Generic Logic to Tile/Detile between tiled and linear layout.
+ *
+ * @param op whether to tile or detile
+ * @param w width of the image
+ * @param h height of the image
+ * @param dst the destination image buffer
+ * @param dstLineSize the size of each row in dst image, in bytes
+ * @param src the source image buffer
+ * @param srcLineSize the size of each row in src image, in bytes
+ * @param tw the structure which contains the tile walk parameters
+ *
+ * @return 0 if detiled, 1 if not
+ */
+
+
+/**
+ * _fbtile_generic_simple tile/detile layout
+ */
+static int _fbtile_generic_simple(enum FFFBTileOps op,
+                                   const int w, const int h,
+                                   uint8_t *dst, const int dstLineSize,
+                                   uint8_t *src, const int srcLineSize,
+                                   const int bytesPerPixel,
+                                   const int subTileWidth, const int subTileHeight,
+                                   const int tileWidth, const int tileHeight,
+                                   const int numDirChanges, const struct FBTWDirChange *dirChanges)
+{
+    int tO, lO;
+    int lX, lY;
+    int cSTL, nSTLines;
+    uint8_t *tld, *lin;
+    int tldLineSize, linLineSize;
+    const int subTileWidthBytes = subTileWidth*bytesPerPixel;
+
+    if (op == FF_FBTILE_OPS_TILE) {
+        lin = src;
+        linLineSize = srcLineSize;
+        tld = dst;
+        tldLineSize = dstLineSize;
+    } else {
+        tld = src;
+        tldLineSize = srcLineSize;
+        lin = dst;
+        linLineSize = dstLineSize;
+    }
+
+    // To keep things sane and simple tile layout is assumed to be tightly packed,
+    // so below check is a indirect logical assumption, even thou tldLineSize is not directly mappable at one level
+    if (w*bytesPerPixel != tldLineSize) {
+        av_log(NULL, AV_LOG_ERROR, "fbtile:genericsimp: w%dxh%d, tldLineSize%d, linLineSize%d\n", w, h, tldLineSize, linLineSize);
+        av_log(NULL, AV_LOG_ERROR, "fbtile:genericsimp: dont support tldLineSize | Pitch going beyond width\n");
+        return AVERROR(EINVAL);
+    }
+    tO = 0;
+    lX = 0;
+    lY = 0;
+    nSTLines = (w*h)/subTileWidth;  // numSubTileLines
+    cSTL = 0;                       // curSubTileLine
+    while (cSTL < nSTLines) {
+        lO = lY*linLineSize + lX*bytesPerPixel;
+#ifdef DEBUG_FBTILE
+        av_log(NULL, AV_LOG_DEBUG, "fbtile:genericsimp: lX%d lY%d; lO%d, tO%d; %d/%d\n", lX, lY, lO, tO, cSTL, nSTLines);
+#endif
+
+        for (int k = 0; k < subTileHeight; k++) {
+            if (op == FF_FBTILE_OPS_TILE) {
+                memcpy(tld+tO+k*subTileWidthBytes, lin+lO+k*linLineSize, subTileWidthBytes);
+            } else {
+                memcpy(lin+lO+k*linLineSize, tld+tO+k*subTileWidthBytes, subTileWidthBytes);
+            }
+        }
+        tO = tO + subTileHeight*subTileWidthBytes;
+
+        cSTL += subTileHeight;
+        for (int i=numDirChanges-1; i>=0; i--) {
+            if ((cSTL%dirChanges[i].posOffset) == 0) {
+                lX += dirChanges[i].xDelta;
+                lY += dirChanges[i].yDelta;
+                break;
+            }
+        }
+        if (lX >= w) {
+            lX = 0;
+            lY += tileHeight;
+        }
+    }
+    return FBT_OK;
+}
+
+
+static int fbtile_generic_simple(enum FFFBTileOps op,
+                           const int w, const int h,
+                           uint8_t *dst, const int dstLineSize,
+                           uint8_t *src, const int srcLineSize,
+                           const struct FBTileWalk *tw)
+{
+    return _fbtile_generic_simple(op, w, h,
+                                   dst, dstLineSize, src, srcLineSize,
+                                   tw->bytesPerPixel,
+                                   tw->subTileWidth, tw->subTileHeight,
+                                   tw->tileWidth, tw->tileHeight,
+                                   tw->numDirChanges, tw->dirChanges);
+}
+
+
+/**
+ * Generic tile/detile minimal optimised version.
+ */
+static int _fbtile_generic_opti(enum FFFBTileOps op,
+                                 const int w, const int h,
+                                 uint8_t *dst, const int dstLineSize,
+                                 uint8_t *src, const int srcLineSize,
+                                 const int bytesPerPixel,
+                                 const int subTileWidth, const int subTileHeight,
+                                 const int tileWidth, const int tileHeight,
+                                 const int numDirChanges, const struct FBTWDirChange *dirChanges)
+{
+    int tO, lO, tOPrev;
+    int lX, lY;
+    int cSTL, nSTLines;
+    int curTileInRow, nTilesInARow;
+    uint8_t *tld, *lin;
+    int tldLineSize, linLineSize;
+    const int subTileWidthBytes = subTileWidth*bytesPerPixel;
+    int parallel = 1;
+
+    if (op == FF_FBTILE_OPS_TILE) {
+        lin = src;
+        linLineSize = srcLineSize;
+        tld = dst;
+        tldLineSize = dstLineSize;
+    } else {
+        tld = src;
+        tldLineSize = srcLineSize;
+        lin = dst;
+        linLineSize = dstLineSize;
+    }
+
+    if (w*bytesPerPixel != tldLineSize) {
+        av_log(NULL, AV_LOG_ERROR, "fbtile:genericopti: w%dxh%d, linLineSize%d, tldLineSize%d\n", w, h, linLineSize, tldLineSize);
+        av_log(NULL, AV_LOG_ERROR, "fbtile:genericopti: dont support tldLineSize | Pitch going beyond width\n");
+        return AVERROR(EINVAL);
+    }
+    if (w%tileWidth != 0) {
+        av_log(NULL, AV_LOG_ERROR, "fbtile:genericopti:NotSupported:Width being non-mult Of TileWidth: width%d, tileWidth%d\n", w, tileWidth);
+        return AVERROR(EINVAL);
+    }
+    tO = 0;
+    tOPrev = 0;
+    lX = 0;
+    lY = 0;
+    nTilesInARow = w/tileWidth;
+    for (parallel=8; parallel>0; parallel--) {
+        if (nTilesInARow%parallel == 0)
+            break;
+    }
+    nSTLines = (w*h)/subTileWidth;  // numSubTileLines
+    cSTL = 0;                       // curSubTileLine
+    curTileInRow = 0;
+    while (cSTL < nSTLines) {
+        lO = lY*linLineSize + lX*bytesPerPixel;
+#ifdef DEBUG_FBTILE
+        av_log(NULL, AV_LOG_DEBUG, "fbtile:genericopti: lX%d lY%d; tO%d, lO%d; %d/%d\n", lX, lY, tO, lO, cSTL, nSTLines);
+#endif
+
+        // As many tiling layouts have subtile and walk sizes which are multiples of 4,
+        // so this loop has been unrolled to be multiples of 4, and speed up a bit.
+        // If this condition is not satisfied, esp along vert dir, then use fbtile_generic_simple.
+        // (De)tile parallely and gain some speed by allowing reuse of some calcs and parallelism.
+        if (op == FF_FBTILE_OPS_DETILE) {
+            for (int k = 0; k < subTileHeight; k+=4) {
+                for (int p = 0; p < parallel; p++) {
+                    int pTldOffset = p*tileWidth*tileHeight*bytesPerPixel;
+                    int pLinOffset = p*tileWidth*bytesPerPixel;
+                    memcpy(lin+lO+(k+0)*linLineSize+pLinOffset, tld+tO+(k+0)*subTileWidthBytes+pTldOffset, subTileWidthBytes);
+                    memcpy(lin+lO+(k+1)*linLineSize+pLinOffset, tld+tO+(k+1)*subTileWidthBytes+pTldOffset, subTileWidthBytes);
+                    memcpy(lin+lO+(k+2)*linLineSize+pLinOffset, tld+tO+(k+2)*subTileWidthBytes+pTldOffset, subTileWidthBytes);
+                    memcpy(lin+lO+(k+3)*linLineSize+pLinOffset, tld+tO+(k+3)*subTileWidthBytes+pTldOffset, subTileWidthBytes);
+                }
+            }
+        } else {
+            for (int k = 0; k < subTileHeight; k+=4) {
+                for (int p = 0; p < parallel; p++) {
+                    int pTldOffset = p*tileWidth*tileHeight*bytesPerPixel;
+                    int pLinOffset = p*tileWidth*bytesPerPixel;
+                    memcpy(tld+tO+(k+0)*subTileWidthBytes+pTldOffset, lin+lO+(k+0)*linLineSize+pLinOffset, subTileWidthBytes);
+                    memcpy(tld+tO+(k+1)*subTileWidthBytes+pTldOffset, lin+lO+(k+1)*linLineSize+pLinOffset, subTileWidthBytes);
+                    memcpy(tld+tO+(k+2)*subTileWidthBytes+pTldOffset, lin+lO+(k+2)*linLineSize+pLinOffset, subTileWidthBytes);
+                    memcpy(tld+tO+(k+3)*subTileWidthBytes+pTldOffset, lin+lO+(k+3)*linLineSize+pLinOffset, subTileWidthBytes);
+                }
+            }
+        }
+
+        tO = tO + subTileHeight*subTileWidthBytes;
+        cSTL += subTileHeight;
+
+        for (int i=numDirChanges-1; i>=0; i--) {
+            if ((cSTL%dirChanges[i].posOffset) == 0) {
+                if (i == numDirChanges-1) {
+                    curTileInRow += parallel;
+                    lX = curTileInRow*tileWidth;
+                    tO = tOPrev + tileWidth*tileHeight*bytesPerPixel*(parallel);
+                    tOPrev = tO;
+                } else {
+                    lX += dirChanges[i].xDelta;
+                }
+                lY += dirChanges[i].yDelta;
+		break;
+            }
+        }
+        if (lX >= w) {
+            lX = 0;
+            curTileInRow = 0;
+            lY += tileHeight;
+            if (lY >= h) {
+                break;
+            }
+        }
+    }
+    return FBT_OK;
+}
+
+
+static int fbtile_generic_opti(enum FFFBTileOps op,
+                         const int w, const int h,
+                         uint8_t *dst, const int dstLineSize,
+                         uint8_t *src, const int srcLineSize,
+                         const struct FBTileWalk *tw)
+{
+    return _fbtile_generic_opti(op, w, h,
+                                 dst, dstLineSize, src, srcLineSize,
+                                 tw->bytesPerPixel,
+                                 tw->subTileWidth, tw->subTileHeight,
+                                 tw->tileWidth, tw->tileHeight,
+                                 tw->numDirChanges, tw->dirChanges);
+}
+
+
+static int fbtile_conv(enum FFFBTileOps op, enum FFFBTileLayout layout,
+                 int w, int h,
+                 uint8_t *dst, int dstLineSize,
+                 uint8_t *src, int srcLineSize,
+                 int bytesPerPixel)
+{
+    static int logStateNone = 0;
+    static int logStateUnknown = 0;
+
+    switch(layout) {
+    case FF_FBTILE_NONE:
+        av_log_once(NULL, AV_LOG_WARNING, AV_LOG_VERBOSE, &logStateNone, "fbtile:conv:FF_FBTILE_NONE: not (de)tiling\n");
+        return AVERROR(EALREADY);
+    case FF_FBTILE_INTEL_XGEN9:
+        return fbtile_generic_opti(op, w, h, dst, dstLineSize, src, srcLineSize, &txTileWalk);
+    case FF_FBTILE_INTEL_YGEN9:
+        return fbtile_generic_opti(op, w, h, dst, dstLineSize, src, srcLineSize, &tyTileWalk);
+    case FF_FBTILE_INTEL_YF:
+        return fbtile_generic_opti(op, w, h, dst, dstLineSize, src, srcLineSize, &tyfTileWalk);
+    default:
+        av_log_once(NULL, AV_LOG_WARNING, AV_LOG_VERBOSE, &logStateUnknown, "fbtile:conv: unknown layout [%d] specified, not (de)tiling\n", layout);
+        return AVERROR(EINVAL);
+    }
+}
+
+
+/*
+ * Copy one AVFrame into another, in the process tiling or detiling as required, if possible.
+ * NOTE: Either the Source or the Destination AVFrame (i.e one of them) should be linear.
+ * NOTE: If the tiling layout is not understood, it falls back to av_frame_copy.
+ */
+int ff_fbtile_frame_copy(AVFrame *dst, enum FFFBTileLayout dstTileLayout, AVFrame *src, enum FFFBTileLayout srcTileLayout,
+                         enum FFFBTileFrameCopyStatus *status)
+{
+    int err;
+
+    if (dstTileLayout == FF_FBTILE_NONE) {         // i.e DeTile
+        err = ff_fbtile_checkpixformats(src->format, dst->format);
+        if (!err) {
+            err = fbtile_conv(FF_FBTILE_OPS_DETILE, srcTileLayout,
+                                dst->width, dst->height,
+                                dst->data[0], dst->linesize[0],
+                                src->data[0], src->linesize[0], 4);
+            if (!err) {
+                *status = FF_FBTILE_FRAMECOPY_TILECOPY;
+                return FBT_OK;
+            }
+        }
+    } else if (srcTileLayout == FF_FBTILE_NONE) {  // i.e Tile
+        err = ff_fbtile_checkpixformats(src->format, dst->format);
+        if (!err) {
+            err = fbtile_conv(FF_FBTILE_OPS_TILE, dstTileLayout,
+                                src->width, src->height,
+                                dst->data[0], dst->linesize[0],
+                                src->data[0], src->linesize[0], 4);
+            if (!err) {
+                *status = FF_FBTILE_FRAMECOPY_TILECOPY;
+                return FBT_OK;
+            }
+        }
+    } else {
+        av_log(NULL, AV_LOG_WARNING, "fbtile:framecopy: both src [%d] and dst [%d] layouts cant be tiled\n", srcTileLayout, dstTileLayout);
+    }
+    *status = FF_FBTILE_FRAMECOPY_COPYONLY;
+    return av_frame_copy(dst, src);
+}
+
+
+// vim: set expandtab sts=4: //
diff --git a/libavutil/fbtile.h b/libavutil/fbtile.h
new file mode 100644
index 0000000000..83360952b1
--- /dev/null
+++ b/libavutil/fbtile.h
@@ -0,0 +1,134 @@
+/*
+ * CPU based Framebuffer Generic Tile DeTile logic
+ * Copyright (c) 2020 C Hanish Menon <HanishKVC>
+ *
+ * This file is part of FFmpeg.
+ *
+ * FFmpeg is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public
+ * License as published by the Free Software Foundation; either
+ * version 2.1 of the License, or (at your option) any later version.
+ *
+ * FFmpeg is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public
+ * License along with FFmpeg; if not, write to the Free Software
+ * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
+ */
+
+#ifndef AVUTIL_FBTILE_H
+#define AVUTIL_FBTILE_H
+
+#include <stdint.h>
+#include "libavutil/pixfmt.h"
+#include "libavutil/frame.h"
+
+/**
+ * @file
+ * @brief CPU based Framebuffer tiler detiler
+ * @author C Hanish Menon <HanishKVC>
+ * @{
+ */
+
+
+/**
+ * Enable printing of the tile walk
+ */
+//#define DEBUG_FBTILE 1
+
+
+/**
+ * The FBTile related operations
+ */
+enum FFFBTileOps {
+    FF_FBTILE_OPS_NONE,
+    FF_FBTILE_OPS_TILE,
+    FF_FBTILE_OPS_DETILE,
+    FF_FBTILE_OPS_UNKNOWN,
+};
+
+/**
+ * The FBTile layout families
+ * Used to help map from an external subsystem like say drm
+ * to fbtile's internal tile layout id.
+ */
+enum FFFBTileFamily {
+    FF_FBTILE_FAMILY_DRM,
+    FF_FBTILE_FAMILY_UNKNOWN,
+};
+
+/**
+ * The FBTile related Layouts
+ * This identifies the supported tile layouts
+ */
+enum FFFBTileLayout {
+    FF_FBTILE_NONE,            // This also corresponds to linear layout
+    FF_FBTILE_INTEL_XGEN9,
+    FF_FBTILE_INTEL_YGEN9,
+    FF_FBTILE_INTEL_YF,
+    FF_FBTILE_UNKNOWN,
+};
+
+/**
+ * FBTile FrameCopy additional status
+ */
+enum FFFBTileFrameCopyStatus {
+    FF_FBTILE_FRAMECOPY_TILECOPY,
+    FF_FBTILE_FRAMECOPY_COPYONLY
+};
+
+
+/**
+ * Identify equivalent fbtile tile layout id given an external subsystem's tile layout id.
+ *
+ * @param family identifies the subsystem
+ * @param familyTileType the tile layout id as defined by the subsystem
+ *
+ * @return the fbtile's equivalent tile layout id
+ */
+enum FFFBTileLayout ff_fbtile_getlayoutid(enum FFFBTileFamily family, uint64_t familyTileType);
+
+
+/**
+ * Supported pixel formats by the fbtile logics
+ */
+extern const enum AVPixelFormat fbtilePixFormats[];
+
+/**
+ * Check if the given pixel formats are supported by fbtile logic.
+ *
+ * @param srcPixFormat pixel format of source image
+ * @param dstPixFormat pixel format of destination image
+ *
+ * @return 0 if supported, 1 if not
+ */
+int ff_fbtile_checkpixformats(const enum AVPixelFormat srcPixFormat, const enum AVPixelFormat dstPixFormat);
+
+
+/**
+ * Copy one AVFrame into another, in the process tiling or detiling as required, if possible.
+ * NOTE: Either the Source or the Destination AVFrame (i.e one of them) should be linear.
+ * NOTE: If the tiling layout is not understood, it falls back to av_frame_copy.
+ *
+ * @param dst the destination avframe
+ * @param dstTileLayout the framebuffer tiling layout expected for the destination avframe
+ * @param src the source avframe
+ * @param srcTileLayout the framebuffer tiling layout of the source avframe
+ * @param status helps identify if only copy was done or (de)tile+copy was done
+ *
+ * @return 0 if copied.
+ */
+int ff_fbtile_frame_copy(AVFrame *dst, enum FFFBTileLayout dstTileLayout,
+                         AVFrame *src, enum FFFBTileLayout srcTileLayout,
+                         enum FFFBTileFrameCopyStatus *status);
+
+
+/**
+ * @}
+ */
+
+#endif /* AVUTIL_FBTILE_H */
+// vim: set expandtab sts=4: //
diff --git a/libavutil/hwcontext_drm.c b/libavutil/hwcontext_drm.c
index 32cbde82eb..c72ea405ca 100644
--- a/libavutil/hwcontext_drm.c
+++ b/libavutil/hwcontext_drm.c
@@ -21,6 +21,7 @@
 #include <unistd.h>
 
 #include <drm.h>
+#include <drm_fourcc.h>
 #include <xf86drm.h>
 
 #include "avassert.h"
@@ -28,6 +29,7 @@
 #include "hwcontext_drm.h"
 #include "hwcontext_internal.h"
 #include "imgutils.h"
+#include "fbtile.h"
 
 
 static void drm_device_free(AVHWDeviceContext *hwdev)
@@ -185,6 +187,40 @@ static int drm_transfer_get_formats(AVHWFramesContext *ctx,
     return 0;
 }
 
+/**
+ * As AVFrame doesnt support tile layout natively, so if detile is successful
+ * the same is notified to any other users by updating the corresponding
+ * hardware AVFrame's tile layout info.
+ * If this is not needed, #define HWCTXDRM_SYNCRELATED_FORMATMODIFIER 0
+ */
+#ifndef HWCTXDRM_SYNCRELATED_FORMATMODIFIER
+#define HWCTXDRM_SYNCRELATED_FORMATMODIFIER 1
+#endif
+static int drm_transfer_with_detile(const AVFrame *hwAVFrame, AVFrame *dst, AVFrame *src)
+{
+    int err;
+    uint64_t formatModifier;
+    enum FFFBTileLayout srcFBTileLayout, dstFBTileLayout;
+    enum FFFBTileFrameCopyStatus status;
+    AVDRMFrameDescriptor *drmFrame = NULL;
+
+    srcFBTileLayout = FF_FBTILE_NONE;
+    dstFBTileLayout = FF_FBTILE_NONE;
+    if (hwAVFrame->format  == AV_PIX_FMT_DRM_PRIME) {
+        drmFrame = (AVDRMFrameDescriptor*)hwAVFrame->data[0];
+        formatModifier = drmFrame->objects[0].format_modifier;
+        srcFBTileLayout = ff_fbtile_getlayoutid(FF_FBTILE_FAMILY_DRM, formatModifier);
+    }
+    err = ff_fbtile_frame_copy(dst, dstFBTileLayout, src, srcFBTileLayout, &status);
+#if HWCTXDRM_SYNCRELATED_FORMATMODIFIER
+    if (!err && (status == FF_FBTILE_FRAMECOPY_TILECOPY)) {
+        if (drmFrame != NULL)
+            drmFrame->objects[0].format_modifier = DRM_FORMAT_MOD_LINEAR;
+    }
+#endif
+    return err;
+}
+
 static int drm_transfer_data_from(AVHWFramesContext *hwfc,
                                   AVFrame *dst, const AVFrame *src)
 {
@@ -206,7 +242,7 @@ static int drm_transfer_data_from(AVHWFramesContext *hwfc,
     map->width  = dst->width;
     map->height = dst->height;
 
-    err = av_frame_copy(dst, map);
+    err = drm_transfer_with_detile(src, dst, map);
     if (err)
         goto fail;
 
-- 
2.25.1



More information about the ffmpeg-devel mailing list