C

在屏幕上获取图形

在屏幕上获取图形

本章解释如何实现平台接口以将像素渲染到屏幕。它描述了如何实现无硬件层的单个屏幕的基本支持。请参阅下一章以实现对支持硬件层的硬件的实现。

概述

Qt Quick Ultralite 核心将 QML 场景转换为一系列渲染命令,以便将其混合到帧缓冲区。提供此帧缓冲区并呈现到屏幕是平台库的责任。如果目标硬件带有硬件加速图形支持,平台库还提供针对其的渲染命令实现。

实现基本图形

Qt Quick Ultralite 核心通过 Qul::Platform::PlatformContext 实例与平台硬件通信。要在屏幕上显示内容,平台必须实现与该上下文相关的图形部分。要实现的第一种方法是 PlatformContext::availableScreens

注意:示例实现假定只有一个屏幕没有额外的层。

Qul::PlatformInterface::Screen *ExamplePlatform::availableScreens(size_t *screenCount) const
{
    *screenCount = sizeof(platformScreens) / sizeof(PlatformInterface::Screen);
    return platformScreens;
}

PlatformContext::availableScreens 返回设备上可用屏幕的数组。返回的 Qul::PlatformInterface::Screen 实例提供了屏幕标识符、其像素尺寸和其颜色格式。它还告诉 Qt Quick Ultralite 核心屏幕是否支持调整大小,在实际情况中意味着平台是否可以支持多个分辨率或帧缓冲区尺寸。在示例中,我们依赖于 Screen 构造函数的默认行为,该行为不支持调整大小。

如果您的设备支持多个屏幕,则应在其实例构造函数中为每个屏幕提供一个名称作为标识符。

接下来,实现 PlatformContext::initializeDisplay 方法,该方法在应用程序希望使用的每个屏幕上调用一次。如果屏幕支持调整大小,它将根据根 QML 项的大小调整自己。根据屏幕尺寸,平台库现在可以初始化显示硬件。

void ExamplePlatform::initializeDisplay(const PlatformInterface::Screen *screen)
{
    // const int screenIndex = screen - platformScreens;
    // initLcd(screenIndex, screen->size().width(), screen->size().height());
}

您还需要提供帧缓冲区以进行渲染。如果屏幕大小固定,您可以像这样静态分配它们:

// Assuming we use 32bpp framebuffers
static const int BytesPerPixel = 4;
unsigned char framebuffer[2][BytesPerPixel * ScreenWidth * ScreenHeight]
    __attribute__((section(".framebufferSection")));
static int backBufferIndex = 0;

通常使用两个帧缓冲区以获得更好的性能,并且直接从内存中刷新显示硬件所需的显示。一个是后缓冲区,Qt Quick Ultralite 引擎将下一帧渲染进其中,另一个是前缓冲区,它将显示在屏幕上。要报告使用此缓冲类型,从 Qul::Platform::FlippedDoubleBuffering 返回 PlatformContext::frameBufferingType。变量 backBufferIndex 用于跟踪哪一个帧缓冲区当前用作后缓冲区。

FrameBufferingType ExamplePlatform::frameBufferingType(const PlatformInterface::LayerEngine::ItemLayer *) const
{
    return FlippedDoubleBuffering;
}

Qt Quick Ultralite 引擎使用从 PlatformContext::frameBufferingType 返回的值,仅对屏幕上更改的部分进行局部渲染更新,而不是每次帧重绘整个帧缓冲区。

注意:某些自定义架构可能根本不使用帧缓冲区,例如使用命令缓冲区即时更新显示。在这种情况下,必须从 Qul::Platform::OtherBuffering 返回通知 Qt Quick Ultralite 引擎必须每帧进行完全重绘。

某些平台可能没有足够的 RAM 来容纳单个完整的帧缓冲区。使用 局部帧缓冲区 可以显着降低应用程序的内存要求,但可能导致性能降低和潜在的视觉撕裂伪影。在这种情况下,必须从 Qul::Platform::PartialBuffering 返回通知 Qt Quick Ultralite 引擎应将部分更新拆分以适应部分帧缓冲区大小。

然后,您将准备就绪,以使 Qt Quick Ultralite 核心能够将渲染放入您自己的帧缓冲区。

