如何将SDL2纹理用作更新画布的一种方式?

5

我正在尝试为嵌入式项目创建一个SDL2动态绘图器。目前,代码在x86和ARM两个架构上均能执行。在x86上,我得到了一个流畅运行的绘图器,然而,在ARM上它运行非常缓慢,仅有x86帧数的一小部分。我很确定这是因为我重新渲染了表面上的每个像素,这是嵌入式设备上过度负荷的原因。

我已经尝试将新内容渲染到纹理中,将其复制到渲染器中,然后再进行渲染,但这根本没有效果。

由于双缓冲,我必须清除每一帧。否则,我会“丢失”更改。但我也需要渲染旧数据点,并且只有在绘图器再次到达它们时才会覆盖它们。

在SDL2中是否有一种方法可以将这些数据点保存到某种画布中,并仅添加(重绘)新添加的数据点?

这是我的源代码:

Plotter.cpp

#include <SDL2/SDL.h>
#include <stdio.h>
#include "PlotterHelper.h"

/*Implementation*/
int main(int arhc, char * argv[])
{
    //Top and bottom viewport
    SDL_Rect topViewport;
    SDL_Rect bottomViewport;
    topViewport = CreateViewPort(0,0, SCREEN_WIDTH, (SCREEN_HEIGHT/2));
    bottomViewport = CreateViewPort(0,(SCREEN_HEIGHT/2), SCREEN_WIDTH, SCREEN_HEIGHT/2);

    float timeFrame = 4.0;
    int updateWidth = round(SCREEN_WIDTH/(60.0 * timeFrame));


    uint8_t backgroundColor = 0xff;

    int delayTime = 0;
    int sinusScale = 0;
    int rectWidth = RECT_WIDTH;
    bool rectEnd = false;

    SDL_Point points[SCREEN_WIDTH] = {0,0};
    int pointPosition = 0;

    if (!init())
    {
        printf("Init failed!\n");
    }
    else
    {

        SDL_ShowCursor(SDL_DISABLE);
        //Main lopp flag
        bool quit = false;
        //Event handler
        SDL_Event event;

        //While application is running
        while(!quit)
        {
            //Handle events on queue
            while (SDL_PollEvent( &event) != 0)
            {
                //User requests quit
                if(event.type == SDL_QUIT)
                {
                    quit = true;
                }
                else if(event.type == SDL_KEYDOWN)
                {
                    switch(event.key.keysym.sym)
                    {
                    case SDLK_w:
                        delayTime += 50;
                        if(delayTime > 5000)
                            delayTime = 5000;
                        break;
                    case SDLK_s:
                        delayTime -= 50;
                        if(delayTime < 0)
                            delayTime = 0;
                        break;
                    case SDLK_d:
                        timeFrame -= 1;
                        if(timeFrame < 1)
                            timeFrame = 1.0;
                        updateWidth = round(SCREEN_WIDTH/(60.0 * timeFrame));
                        printf("timeFrame = %lf\n", timeFrame);
                        break;
                    case SDLK_a:
                        timeFrame += 1;
                        if(timeFrame > 44)
                            timeFrame = 44.0;
                        updateWidth = round(SCREEN_WIDTH/(60.0 * timeFrame));
                        printf("timeFrame = %lf\n", timeFrame);
                        break;
                    case SDLK_r:
                        if(backgroundColor == 0x3f)
                            break;
                        else
                        {
                            ++backgroundColor;
                            break;
                        }
                    case SDLK_f:
                        if(backgroundColor == 0x00)
                                break;
                        else
                        {
                            --backgroundColor;
                            break;
                        }
                    }
                }
            }

            //Reset Plotter when the end of the window was reached
            if(pointPosition > SCREEN_WIDTH-1)
            {
                pointPosition = 0;
                sinusScale = (rand() % 100 + 1) - 50;
                rectWidth = RECT_WIDTH;
                rectEnd = false;
            }

            //Handler eraser when he reaches end of window
            if(((SCREEN_WIDTH-1) - pointPosition) < RECT_WIDTH)
            {
                rectWidth = (SCREEN_WIDTH -1) - pointPosition;
                rectEnd = true;

            }


            //Clear screen
            SDL_SetRenderDrawColor(gRenderer, backgroundColor, backgroundColor, backgroundColor, 0xff);
            SDL_RenderClear(gRenderer);


            //Draw top viewport
            SDL_RenderSetViewport( gRenderer, &topViewport );

            SDL_SetRenderDrawColor(gRenderer, 0x00, 0x66, 0x00, 0xff);
            for(int iterator = 0; iterator <= SCREEN_WIDTH -1; ++iterator)
            {
                SDL_RenderDrawLine(
                        gRenderer,
                        points[iterator].x,
                        0,
                        points[iterator].x,
                        SCREEN_HEIGHT/2);
            }

            PlotEraser(rectEnd, backgroundColor, rectWidth,points[pointPosition].x );

            //raw bottom viewport
            SDL_RenderSetViewport( gRenderer, &bottomViewport );

            SDL_SetRenderDrawColor(gRenderer, 0x00, 0x66, 0x00, 0xff);
            for(int iterator = 0; iterator <= SCREEN_WIDTH -1; ++iterator)
                {
                    SDL_RenderDrawLine(
                            gRenderer,
                            points[iterator].x,
                            SCREEN_HEIGHT/2,
                            points[iterator].x,
                            points[iterator].y);
                }

            PlotEraser(rectEnd, backgroundColor, rectWidth,points[pointPosition].x );


            for(int iterator = pointPosition; iterator <= pointPosition + updateWidth; ++iterator)
            {
                points[iterator].x = iterator;
                points[iterator].y = round(((SCREEN_HEIGHT/4 )* sin(iterator/(100.0+sinusScale))) + SCREEN_HEIGHT/4);
            }
            pointPosition += updateWidth;

            //Update Screen
            SDL_RenderPresent(gRenderer);

            SDL_Delay(delayTime);
        }

    }

    //Free resources and close SDL
    close();

    return 0;
}

