OK, So after looking into it further, SDL_SetRenderClipRect looks like the function that you want. It allows you to specify a small rectangle in which drawing occurs. All draw calls landing outside the rectangle are ignored by the GPU, and it can reduce GPU processing a lot.
But SDL_Clear breaks SDL_SetRenderClipRect. ← According to this link, it fails to take the clipping rectangle into account when clearing the screen. It sounds like this has been an ongoing issue. @icculus I can confirm that it has carried over into SDL3 (on XFCE4 Ubuntu).
The reason that SDL_Clear is so strongly recommended to begin with has to do with MacOS/IOS GPUs running in a slower “Preserve Framebuffer” mode when you fail to call it. If there are no moving objects on the screen that need to be overdrawn, it is safe to skip SDL_Clear(), just be aware that doing so may be less optimal on some devices (Mainly Mac).
Since SDL_Clear breaks things, I wrote a small (142 lines) example in which SDL_WaitEventTimeout handles drawing a blinking cursor. It simply avoids SDL_Clear while in a blinking state. Using SDL_SetRenderClipRect is not required here. You’ll need to change the font “roboto.ttf” to some font path that you have on your computer:
#include <SDL3/SDL.h>
#include <SDL3_ttf/SDL_ttf.h>
#include <string>
SDL_Color darkGray = {50, 50, 60, 255};
SDL_Texture * updateTextbox(SDL_Renderer * renderer, SDL_FRect & box, TTF_Font * font, const std::string text)
{
SDL_Texture * dest = NULL;
if(font)
{
if(text.length() > 0)
{
SDL_Surface * temp = TTF_RenderText_Blended(font, text.c_str(), text.length(), darkGray);
if(temp)
{
dest = SDL_CreateTextureFromSurface(renderer, temp);
box.w = temp->w;
box.h = temp->h;
SDL_DestroySurface(temp);
}
else
{
SDL_Log("Failed to render text to a surface");
}
}
}
return dest;
}
int main()
{
SDL_Init(SDL_INIT_VIDEO);
TTF_Init();
SDL_Window * window = SDL_CreateWindow("Txt", 800, 800, SDL_WINDOW_RESIZABLE);
SDL_Renderer * renderer = SDL_CreateRenderer(window, 0);
SDL_SetRenderVSync(renderer, 1);
TTF_Font * font = TTF_OpenFont("roboto.ttf", 50);
if(!font)
{
SDL_Log("Failed to load font");
}
SDL_FPoint cursorPos = {30, 30};
SDL_FRect textBox = {30, 30, 740, 50};
std::string text = "";
SDL_Texture * textOutput = NULL;
textOutput = updateTextbox(renderer, textBox, font, "Type Something");
SDL_StartTextInput(window);
bool blinkOn = true;
bool run = true;
int cursor = 0;
while(run)
{
while(SDL_WaitEventTimeout(NULL, 500) == 0)
{
SDL_Rect cursorView = {(int)cursorPos.x, (int)cursorPos.y, 3, (int) textBox.h + 1};
SDL_SetRenderClipRect(renderer, &cursorView);
// no user input, blink text cursor
blinkOn = !blinkOn;
if(blinkOn)
{
SDL_SetRenderDrawColor(renderer, 215, 225, 255, 255);
SDL_RenderLine(renderer, cursorPos.x, cursorPos.y, cursorPos.x, cursorPos.y + textBox.h);
}
else
{
SDL_SetRenderDrawColor(renderer, 10, 10, 10, 255);
SDL_RenderLine(renderer, cursorPos.x, cursorPos.y, cursorPos.x, cursorPos.y + textBox.h);
}
SDL_RenderPresent(renderer);
SDL_SetRenderClipRect(renderer, NULL);
}
SDL_Event ev;
while(SDL_PollEvent(&ev))
{
switch(ev.type)
{
case SDL_EVENT_TEXT_INPUT:
text.insert(cursor, ev.text.text);
cursor ++;
if(textOutput)
{
SDL_DestroyTexture(textOutput);
}
textOutput = updateTextbox(renderer, textBox, font, text);
break;
case SDL_EVENT_KEY_DOWN:
switch(ev.key.key)
{
case SDLK_BACKSPACE:
if(cursor > 0)
{
text.erase(cursor - 1, 1);
cursor --;
if(textOutput)
{
SDL_DestroyTexture(textOutput);
}
textOutput = updateTextbox(renderer, textBox, font, text);
}
break;
case SDLK_LEFT:
cursor --;
if(cursor < 0)
{
cursor = 0;
}
break;
case SDLK_RIGHT:
cursor ++;
if(cursor > text.length())
{
cursor = text.length();
}
break;
case SDLK_RETURN:
SDL_Log("User has input this text: %s", text.c_str());
break;
case SDLK_ESCAPE:
run = false;
break;
}
break;
case SDL_EVENT_QUIT:
run = false;
break;
}
}
int width = 0;
if(cursor > 0)
{
TTF_GetStringSize(font, text.substr(0, cursor).c_str(), cursor, &width, NULL);
}
cursorPos.x = textBox.x + width;
SDL_SetRenderDrawColor(renderer, 255, 245, 255, 255);
SDL_RenderClear(renderer);
SDL_SetRenderDrawColor(renderer, 5, 25, 55, 255);
// box outline:
// SDL_RenderRect(renderer, &textBox);
SDL_RenderLine(renderer, cursorPos.x, cursorPos.y, cursorPos.x, cursorPos.y + textBox.h);
SDL_RenderTexture(renderer, textOutput, NULL, &textBox);
SDL_RenderPresent(renderer);
}
TTF_CloseFont(font);
TTF_Quit();
SDL_Quit();
}
For coders looking for help with textbox: This is not how text input code should be set up, this is rough test code meant to have reduced line count and minimal functionality. Feel free to grab the parts that make sense, but please use structs/classes to build a much more useful text I/O API.