如果选择完整帧缓冲区,则在需要视觉更新时,Qt Quick Ultralite 核心对每帧调用一次 PlatformContext::beginFramePlatformContext::endFrame

如果选择局部缓冲区,Qt Quick Ultralite 核心将针对需要视觉更新的每个区域调用一次 beginFrameendFrame。更新的区域以 rectPlatformContext::beginFrame 中提供。有关更多信息,请参见 实现局部帧缓冲区支持

beginFrame 返回一个包含

  • 像素格式和屏幕或层大小,
  • 用于渲染的帧缓冲区的指针,
  • 每行的字节数,
  • 以及用于渲染到缓冲区的绘图引擎。

如果您完全依赖于 Qt Quick Ultralite 核心中包含的默认软件渲染,则可以在此使用纯 Qul::PlatformInterface::DrawingEngine 而不是 ExampleDrawingEngine。通过继承绘图引擎,平台可以启用到缓冲区的硬件加速绘图。如何进行此操作的详细解释将在后续部分中说明。

PlatformInterface::DrawingDevice *ExamplePlatform::beginFrame(const PlatformInterface::LayerEngine::ItemLayer *layer,
                                                              const PlatformInterface::Rect &rect,
                                                              int refreshInterval)
{
    if (enableLayerEngine)
        return ExampleLayerEngine::beginFrame(layer, rect, refreshInterval);

    static ExampleDrawingEngine drawingEngine;

    requestedRefreshInterval = refreshInterval;

    // Wait until the back buffer is free, i.e. no longer held by the display
    waitForBufferFlip();

    // A pointer to the back buffer
    uchar *bits = framebuffer[backBufferIndex];
    static PlatformInterface::DrawingDevice buffer(Qul::PixelFormat_RGB32,
                                                   PlatformInterface::Size(ScreenWidth, ScreenHeight),
                                                   bits,
                                                   ScreenWidth * BytesPerPixel,
                                                   &drawingEngine);

    buffer.setBits(bits);
    return &buffer;
}

waitForBufferFlip 调用以确保后缓冲区(前一个帧的前缓冲区)已被显示控制器释放并准备好进行渲染。如何在 presentFrame 上下文中实现它的解释将在稍后说明。

在完成给定屏幕的帧渲染后,将调用 PlatformContext::endFrame。对于多个屏幕或层的情况,某些平台可能希望在此时刷新硬件加速混合单元的命令缓冲区,但在大多数情况下可以将其留空。

void ExamplePlatform::endFrame(const PlatformInterface::LayerEngine::ItemLayer *layer)
{
    if (enableLayerEngine)
        ExampleLayerEngine::endFrame(layer);
}

在屏幕渲染后,Qt Quick Ultralite 核心会调用函数 PlatformContext::presentFrame。在这里,平台应使刚刚渲染的缓冲区在显示器上可见。该示例使用双缓冲,因此将后缓冲区的指针传递到显示器,并交换前后缓冲区,以便下一帧在之前帧的前缓冲区上进行渲染。

FrameStatistics ExamplePlatform::presentFrame(const PlatformInterface::Screen *screen,
                                              const PlatformInterface::Rect &rect)
{
    if (enableLayerEngine)
        return ExampleLayerEngine::presentFrame(screen, rect);

    // HW_SyncFramebufferForCpuAccess();

    synchronizeAfterCpuAccess(rect);
    framebufferAccessedByCpu = false;

    FrameStatistics stats;
    stats.refreshDelta = refreshCount - requestedRefreshInterval;
    waitForRefreshInterval();
    static const int RefreshRate = 60; // screen refresh rate in Hz
    stats.remainingBudget = idleTimeWaitingForDisplay + stats.refreshDelta * int(1000.0f / RefreshRate);

    waitingForBufferFlip = true;
    idleTimeWaitingForDisplay = 0;

    // Now we can update the framebuffer address
    // LCD_SetBufferAddr(framebuffer[backBufferIndex]);

    // Now the front and back buffers are swapped
    if (backBufferIndex == 0)
        backBufferIndex = 1;
    else
        backBufferIndex = 0;

    return stats;
}

在硬件加速混合的情况下,presentFrame 首先与硬件加速混合单元同步,以确保所有挂起的混合命令都已完全提交到后缓冲区。