/*End of File*/

PlotterHelper.cpp

/*Includes*/

#include "PlotterHelper.h"

SDL_Window  * gWindow = NULL;
SDL_Renderer * gRenderer = NULL;
SDL_Renderer * intermediate = NULL;

/*Implementation*/

/*********************************************************************
 *********************************************************************
 *********************************************************************
*/
bool init()
{
    //Init  flag
    bool success = true;

    //Init SDL
    if(SDL_Init(SDL_INIT_VIDEO) < 0)
    {
        printf( "SDL could not initialize! SDL_Error: %s\n", SDL_GetError() );
        success = false;
    }
    else
    {
        //Set texture filtering to linear
        if( !SDL_SetHint( SDL_HINT_RENDER_SCALE_QUALITY, "1" ) )
        {
            printf( "Warning: Linear texture filtering not enabled!" );
        }

        //Set VSYNC
        if( !SDL_SetHint( SDL_HINT_RENDER_VSYNC, "1" ) )
        {
            printf( "Warning: VSYNC not enabled!" );
        }

        //Create window
        gWindow = SDL_CreateWindow(
                "SDL Plotter",
                SDL_WINDOWPOS_UNDEFINED,
                SDL_WINDOWPOS_UNDEFINED,
                SCREEN_WIDTH,
                SCREEN_HEIGHT,
                SDL_WINDOW_SHOWN );
        if (NULL == gWindow)
        {
            printf( "Window could not be created! SDL_Error: %s\n", SDL_GetError() );
            success = false;
        }
        else
        {
            //Create renderer for window
            gRenderer = SDL_CreateRenderer(gWindow, -1, SDL_RENDERER_ACCELERATED );
            if(NULL == gRenderer)
            {
                printf("Renderer could not be created! SDLError: %s\n", SDL_GetError());
                success = false;
            }
            else
            {
                //Initialise renderer colour
                SDL_SetRenderDrawColor(gRenderer, 0xff, 0xff, 0xff, 0xff);
            }

        }
    }
    return success;
}

/*********************************************************************
 *********************************************************************
 *********************************************************************
*/
void close()
{
    //Destroy window
    SDL_DestroyRenderer(gRenderer);
    SDL_DestroyWindow(gWindow);
    gRenderer = NULL;
    gWindow = NULL;

    //Quit SDL subsystems
    SDL_Quit();
}

/*********************************************************************
 *********************************************************************
 *********************************************************************
*/

SDL_Rect CreateViewPort(int x, int y, int w, int h)
{
    SDL_Rect Viewport;
    Viewport.x = x;
    Viewport.y = y;
    Viewport.w = w;
    Viewport.h = h;

    return Viewport;
}
/*********************************************************************
 *********************************************************************
 *********************************************************************
*/
void PlotEraser(bool rectEnd, uint8_t backgroundColor, int rectWidth, int x)
{
    SDL_Rect fillRectBot = {x, 0, rectWidth, SCREEN_HEIGHT/2};

    SDL_SetRenderDrawColor(gRenderer, backgroundColor, backgroundColor, backgroundColor, 0xff);
    SDL_RenderFillRect(gRenderer, &fillRectBot);

    if(rectEnd)
    {
        int startRecWidth = RECT_WIDTH - rectWidth;

        SDL_Rect startRec = {0, 0, startRecWidth, SCREEN_HEIGHT/2};
        SDL_RenderFillRect(gRenderer, &startRec);
    }
}

