Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Changing texture sampler / Passing a bigger ImVec2 to AddImage and Image leads to pixel line #7230

Closed
Infiziert90 opened this issue Jan 16, 2024 · 7 comments

Comments

@Infiziert90
Copy link

Infiziert90 commented Jan 16, 2024

Version/Branch of Dear ImGui:

1.90.2 and before

Back-ends:

Win32 DirectX11

Compiler, OS:

MSVC, Windows 10

Full config/build information:

No response

Details:

Was trying around with AddImage in ImGui.NET and noticed that giving the AddImage/Image functions p_max that exceed the image width/height leads to a 1 pixel line being added on top of it.

This doesn't happen with the exact size or smaller size.

So i tried the same in the DirectX11 example provided for C ImGui and it produces the same result.

Looking through the web for DirectX11 issues with this specific problem, i couldn't find anything, so i wondered if this is ImGui doing something wrong

Screenshots/Video:

The result in the ImGui preview window, very noticeable line above it
ImGuiPreview

And here the used texture for this test
0

Minimal, Complete and Verifiable Example code:

Code from https://github.com/ocornut/imgui/wiki/Image-Loading-and-Displaying-Examples#example-for-directx11-users

        int my_image_width = 0;
        int my_image_height = 0;
        ID3D11ShaderResourceView* my_texture = NULL;
        bool ret = LoadTextureFromFile("0.png", &my_texture, &my_image_width, &my_image_height);
        IM_ASSERT(ret);

        ImGui::PushStyleColor(2, ImVec4(1, 1, 1, 1));
        ImGui::Begin("DirectX11 Texture Test");
        ImGui::Image((void*)my_texture, ImVec2(my_image_width, my_image_height));
        ImGui::SameLine();
        ImGui::Image((void*)my_texture, ImVec2(my_image_width * 2, my_image_height * 2));
        ImGui::End();
        ImGui::PopStyleColor();
@GamingMinds-DanielC
Copy link
Contributor

Looks like texture wrapping combined with bilinear filtering to me. Because of the magnification, the top pixel row filters above the top row texel center and therefore interpolates to the bottom row texels. If you look closely or with other textures, you will see that this happens on all sides, not just the top. It is just more noticeable there because of the image and your background.

You could adjust the sampler directly in your backend to use clamping instead of wrapping. Or you could substitute y clamping sampler in a draw list callback. before submitting the image, then restore the default one afterwards.

@ocornut
Copy link
Owner

ocornut commented Jan 16, 2024

As Daniel said. Basically any form of GPU-based textured rendering would give you similar result, and you may need to configure the texture sampler.

For some reasons all our backends are setup by default with wrapping samplers. In imgui_impl_dx11.cpp code we use:

    // Create texture sampler
    // (Bilinear sampling is required by default. Set 'io.Fonts->Flags |= ImFontAtlasFlags_NoBakedLines' or 'style.AntiAliasedLinesUseTex = false' to allow point/nearest sampling)
    {
        D3D11_SAMPLER_DESC desc;
        ZeroMemory(&desc, sizeof(desc));
        desc.Filter = D3D11_FILTER_MIN_MAG_MIP_LINEAR;
        desc.AddressU = D3D11_TEXTURE_ADDRESS_WRAP;
        desc.AddressV = D3D11_TEXTURE_ADDRESS_WRAP;
        desc.AddressW = D3D11_TEXTURE_ADDRESS_WRAP;
        desc.MipLODBias = 0.f;
        desc.ComparisonFunc = D3D11_COMPARISON_ALWAYS;
        desc.MinLOD = 0.f;
        desc.MaxLOD = 0.f;
        bd->pd3dDevice->CreateSamplerState(&desc, &bd->pFontSampler);
    }

You would need to create your own sampler, using D3D11_TEXTURE_ADDRESS_CLAMP value, then use ImDrawList::AddCallback() to register a callback calling bd->pd3dDevice->PSSetSamplers(0, 1, YOUR_SAMPLER).

If I have to be honest I am surprised this hasn't been asked before, as it seems like the kind of problem many would encounter, and we could argue that it may be useful if our backends had a system in place to standardize this, the same way we have a pseudo-callback value ImDrawCallback_ResetRenderState to standardize resetting state.