此外,对于某些平台,函数 synchronizeAfterCpuAccess 将非常有用。如果任何 CPU 渲染回退被使用,它将使任何数据缓存失效,在允许异步读取从显示控制器访问内存之前。

static bool framebufferAccessedByCpu = false;

static void synchronizeAfterCpuAccess(const PlatformInterface::Rect &rect)
{
    if (framebufferAccessedByCpu) {
        unsigned char *backBuffer = framebuffer[backBufferIndex];
        for (int i = 0; i < rect.height(); ++i) {
            unsigned char *pixels = backBuffer + (ScreenWidth * (rect.y() + i) + rect.x()) * BytesPerPixel;
            // CleanInvalidateDCache_by_Addr(pixels, rect.width() * BytesPerPixel);
        }
    }
}

presentFrame 函数接着调用 waitForRefreshInterval,以便在 Qt Quick Ultralite 核心请求时降低刷新率。这需要跟踪自上次调用 presentFrame 以来发生的刷新次数,并等待直到该计数达到请求的刷新间隔。在 FrameStatistics 结构中对 refreshDeltaremainingBudget 值的更改是有必要实现帧跳过补偿,该内容将在稍后解释。

static void waitForRefreshInterval()
{
    if (refreshCount < requestedRefreshInterval) {
        uint64_t startTime = getPlatformInstance()->currentTimestamp();
        while (refreshCount < requestedRefreshInterval) {
            // The device may yield or go into sleep mode
        }
        idleTimeWaitingForDisplay += getPlatformInstance()->currentTimestamp() - startTime;
    }

    refreshCount = 0;
}

这假设还存在一个跟踪屏幕刷新次数的中断处理程序

static volatile int refreshCount = 1;
volatile unsigned int currentFrame = 0;

// Note: This line incrementing the refreshCount will need to be moved to the
// actual interrupt handler for the display available on the target platform. It
// needs to be called once per vertical refresh of the display, to keep track of
// how many refreshes have happened between calls to presentFrame in order to
// support custom refresh intervals. On some implementations this can be done
// using a so called "line event".
void LCD_RefreshInterruptHandler()
{
    ++refreshCount;

    // currentFrame is only needed if the layer backend is used
    ++currentFrame;
}

最后,presentFrame 可以指示显示控制器从后缓冲区而不是当前持有的前缓冲区开始刷新。这是异步发生的,因为显示控制器可能仍在扫描前缓冲区以进行显示。因此,有必要将 waitingForBufferFlip 设置为 true,并使用中断处理程序接收显示控制器的通知,当旧的前缓冲区被释放并可以用作新后缓冲区进行绘制时。

static volatile bool waitingForBufferFlip = false;
static uint32_t idleTimeWaitingForDisplay = 0;

static void waitForBufferFlip()
{
    // Has there already been a buffer flip?
    if (!waitingForBufferFlip)
        return;
    const uint64_t startTime = getPlatformInstance()->currentTimestamp();
    while (waitingForBufferFlip) {
        // The device may yield or go into sleep mode
    }

    idleTimeWaitingForDisplay = getPlatformInstance()->currentTimestamp() - startTime;
}

// Note: This line clearing waitingForBufferFlip will need to be moved to the
// actual interrupt handler for the display available on the target platform.
// It's needed to inform about when the buffer address used to scan out pixels
// to the display has been updated, making the buffer free in order to start
// drawing the next frame.
void LCD_BufferFlipInterruptHandler()
{
    waitingForBufferFlip = false;
}

idleTimeWaitingForDisplay 跟踪花费在等待显示器上的时间,因此这是可以用作渲染的时间。它用于实现帧跳过补偿,该内容将在下文解释。

帧跳过补偿

PlatformContext::presentFrame 的返回值是一个 Qul::Platform::FrameStatistics 值,Qt Quick Ultralite 核心使用此值来实现帧跳过补偿以实现更平滑的动画效果。 FrameStatistics 包含 Qul::Platform::FrameStatistics::refreshDelta,表示前一个帧相对于其目标帧延迟了多少。因此,如果最后一帧的渲染时间过长,最终显示在请求刷新间隔之后(这意味着发生了帧跳过),则必须将 refreshDelta 设置为 1。