/*End of File*/

PlotterHelper.h



#ifndef PLOTTER_HELPER_H_
#define PLOTTER_HELPER_H_

#include <SDL2/SDL.h>
#include <stdio.h>
#include <cmath>
#include <string>
/*Globals*/

//Screen  constants
const int SCREEN_WIDTH = 1024;
const int SCREEN_HEIGHT = 768;
const int RECT_WIDTH = 10;

//The window we'll be rendering to
extern SDL_Window * gWindow;
//The window renderer
extern SDL_Renderer * gRenderer;
extern SDL_Renderer * intermediate;


/*Prototypes*/
/*********************************************************************
 *********************************************************************
 *********************************************************************
*/
//Starts up SDL and creates window
bool init();
/*********************************************************************
 *********************************************************************
 *********************************************************************
*/
//Free media and shut down SDL
void close();
/*********************************************************************
 *********************************************************************
 *********************************************************************
*/
//CreateVieport
SDL_Rect CreateViewPort(int x, int y, int w, int h);
/*********************************************************************
 *********************************************************************
 *********************************************************************
*/
//Plot Eraser
void PlotEraser(bool rectEnd, uint8_t backgroundColor, int rectWidth, int x);
#endif /* PLOTTER_HELPER_H_ */
/*End of File*/

1个回答

4

您可能有几种选择来帮助解决此问题:

使用纹理渲染目标

您可以使用“渲染目标纹理”来实现这一点。这是一个SDL_Texture,您可以使用SDL_TEXTUREACCESS_TARGET标志创建(请参阅SDL_CreateTexture)。

通过调用SDL_SetRenderTarget并在渲染点之前传递此纹理来向此纹理绘制新的点。

然后,您的主循环需要调用SDL_SetRenderTarget并传递nullptr以将窗口恢复为渲染目标。然后,在每个帧中,您将渲染您的纹理到窗口中。

SDL文档中有一个演示如何使用渲染目标纹理的小样例。

升级到SDL 2.0.9并使用渲染批处理

最新版本的SDL 2支持渲染批处理,如果您设置了SDL_HINT_RENDER_BATCHING(SDL_SetHint(SDL_HINT_RENDER_BATCHING, "1");),则可以使用。您应该使用SDL_CreateWindowAndRenderer()来确保启用了批处理。

如果您的平台不支持硬件加速,则可能无法实现任何效果。您可以与渲染目标一起使用。在这里阅读关于SDL渲染批处理的更多信息。

将矩形/点合并成批次

这与渲染批处理分开,应该适用于SDL 2的任何版本。

如果可以,请将所有矩形/点收集到一个数组中,然后可以使用SDL_RenderDrawPointsSDL_RenderDrawRects一次性绘制所有对象,这应该能提高性能,即使没有硬件加速。这可以与渲染目标一起使用。

抛弃SDL_Renderer / SDL_Texture

如果您的设备缺乏硬件加速,则放弃SDL_Renderer并改为使用SDL_GetWindowSurface获取窗口的SDL_Surface,并使用SDL_BlitSurface(或通过surface->pixels手动设置像素) 直接绘制到其中,然后使用SDL_UpdateWindowSurface更新窗口。有关如何仅更新您正在更改的矩形的信息,请参见SDL_UpdateWindowSurfaceRects

您需要熟悉SDL_Surface ,因为您需要检查窗口表面的像素格式,以便正确更新它,如果选择直接操作像素。


1
@J.Panek 如果您尝试过渲染纹理,为什么在问题中没有提到呢?我会更新我的答案,提供一种替代方案,可以直接将软件渲染到窗口,但我不知道它是否真的有助于性能。 - Brad Allred
@J.Panek 你也可以尝试禁用垂直同步。这样有帮助吗? - Brad Allred
@J.Panek更新了答案,并提出了其他几个想法。如果您可以批量处理绘图,那应该会有明显的帮助。 - Brad Allred
在我的问题中,我写到了我尝试使用渲染纹理的情况:“我尝试将新内容渲染到纹理上,将其复制到渲染器并进行渲染,但这根本不起作用”,也许我表达得不太好。你的回答帮了我很多,非常感谢,现在只剩下撕裂问题了。 - J.Panek
@J.Panek 您好,欢迎您。很抱歉造成了困惑。您可能需要研究一些批处理的概念来进一步提高性能。如果出现撕裂的情况,那么您可能需要使用垂直同步,但这会对性能产生影响。 - Brad Allred
显示剩余2条评论

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接