@ocornut
Copy link
Owner

ocornut commented Jan 16, 2024

By the way, it this is very a bad idea to use hard-coded constants instead of enum value:

 ImGui::PushStyleColor(2, ImVec4(1, 1, 1, 1));

Should be

 ImGui::PushStyleColor(ImGuiCol_WindowBg, ImVec4(1, 1, 1, 1));

The library doesn't provide ABI backward/forward compatibility meaning we are technically free to change underlying value of enums/flags, and we occasionally do.

@Infiziert90
Copy link
Author

By the way, it this is very a bad idea to use hard-coded constants instead of enum value:

 ImGui::PushStyleColor(2, ImVec4(1, 1, 1, 1));

Should be

 ImGui::PushStyleColor(ImGuiCol_WindowBg, ImVec4(1, 1, 1, 1));

The library doesn't provide ABI backward/forward compatibility meaning we are technically free to change underlying value of enums/flags, and we occasionally do.

Yeah, is what I normally do, this time Visual Studio for C just didn't want to auto complete so I took the int value from ImGui.NET and tried it

@Infiziert90
Copy link
Author

Switching to a sampler with Clamp works as expected, thanks

@ocornut
Copy link
Owner

ocornut commented Oct 7, 2024

First:

  • I have changed all backends to use Clamp instead of Repeat/Wrap by default. 42206b3
  • Metal and SDLRenderer2 were already clamping. SDLRenderer3 seems to be aborting rendering when UV are outside 0..1 ranges.

Secondly:

I have verified that it was possible with the DX11 backend:

    {
        D3D11_SAMPLER_DESC desc;
        ZeroMemory(&desc, sizeof(desc));
        desc.Filter = D3D11_FILTER_MIN_MAG_MIP_POINT;
        desc.AddressU = D3D11_TEXTURE_ADDRESS_WRAP;
        desc.AddressV = D3D11_TEXTURE_ADDRESS_WRAP;
        desc.AddressW = D3D11_TEXTURE_ADDRESS_WRAP;
        desc.MipLODBias = 0.f;
        desc.ComparisonFunc = D3D11_COMPARISON_ALWAYS;
        desc.MinLOD = 0.f;
        desc.MaxLOD = 0.f;
        g_pd3dDevice->CreateSamplerState(&desc, &g_SamplerPoint);
    }
        ImGui::Image((void*)my_texture, ImVec2((float)my_image_width * 4, (float)my_image_height * 4));

        auto dx11_set_sampler = [](const ImDrawList* parent_list, const ImDrawCmd* cmd)
        {
            ImGui_ImplDX11_RenderState* state = (ImGui_ImplDX11_RenderState*)ImGui::GetPlatformIO().Renderer_RenderState;
            ID3D11SamplerState* sampler = cmd->UserCallbackData ? (ID3D11SamplerState*)cmd->UserCallbackData : state->SamplerDefault;
            state->DeviceContext->PSSetSamplers(0, 1, &sampler);
        };

        ImGui::GetWindowDrawList()->AddCallback(dx11_set_sampler, g_SamplerPoint);
        ImGui::Image((void*)my_texture, ImVec2((float)my_image_width * 4, (float)my_image_height * 4));
        ImGui::GetWindowDrawList()->AddCallback(dx11_set_sampler, NULL);

        ImGui::End();

I am not 100% sure it is possible OR particularly convenient with all backends.
We will need to work/amend this on a backend per backend basis.

@ocornut ocornut changed the title Passing a bigger ImVec2 to AddImage and Image leads to pixel line Changing texture sampler / Passing a bigger ImVec2 to AddImage and Image leads to pixel line Oct 8, 2024
@ocornut
Copy link
Owner

ocornut commented Oct 8, 2024

I have added a "Modifying Render State" section in the Texture/Image wiki page:
https://github.com/ocornut/imgui/wiki/Image-Loading-and-Displaying-Examples#modifying-render-state
I can imagine that maybe once we prove that all backends can do this, we can even consider adding those helper callbacks in the backends themselves...

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Projects
None yet
Development

No branches or pull requests

3 participants