另一方面,如果请求的刷新间隔是 2,并且渲染速度足够快,以至于可以使用刷新间隔 1 来避免帧跳过,则必须将 refreshDelta 设置为 -1。

此外,FrameStatistics 有一个值 Qul::Platform::FrameStatistics::remainingBudget。这表示在跳过帧的情况下可以在渲染上额外花费多少时间(以毫秒为单位),假设 refreshDelta 已添加到交换间隔。如果此值非常低,Qt Quick Ultralite 核心可能会先行增加交换间隔,以降低在动画期间跳过任何帧的风险,通过临时降低刷新率并确保动画帧最终在正确的时间被显示。

如果不需要帧跳过补偿,可以返回默认构造的 FrameStatistics 值。

在示例中,refreshDelta 是计算为请求的刷新间隔与从上次显示帧以来实际发生的刷新次数之间的差异。剩余的预算是在添加refreshDelta到刷新间隔的情况下,本可花费在渲染上的时间。这可以通过将用于空闲等待显示的时间加上refreshDelta乘以以毫秒为单位的屏幕刷新间隔来计算。

FrameStatistics stats;
stats.refreshDelta = refreshCount - requestedRefreshInterval;
waitForRefreshInterval();
static const int RefreshRate = 60; // screen refresh rate in Hz
stats.remainingBudget = idleTimeWaitingForDisplay + stats.refreshDelta * int(1000.0f / RefreshRate);

硬件加速

要实现硬件加速,请扩展 Qul::PlatformInterface::DrawingEngine 并覆盖平台能加速的功能。

class ExampleDrawingEngine : public PlatformInterface::DrawingEngine
{
public:
    void blendRect(PlatformInterface::DrawingDevice *drawingDevice,
                   const PlatformInterface::Rect &rect,
                   PlatformInterface::Rgba32 color,
                   BlendMode blendMode) QUL_DECL_OVERRIDE;

    void blendRoundedRect(PlatformInterface::DrawingDevice *drawingDevice,
                          const PlatformInterface::Rect &rect,
                          const PlatformInterface::Rect &clipRect,
                          PlatformInterface::Rgba32 color,
                          int radius,
                          BlendMode blendMode) QUL_DECL_OVERRIDE;

    void blendImage(PlatformInterface::DrawingDevice *drawingDevice,
                    const PlatformInterface::Point &pos,
                    const PlatformInterface::Texture &source,
                    const PlatformInterface::Rect &sourceRect,
                    int sourceOpacity,
                    BlendMode blendMode) QUL_DECL_OVERRIDE;

    void blendAlphaMap(PlatformInterface::DrawingDevice *drawingDevice,
                       const PlatformInterface::Point &pos,
                       const PlatformInterface::Texture &source,
                       const PlatformInterface::Rect &sourceRect,
                       PlatformInterface::Rgba32 color,
                       BlendMode blendMode) QUL_DECL_OVERRIDE;

    void blendTransformedImage(PlatformInterface::DrawingDevice *drawingDevice,
                               const PlatformInterface::Transform &transform,
                               const PlatformInterface::RectF &destinationRect,
                               const PlatformInterface::Texture &source,
                               const PlatformInterface::RectF &sourceRect,
                               const PlatformInterface::Rect &clipRect,
                               int sourceOpacity,
                               BlendMode blendMode);
    void blendTransformedAlphaMap(PlatformInterface::DrawingDevice *drawingDevice,
                                  const PlatformInterface::Transform &transform,
                                  const PlatformInterface::RectF &destinationRect,
                                  const PlatformInterface::Texture &source,
                                  const PlatformInterface::RectF &sourceRect,
                                  const PlatformInterface::Rect &clipRect,
                                  PlatformInterface::Rgba32 color,
                                  BlendMode blendMode);

    void synchronizeForCpuAccess(PlatformInterface::DrawingDevice *drawingDevice,
                                 const PlatformInterface::Rect &rect) QUL_DECL_OVERRIDE;

    PlatformInterface::DrawingEngine::Path *allocatePath(const PlatformInterface::PathData *pathData,
                                                         PlatformInterface::PathFillRule fillRule) QUL_DECL_OVERRIDE;

