SDL renderer: How to display the same texture with different scale modes?
Version/Branch of Dear ImGui:
Version 1.90.6
Back-ends:
imgui_impl_sdl2.cpp + imgui_impl_sdlrenderer2.cpp
Compiler, OS:
Windows 10 + MSVC 2022
Full config/build information:
No response
Details:
I want to display the same texture in different scale modes. The screenshot shows the effect of test_fn below. Can I achieve the same effect without creating multiple textures?
Screenshots/Video:
Minimal, Complete and Verifiable Example code:
static void test_fn(SDL_Renderer* renderer) {
static SDL_Texture *nearest = nullptr, *linear = nullptr;
const int w = 33, h = 25;
const int zoom = 4;
if (!nearest) {
nearest = SDL_CreateTexture(renderer, SDL_PIXELFORMAT_ABGR8888, SDL_TEXTUREACCESS_STATIC, w, h);
SDL_SetTextureScaleMode(nearest, SDL_ScaleModeNearest);
linear = SDL_CreateTexture(renderer, SDL_PIXELFORMAT_ABGR8888, SDL_TEXTUREACCESS_STATIC, w, h);
SDL_SetTextureScaleMode(linear, SDL_ScaleModeLinear);
Uint32 pixels[h][w];
bool b = false;
for (auto& line : pixels) {
for (Uint32& p : line) {
p = b ? 0 : -1;
b = !b;
}
}
SDL_UpdateTexture(nearest, nullptr, pixels, w * sizeof(Uint32));
SDL_UpdateTexture(linear, nullptr, pixels, w * sizeof(Uint32));
}
if (ImGui::Begin("Test", nullptr, ImGuiWindowFlags_AlwaysAutoResize | ImGuiWindowFlags_NoCollapse)) {
ImGui::Image(nearest, ImVec2(w * zoom, h * zoom));
ImGui::SameLine();
ImGui::Image(linear, ImVec2(w * zoom, h * zoom));
}
ImGui::End();
}
Here is the background of this issue: I wanted to display a zoom window in nearest scale mode for a texture shown in linear mode. The problem was worked around by making a new texture with different scale mode, but I think the solution is wasteful.
Hello,
Assuming that SDL Render allows switching texture scale mode between rendering,
you should be able to use ImGui::GetWindowDrawList->AddCallback() and pass a callback which will call SDL_SetTextureScaleMode().
This is analoguous to this example here changing the sampler for DX11 rendering: https://github.com/ocornut/imgui/wiki/Image-Loading-and-Displaying-Examples#modifying-render-state
I've tried this:
void ImDrawCallback_ImplSDLRenderer_SetScaleModeNearest(const ImDrawList* parent_list, const ImDrawCmd* cmd)
{
SDL_Texture* texture = (SDL_Texture*)cmd->UserCallbackData;
SDL_SetTextureScaleMode(texture, SDL_ScaleModeNearest);
}
void ImDrawCallback_ImplSDLRenderer_SetScaleModeLinear(const ImDrawList* parent_list, const ImDrawCmd* cmd)
{
SDL_Texture* texture = (SDL_Texture*)cmd->UserCallbackData;
SDL_SetTextureScaleMode(texture, SDL_ScaleModeLinear);
}
ImGui::Image((ImTextureID)(intptr_t)my_texture, ImVec2((float)my_image_width * 4, (float)my_image_height * 4));
ImGui::GetWindowDrawList()->AddCallback(ImDrawCallback_ImplSDLRenderer_SetScaleModeNearest, (void*)my_texture);
ImGui::Image((ImTextureID)(intptr_t)my_texture, ImVec2((float)my_image_width * 4, (float)my_image_height * 4));
ImGui::GetWindowDrawList()->AddCallback(ImDrawCallback_ImplSDLRenderer_SetScaleModeLinear, (void*)my_texture);
But unfortunately SDL Renderer doesn't seem to allow that changes to happen: because all rendering is done at once and the texture scale mode at the time of calling SDL_RenderGeometryRaw() doesn't seem to be register.
This didn't work either, which is more puzzling:
void ImDrawCallback_ImplSDLRenderer_SetScaleModeNearest(const ImDrawList* parent_list, const ImDrawCmd* cmd)
{
ImGui_ImplSDLRenderer2_RenderState* render_state = (ImGui_ImplSDLRenderer2_RenderState*)ImGui::GetPlatformIO().Renderer_RenderState;
SDL_RenderFlush(render_state->Renderer);
SDL_Texture* texture = (SDL_Texture*)cmd->UserCallbackData;
SDL_SetTextureScaleMode(texture, SDL_ScaleModeNearest);
}
void ImDrawCallback_ImplSDLRenderer_SetScaleModeLinear(const ImDrawList* parent_list, const ImDrawCmd* cmd)
{
ImGui_ImplSDLRenderer2_RenderState* render_state = (ImGui_ImplSDLRenderer2_RenderState*)ImGui::GetPlatformIO().Renderer_RenderState;
SDL_RenderFlush(render_state->Renderer);
SDL_Texture* texture = (SDL_Texture*)cmd->UserCallbackData;
SDL_SetTextureScaleMode(texture, SDL_ScaleModeLinear);
}
I would expect this to work (even if it's perhaps not efficient).
Out of curiosity I tried with SDL3 and it worked (but that's with the SDL_FlushRenderer() call, which is not great).
void ImDrawCallback_ImplSDLRenderer3_SetScaleModeNearest(const ImDrawList* parent_list, const ImDrawCmd* cmd)
{
ImGui_ImplSDLRenderer3_RenderState* render_state = (ImGui_ImplSDLRenderer3_RenderState*)ImGui::GetPlatformIO().Renderer_RenderState;
SDL_FlushRenderer(render_state->Renderer);
SDL_Texture* texture = (SDL_Texture*)cmd->UserCallbackData;
SDL_SetTextureScaleMode(texture, SDL_SCALEMODE_NEAREST);
}
void ImDrawCallback_ImplSDLRenderer3_SetScaleModeLinear(const ImDrawList* parent_list, const ImDrawCmd* cmd)
{
ImGui_ImplSDLRenderer3_RenderState* render_state = (ImGui_ImplSDLRenderer3_RenderState*)ImGui::GetPlatformIO().Renderer_RenderState;
SDL_FlushRenderer(render_state->Renderer);
SDL_Texture* texture = (SDL_Texture*)cmd->UserCallbackData;
SDL_SetTextureScaleMode(texture, SDL_SCALEMODE_LINEAR);
}
I don't think SDL_Renderer is designed for that, but you could ask the team if they would like texture state to be latched during draw calls, it may be a possible improvement for SDL3.
I am going to have to close this as answered, unfortunately this is not supported by SDL, but feel free to ask on SDL3 repo if they want to make the change to make it work without flushing. I suspect SDL3 will nowadays focus their effort on SDL_GPU rather than SDL_Renderer, but if SDL_Renderer ends up using SDL_GPU then maybe this improvement will come automatically.
It's been interesting for me to dig because this topic aligns with recent work I've been doing toward making it easier to change backend render state. We might eventually settle on a mechanism with officially supported callbacks to do exactly what you are trying to do, since it's been a quite common request. It also highlighted an old request that has been to make it easier to make more data to callbacks. I think I may extend ImDrawList to allow for user storage.
It also highlighted an old request that has been to make it easier to make more data to callbacks. I think I may extend ImDrawList to allow for user storage.
FYI, I just did that with 98d52b7 (but it doesn't solve your main issue here)