我正在尝试恢复 20 多年前我为学生编写的一个旧的跨平台图形库。 Apple 停止使用 Carbon 后,我在 SDL2 之上重新实现了 Mac 端,SDL2 大约 10 年前就已经运行了。该库是围绕持久屏幕模型而不是持久对象模型构建的:绘制后的像素将保持绘制状态,直到被擦除。显然,该模型不适用于双缓冲。所以我围绕这个答案进行了重建,以获得单一缓冲。
单独使用时,下面的代码按预期工作,单缓冲,生成此图像:
将 VERBATIM 复制到我的库的较大主文件中,代替该文件的 main(),它以某种方式以双缓冲模式执行,具有明显的交替行被绘制到交替缓冲区中的模式。此外,有证据表明 SDL_RenderDrawLine 正在绘制某种未初始化的内存,然后被逐块传输到主显示纹理上。 (尽管为了简洁起见,我在此处包含了最少的标头,但即使我包含与库的完整主文件中使用的完全相同的标头(一个更大的集合),独立版本仍然可以按预期工作。)
显然我不能指望有人调试我的大型程序,但是有谁知道:SDL2 中决定它是否在单缓冲区或双缓冲区模式下运行的“开关”到底在哪里?或者我可能暴露了一个错误吗?似乎有一些非常微妙的东西正在做出决定。
MacOS 14.2.1; SDL2 版本 2.30.4
代码:
#include <SDL2/SDL.h>
#include <SDL2/SDL2_gfxPrimitives.h>
#include <unistd.h>
SDL_Window* gWindow = NULL; //The window we'll be rendering to
SDL_Renderer* gRenderer = NULL; // Renders to an offscreen "texture" (frequent)
static SDL_Texture *target;
SDL_Event event;
void DrawNow();
int points[][2] = {
{50, 50},
{200, 100},
{50, 300},
{400, 400},
{300, 70},
{70, 450}
};
void AutoDraw() { // "User" graphics
filledCircleColor(gRenderer, 100, 100, 40, 0xff00ff00);
SDL_SetRenderDrawColor(gRenderer, 0xff, 0xaa, 0x00, 0xFF );
for (int i = 1; i < 6; i++) {
SDL_RenderDrawLine( gRenderer,
points[i - 1][0], points[i - 1][1],
points[i][0], points[i][1]);
DrawNow();
sleep(1);
}
}
void DrawNow() {
while (SDL_PollEvent(&event)) {
printf("Event: %d\n", event.type);
}
fprintf(stderr, "step ");
SDL_SetRenderTarget(gRenderer, NULL);
SDL_RenderCopy(gRenderer, target, NULL, NULL);
SDL_RenderPresent(gRenderer);
SDL_SetRenderTarget(gRenderer, target);
}
int main() {
//Initialize SDL
if( SDL_Init( SDL_INIT_EVERYTHING ) < 0 ) {
printf( "SDL could not initialize! SDL Error: %s\n", SDL_GetError() );
return 1;
}
gWindow = SDL_CreateWindow("Drawbox", 10, 10, 500, 500, 0);
if( gWindow == NULL ) {
printf( "Window could not be created! SDL Error: %s\n", SDL_GetError() );
return -1;
}
//Create renderer for window
gRenderer = SDL_CreateRenderer(gWindow, -1, SDL_RENDERER_ACCELERATED | SDL_RENDERER_TARGETTEXTURE);
if( gRenderer == NULL )
{
printf( "Renderer could not be created! SDL Error: %s\n", SDL_GetError() );
return -1;
}
/* Create texture for display buffering */
target = SDL_CreateTexture(gRenderer, SDL_PIXELFORMAT_RGBA8888, SDL_TEXTUREACCESS_TARGET, 500, 500);
SDL_SetRenderTarget(gRenderer, target);
//Clear buffer
SDL_SetRenderDrawColor( gRenderer, 0, 0, 0, 0xff );
SDL_RenderClear( gRenderer );
// handle window creation events: A MUST!
SDL_Event event;
while (SDL_PollEvent(&event)) {
printf("Event: %d\n", event.type);
}
circleColor(gRenderer, 100, 100, 50, 0x00ff00ff);
DrawNow();
AutoDraw();
sleep(15);
return 0;
}
SDL2 中决定运行在单缓冲区还是双缓冲区模式的“开关”到底在哪里?
没有开关,总是假设它是双缓冲的(强调我的):
:SDL_RenderPresent()
SDL 的渲染函数在后台缓冲区上运行;也就是说,调用渲染函数(例如 SDL_RenderDrawLine())不会直接在屏幕上放置一条线,而是更新后台缓冲区。因此,您可以组合整个场景,并将组合后的后台缓冲区作为完整图片“呈现”到屏幕上。 因此,当使用 SDL 的渲染 API 时,我们会完成针对该帧的所有绘制,然后每帧调用此函数一次,将最终的绘制呈现给用户。
每次出现后,后台缓冲区都应被视为无效;不要假设帧之间会存在先前的内容。强烈建议您在开始每个新帧的绘制之前调用 SDL_RenderClear() 来初始化后台缓冲区,即使您计划覆盖每个像素。
因此,对于
DrawNow()
,您需要在
SDL_RenderClear()
“blit”之前使用SDL_RenderCopy()
来清除后台缓冲区中可能存在或不存在的任何垃圾:void DrawNow() {
while (SDL_PollEvent(&event)) {
printf("Event: %d\n", event.type);
}
fprintf(stderr, "step ");
SDL_SetRenderTarget(gRenderer, NULL);
// clear backbuffer
SDL_SetRenderDrawColor( gRenderer, 0, 0, 0, 0xff );
SDL_RenderClear( gRenderer )
SDL_RenderCopy(gRenderer, target, NULL, NULL);
SDL_RenderPresent(gRenderer);
SDL_SetRenderTarget(gRenderer, target);
}