    void setStrokeProperties(PlatformInterface::DrawingEngine::Path *path,
                             const PlatformInterface::StrokeProperties &strokeProperties) QUL_DECL_OVERRIDE;

    void blendPath(PlatformInterface::DrawingDevice *drawingDevice,
                   PlatformInterface::DrawingEngine::Path *path,
                   const PlatformInterface::Transform &transform,
                   const PlatformInterface::Rect &clipRect,
                   const PlatformInterface::Brush *fillBrush,
                   const PlatformInterface::Brush *strokeBrush,
                   int sourceOpacity,
                   PlatformInterface::DrawingEngine::BlendMode blendMode) QUL_DECL_OVERRIDE;
};

如果Qul::PlatformInterface::DrawingEngine::blendRectQul::PlatformInterface::DrawingEngine::blendRoundedRectQul::PlatformInterface::DrawingEngine::blendImageQul::PlatformInterface::DrawingEngine::blendAlphaMapQul::PlatformInterface::DrawingEngine::blendTransformedImageQul::PlatformInterface::DrawingEngine::blendTransformedAlphaMap 中的任何一个功能没有被覆盖,则会使用默认实现。这通常在调用Qul::PlatformInterface::DrawingEngine::synchronizeForCpuAccess后调用Qul::PlatformInterface::DrawingDevice::fallbackDrawingEngine上的对应回退实现。如果平台能够部分加速某些混合功能,例如仅针对特定的混合模式或透明度参数,则平台本身可以使用fallbackDrawingEngine来回退到某个参数集合。

警告:如果您在使用fallbackDrawingEngine时遇到崩溃,那可能是因为您没有在initializePlatform()中调用Qul::PlatformInterface::init32bppRendering()或类似的代码。

注意:如果blendImage函数默认使用软件渲染或使用fallbackDrawingEngine,则假设源图像为ARGB32_Premultiplied格式。为了启用与其他格式混合,请将QUL_PLATFORM_DEFAULT_RESOURCE_ALPHA_OPTIONS设置为"Always"。

混合函数的实际实现可能根据硬件加速API而有很大差异。为了使实现这些函数的任务变得更容易,使用了一个假想的硬件加速API进行演示。例如,下面是Qul::PlatformInterface::DrawingEngine::blendRect实现的示例:

void ExampleDrawingEngine::blendRect(PlatformInterface::DrawingDevice *drawingDevice,
                                     const PlatformInterface::Rect &rect,
                                     PlatformInterface::Rgba32 color,
                                     BlendMode blendMode)
{
    // Implement rectangle blending here

    // If only blitting is supported by the hardware, this is how to use the
    // fallback drawing engine for the blending path.
    if (color.alpha() != 255 && blendMode != BlendMode_SourceOver) {
        synchronizeForCpuAccess(drawingDevice, rect);
        drawingDevice->fallbackDrawingEngine()->blendRect(drawingDevice, rect, color, blendMode);
        return;
    }

    // HW_SetColor(1.0f, 1.0f, 1.0f, sourceOpacity * (1 / 256.0f));

    // HW_BlitRect(toHwRect(rect));
}

此示例代码假设硬件加速API不提供矩形混合支持,仅用于演示如何使用回退绘图引擎来完成这项任务。

否则,将设置颜色,并发出blit矩形的调用。

Qul::PlatformInterface::DrawingEngine::blendRoundedRect在混合圆角矩形时被调用。平台实现还必须检查提供给剪辑矩形的剪辑矩形是否小于此要混合的矩形。例如实现请参阅

void ExampleDrawingEngine::blendRoundedRect(PlatformInterface::DrawingDevice *drawingDevice,
                                            const PlatformInterface::Rect &rect,
                                            const PlatformInterface::Rect &clipRect,
                                            PlatformInterface::Rgba32 color,
                                            int radius,
                                            BlendMode blendMode)
{
    // Implement rectangle blending here

    // HW_SetColor(1.0f, 1.0f, 1.0f, sourceOpacity * (1 / 256.0f));
    // HW_SetClip(clipRect.x(), clipRect.y(), clipRect.width(), clipRect.height());

    // HW_BlitRoundRect(toHwRect(rect), radius);

    // HW_SetClip(0, 0, screen->width(), screen->height());
}

