SDL按钮文本经约1399次渲染迭代后消失问题排查
SDL按钮文本渲染约1399次后消失的问题
问题现象
开发的SDL按钮文本会在约1399次渲染迭代后消失,全程调用同一个类函数,排除函数本身逻辑问题。对比消失前后的状态发现:类成员SDL_Surface* buttonTextSurface在文本消失时指向NULL。
消失前状态
- 按钮文本正常显示,
buttonTextSurface指向有效内存地址 - 相关成员变量状态无异常
消失后状态
buttonTextSurface变为NULL- 按钮文本无法渲染显示
相关代码
#include"include/SDL.h" #include<iostream> #include<Windows.h> #include"include/SDL_ttf.h" #include<conio.h> SDL_Window* window = SDL_CreateWindow("Button Example", SDL_WINDOWPOS_UNDEFINED, SDL_WINDOWPOS_UNDEFINED, 640, 480, SDL_WINDOW_SHOWN); SDL_Renderer* renderer = SDL_CreateRenderer(window, -1, SDL_RENDERER_ACCELERATED); TTF_Font* font; using namespace std; class Button { SDL_Rect Letter_Button; char text_for_button; bool Button_pushed; bool Button_hovered; COORD Position; int Shadow_offset; int Button_Size; SDL_Rect buttonShadow; SDL_Surface* buttonTextSurface = TTF_RenderText_Solid(font, &text_for_button, { 0, 0, 255 }); //text Color SDL_Texture* buttonTextTexture = SDL_CreateTextureFromSurface(renderer, buttonTextSurface); public: Button() : Button_pushed(0), Button_hovered(0), Position({ 0,0 }), Shadow_offset(5), Button_Size(0) {} Button(short x_axis, short y_axis) : Button_pushed(0), Button_hovered(0), Position({ x_axis, y_axis }) {} void Set_Button(char Alphabet, COORD Position, int Button_Size) { cout << "enterd\n"; text_for_button = Alphabet; this->Position = Position; this->Button_Size = Button_Size; Letter_Button = { Position.X, Position.Y, Button_Size, Button_Size }; buttonShadow = { Position.X + Shadow_offset, Position.Y + Shadow_offset, Button_Size, Button_Size }; buttonTextSurface = TTF_RenderText_Solid(font, &text_for_button, { 0, 0, 255 }); //text Color } void Diplay_Shadow() { SDL_SetRenderDrawColor(renderer, 75, 75, 75, 50); SDL_RenderFillRect(renderer, &buttonShadow); } void Display_Button() { Diplay_Shadow(); buttonTextSurface = TTF_RenderText_Solid(font, &text_for_button, { 0, 0, 255 }); //text Color buttonTextTexture = SDL_CreateTextureFromSurface(renderer, buttonTextSurface); SDL_SetRenderDrawColor(renderer, 255, 65, 65, 50); SDL_RenderFillRect(renderer, &Letter_Button); SDL_RenderCopy(renderer, buttonTextTexture, NULL, &Letter_Button); //if(Button_pushed) //change color //if(Button_hover) //change color } }; Button Normal_Letters[26]; int main(int argc, char* argv[]) { SDL_Init(SDL_INIT_VIDEO); TTF_Init(); font = TTF_OpenFont("arial.ttf", 1000);//16 //max : 7332 // Load a font to use for the button text // Create a surface for the button text SDL_Surface* buttonTextSurface = TTF_RenderText_Solid(font, "A", { 0, 0, 255 }); //text Color int x; // Create a texture from the surface SDL_Texture* buttonTextTexture = SDL_CreateTextureFromSurface(renderer, buttonTextSurface); // Create a rectangle for the button position and dimensions SDL_Rect buttonRect{ 10, 10, 100, 100 }; int Shadow_offset = 5; SDL_Rect button_Shadow{ 10 + Shadow_offset, 10 + Shadow_offset, 100, 100 }; bool buttonHovered = false, Button_Pushed = 0; SDL_SetRenderDrawBlendMode(renderer, SDL_BLENDMODE_BLEND); //cout << test << "\n"; while (true) { SDL_Event event; if (_kbhit()) { x = _getch() - '0'; cout << "df\n"; } while (SDL_PollEvent(&event)) { if (event.type == SDL_QUIT || event.key.keysym.sym == SDLK_ESCAPE) { // Close the window and quit the game SDL_DestroyTexture(buttonTextTexture); SDL_FreeSurface(buttonTextSurface); TTF_CloseFont(font); SDL_DestroyRenderer(renderer); SDL_DestroyWindow(window); SDL_Quit(); exit(0); return 0; } if (event.type == SDL_MOUSEMOTION) { // Check if the mouse is hovering over the button rectangle int x, y; SDL_GetMouseState(&x, &y); buttonHovered = (x >= buttonRect.x && x < buttonRect.x + buttonRect.w && y >= buttonRect.y && y < buttonRect.y + buttonRect.h); } if (event.type == SDL_MOUSEBUTTONUP) { // Check if the mouse click was within the button rectangle int x, y; SDL_GetMouseState(&x, &y); if (x >= buttonRect.x && x < buttonRect.x + buttonRect.w && y >= buttonRect.y && y < buttonRect.y + buttonRect.h) { // The mouse click was within the button, so do something std::cout << "Button clicked!\n"; Button_Pushed = !Button_Pushed; Shadow_offset = Shadow_offset * -1; if (Button_Pushed) { buttonRect = { 15, 15, 95, 95 }; button_Shadow.x = 10; button_Shadow.y = 10; } else { buttonRect = { 10, 10, 100, 100 }; button_Shadow.x = 10 + Shadow_offset; button_Shadow.y = 10 + Shadow_offset; } } } } // Render the button SDL_SetRenderDrawColor(renderer, 255, 255, 255, 255); SDL_RenderClear(renderer); //for (size_t i = 0; i < 26; i++) // Normal_Letters[i].text_for_button = char(i + 65); Normal_Letters[1].Set_Button('B', { 200, 200 }, 100); Normal_Letters[1].Display_Button(); if (_kbhit()) x = _getch() - '0'; if (Button_Pushed || 1)//if not Pressed then show shadow { SDL_SetRenderDrawColor(renderer, 75, 75, 75, 200); SDL_RenderFillRect(renderer, &button_Shadow); } if (buttonHovered) { // Use cyan and yellow for the button color when it is hovered SDL_SetRenderDrawColor(renderer, 0, 255, 255, 255); SDL_RenderFillRect(renderer, &buttonRect); //SDL_SetRenderDrawColor(renderer, 255, 255, 0, 255); //SDL_RenderDrawRect(renderer, &buttonRect); } else { // Use white for the button color when it is not hovered SDL_SetRenderDrawColor(renderer, 255, 255, 255, 255); } SDL_RenderFillRect(renderer, &buttonRect); SDL_RenderCopy(renderer, buttonTextTexture, NULL, &buttonRect); SDL_RenderPresent(renderer); } }
问题根源
- 内存泄漏耗尽资源:
Display_Button()和Set_Button()中每次调用都会创建新的buttonTextSurface和buttonTextTexture,但从未释放旧资源。SDL的Surface、Texture等系统资源有限,反复创建不释放会导致内存耗尽,最终TTF_RenderText_Solid()和SDL_CreateTextureFromSurface()无法分配内存,返回NULL。 - 初始化顺序错误:全局
Button数组Normal_Letters[26]在main()前初始化,此时font还未被TTF_OpenFont()初始化,构造函数中创建buttonTextSurface的操作会使用未初始化的font,引发未定义行为。 - 主循环重复初始化按钮:主循环每次迭代都调用
Normal_Letters[1].Set_Button(),属于无意义操作,还会加剧内存泄漏。
修复方案
1. 及时释放旧资源
在重新创建Surface和Texture前,释放旧的资源:
void Display_Button() { Diplay_Shadow(); // 释放旧资源 if (buttonTextSurface != nullptr) { SDL_FreeSurface(buttonTextSurface); buttonTextSurface = nullptr; } if (buttonTextTexture != nullptr) { SDL_DestroyTexture(buttonTextTexture); buttonTextTexture = nullptr; } buttonTextSurface = TTF_RenderText_Solid(font, &text_for_button, { 0, 0, 255 }); buttonTextTexture = SDL_CreateTextureFromSurface(renderer, buttonTextSurface); SDL_SetRenderDrawColor(renderer, 255, 65, 65, 50); SDL_RenderFillRect(renderer, &Letter_Button); SDL_RenderCopy(renderer, buttonTextTexture, NULL, &Letter_Button); }
Set_Button()中同样需要释放旧Surface:
void Set_Button(char Alphabet, COORD Position, int Button_Size) { cout << "enterd\n"; text_for_button = Alphabet; this->Position = Position; this->Button_Size = Button_Size; Letter_Button = { Position.X, Position.Y, Button_Size, Button_Size }; buttonShadow = { Position.X + Shadow_offset, Position.Y + Shadow_offset, Button_Size, Button_Size }; // 释放旧Surface if (buttonTextSurface != nullptr) { SDL_FreeSurface(buttonTextSurface); buttonTextSurface = nullptr; } buttonTextSurface = TTF_RenderText_Solid(font, &text_for_button, { 0, 0, 255 }); }
2. 修正初始化顺序
将类成员Surface和Texture初始化为NULL,避免构造函数中使用未初始化的font:
class Button { // ... 其他成员变量 ... SDL_Surface* buttonTextSurface = nullptr; SDL_Texture* buttonTextTexture = nullptr; public: Button() : Button_pushed(0), Button_hovered(0), Position({ 0,0 }), Shadow_offset(5), Button_Size(0), buttonTextSurface(nullptr), buttonTextTexture(nullptr) {} // ... 其他构造函数 ... };
3. 移除主循环中的重复初始化
将按钮初始化移到主循环外,只执行一次:
int main(int argc, char* argv[]) { // ... SDL、TTF、font初始化 ... Normal_Letters[1].Set_Button('B', { 200, 200 }, 100); while (true) { // ... 事件处理 ... SDL_RenderClear(renderer); Normal_Letters[1].Display_Button(); // ... 其他渲染操作 ... } }
4. 添加错误检查
调用SDL/TTF函数时检查返回值,便于调试:
buttonTextSurface = TTF_RenderText_Solid(font, &text_for_button, { 0, 0, 255 }); if (buttonTextSurface == nullptr) { cout << "TTF渲染失败: " << TTF_GetError() << endl; } buttonTextTexture = SDL_CreateTextureFromSurface(renderer, buttonTextSurface); if (buttonTextTexture == nullptr) { cout << "创建纹理失败: " << SDL_GetError() << endl; }
内容的提问来源于stack exchange,提问作者Wakeel Furqan Ahmed
相关产品推荐
相关产品推荐