类似地,当混合图像和通道图时,您还需要以某种方式通知硬件加速API关于纹理布局和数据。示例假设有一个处理此的bindTexture函数。此函数必须根据特定硬件加速API的需求由平台版本实施。

对于blendImage,存在一个源矩形、一个目标点和一个从0到256的范围内的源不透明度。以下是其实施可能的样子:

void ExampleDrawingEngine::blendImage(PlatformInterface::DrawingDevice *drawingDevice,
                                      const PlatformInterface::Point &pos,
                                      const PlatformInterface::Texture &source,
                                      const PlatformInterface::Rect &sourceRect,
                                      int sourceOpacity,
                                      BlendMode blendMode)
{
    // Fall back to default CPU drawing engine for pixel formats that can't be
    // blended with hardware acceleration
    if (source.format() == Qul::PixelFormat_RGB332 || source.format() == PixelFormat_RLE_ARGB32
        || source.format() == PixelFormat_RLE_ARGB32_Premultiplied || source.format() == PixelFormat_RLE_RGB32
        || source.format() == PixelFormat_RLE_RGB888) {
        DrawingEngine::blendImage(drawingDevice, pos, source, sourceRect, sourceOpacity, blendMode);
        return;
    }

    // Implement image blending here

    // HW_SetBlendMode(toHwBlendMode(blendMode));

    // bindTexture(source);

    // HW_SetColor(1.0f, 1.0f, 1.0f, sourceOpacity * (1 / 256.0f));

    // const Rect destinationRect(pos, sourceRect.size());
    // HW_BlendTexture(toHwRect(sourceRect), toHwRect(destinationRect));
}

接下来,blendAlphaMap非常类似,主要区别在于纹理将具有PixelFormat_Alpha1PixelFormat_Alpha8的像素格式,分别表示每个像素有一个位或一个字节表示不透明度。对于每个像素,在不透明度值被混合或位图之前,必须将其乘以给定的color

void ExampleDrawingEngine::blendAlphaMap(PlatformInterface::DrawingDevice *drawingDevice,
                                         const PlatformInterface::Point &pos,
                                         const PlatformInterface::Texture &source,
                                         const PlatformInterface::Rect &sourceRect,
                                         PlatformInterface::Rgba32 color,
                                         BlendMode blendMode)
{
    // Implement alpha map blending here

    // HW_SetBlendMode(toHwBlendMode(blendMode));

    // bindTexture(source);

    // const float inv = 1 / 255.0f;
    // HW_SetColor(color.red() * inv, color.green() * inv, color.blue() * inv, color.alpha() * inv);

    // const PlatformInterface::Rect destinationRect(pos, sourceRect.size());
    // HW_BlendTexture(toHwRect(sourceRect), toHwRect(destinationRect));
}

接下来是图像和alpha图的可转换混合。如果平台支持加速此操作,blendTransformedImage的实现可能如下:

void ExampleDrawingEngine::blendTransformedImage(PlatformInterface::DrawingDevice *drawingDevice,
                                                 const PlatformInterface::Transform &transform,
                                                 const PlatformInterface::RectF &destinationRect,
                                                 const PlatformInterface::Texture &source,
                                                 const PlatformInterface::RectF &sourceRect,
                                                 const PlatformInterface::Rect &clipRect,
                                                 int sourceOpacity,
                                                 BlendMode blendMode)
{
    // Fall back to default CPU drawing engine for pixel formats that can't be
    // blended with hardware acceleration
    if (source.format() == Qul::PixelFormat_RGB332 || source.format() == PixelFormat_RLE_ARGB32
        || source.format() == PixelFormat_RLE_ARGB32_Premultiplied || source.format() == PixelFormat_RLE_RGB32
        || source.format() == PixelFormat_RLE_RGB888) {
        DrawingEngine::blendTransformedImage(drawingDevice,
                                             transform,
                                             destinationRect,
                                             source,
                                             sourceRect,
                                             clipRect,
                                             sourceOpacity,
                                             blendMode);
        return;
    }

    // Implement transformed image blending here

    // float matrix[16];
    // toHwMatrix(transform, &matrix);

    // HW_SetTransformMatrix(matrix);
    // HW_SetClip(clipRect.x(), clipRect.y(), clipRect.width(), clipRect.height());

    // HW_SetBlendMode(toHwBlendMode(blendMode));

    // bindTexture(source);

    // HW_SetColor(1.0f, 1.0f, 1.0f, sourceOpacity * (1 / 256.0f));

    // HW_BlendTexture(toHwRect(sourceRect), toHwRect(destinationRect));

    // HW_SetClip(0, 0, screen->width(), screen->height());
    // HW_SetTransformIdentity();
}

除了设置裁剪矩形外,你还需要将给定的转换转换为硬件加速API接受的某些矩阵表示形式。如果使用4x4矩阵,转换可能类似于以下示例toHwMatrix

static void toHwMatrix(const PlatformInterface::Transform &transform, float *matrix)
{
    matrix[0] = transform.m11();
    matrix[1] = transform.m12();
    matrix[2] = 0;
    matrix[3] = 0;
    matrix[4] = transform.m21();
    matrix[5] = transform.m22();
    matrix[6] = 0;
    matrix[7] = 0;
    matrix[8] = 0;
    matrix[9] = 0;
    matrix[10] = 1;
    matrix[11] = 0;
    matrix[12] = transform.dx();
    matrix[13] = transform.dy();
    matrix[14] = 0;
    matrix[15] = 1;
}

blendTransformedAlphaMap示例代码看起来与blendTransformedImage非常相似,除了考虑颜色外,这在上述blendAlphaMap示例代码中已经展示。有关blendTransformedAlphaMap示例代码,请参阅文件platform/boards/qt/example-baremetal/platform_context.cpp

由于硬件混合通常异步发生,如果任何基于CPU的读取或写入到帧缓冲区发生,Qt Quick Ultralite核心将调用Qul::PlatformInterface::DrawingEngine::synchronizeForCpuAccess。此函数需要与硬件加速混合单元同步,以确保每个挂起的混合命令已完全提交到帧缓冲区。以下是这与我们的假硬件加速API看起来可能的样子:

void ExampleDrawingEngine::synchronizeForCpuAccess(PlatformInterface::DrawingDevice *drawingDevice,
                                                   const PlatformInterface::Rect &rect)
{
    // HW_SyncFramebufferForCpuAccess();

    framebufferAccessedByCpu = true;

    unsigned char *backBuffer = framebuffer[backBufferIndex];
    for (int i = 0; i < rect.height(); ++i) {
        unsigned char *pixels = backBuffer + (ScreenWidth * (rect.y() + i) + rect.x()) * BytesPerPixel;
        // CleanInvalidateDCache_by_Addr(pixels, rect.width() * BytesPerPixel);
    }
}

在某些硬件上,可能还需要使涉及区域的缓存无效。这确保 blending 单元执行的异步写入被 CPU 全部看到。

此外,还需要实现两个更多函数,以确保 Qt Quick Ultralite 核心可以动态读取和写入纹理数据。它们是PlatformContext::waitUntilAsyncReadFinishedPlatformContext::flushCachesForAsyncRead

它们的具体实现可能如下所示:

void ExamplePlatform::waitUntilAsyncReadFinished(const void * /*begin*/, const void * /*end*/)
{
    // HW_SyncFramebufferForCpuAccess();
}

void ExamplePlatform::flushCachesForAsyncRead(const void * /*addr*/, size_t /*length*/)
{
    // CleanInvalidateDCache_by_Addr(const_cast<void *>(addr), length);
}

waitUntilAsyncReadFinished应确保在返回之前,任何从给定内存区域读取的异步操作已经完成。通常,该内存区域指代某些将要被覆盖的纹理数据。这里的最简单选项是与硬件加速混合单元同步,以确保没有更多的纹理混合操作挂起。

接下来,flushCachesForAsyncRead确保任何由CPU写入的更改都已刷新,以便随后的异步读取获取正确和最新的内存数据。如果有必要在特定硬件上执行此操作,必须调用使数据缓存无效的调用。

这涵盖了实现硬件加速混合所需的所有组件,以确保 Qt Quick Ultralite UI在移植到平台上运行得既平稳又高效。

另请参阅帧缓冲区要求部分帧缓冲区

在特定的Qt许可证下提供。
了解更多信息。