Per-texture filter option for add_static_texture / add_dynamic_texture / add_raw_texture#2637
Open
ricky-groenewald wants to merge 2 commits intohoffstadt:masterfrom
Open
Conversation
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
name: Pull Request
about: Create a pull request to help us improve
title: Per-texture filter option for add_static_texture / add_dynamic_texture / add_raw_texture
assignees: @hoffstadt @v-ein
Description:
Closes #773
Adds a
filterkeyword argument toadd_static_texture,add_dynamic_texture, andadd_raw_texture, letting the caller choose between bilinear (default) and nearest-neighbor sampling. Addresses the long-standing request to avoid blurred interpolation on pixel-art, discrete heatmaps, and other textures where point sampling is the right call.Two new module constants are exposed:
mvTextureFilter_Linear(0, default) andmvTextureFilter_Nearest(1). The filter lives on the texture item, so every widget that references that texture inherits its mode. Affectsadd_image,add_image_button,draw_image,draw_image_quad, andadd_image_series.The filter is mutable after creation via
configure_item, and round-trips throughget_item_configuration:Internally, the constants are defined as a file-scoped enum in
src/mvUtilities.hso C++ call sites reference them by name.Cross-platform. Filter state lives in a different architectural layer on each backend, so the implementation differs:
OpenGL (Linux):
glTexParameteri(GL_TEXTURE_MIN/MAG_FILTER, ...)is set on the GL texture object. Mutation is handled by a render-threadApplyTextureFiltercall on the nextdraw()afterconfigure_itemmarks_filterDirty.D3D11 (Windows):
imgui_impl_dx11.cppuses a single globalpTexSamplerLinear. DPG creates a second sampler (pTexSamplerNearest) onmvGraphics_D3D11at init, and image-drawing widgets emit anImDrawList::AddCallbackpair around the draw: the first swapsPSSetSamplersto nearest, the second is the built-inImDrawCallback_ResetRenderStatesentinel which restores the backend's defaults. Since the callback reads_filterlive every frame, mutation requires no GPU-side rebuild.Metal (macOS):
imgui_impl_metal.mmbakes aconstexpr sampler linearSamplerinto its MSL fragment shader, so a CPU-sidesetFragmentSamplerStatecall has no effect without a shader change. Rather than patch the ImGui backend, DPG ships a small parallel Metal pipeline inmvGraphics_apple.mmwith a bindable sampler. The shader mirrors the ImGui one and consumes the same vertex buffer and uniforms layout; the callback binds the DPG pipeline + a nearestMTLSamplerState, andImDrawCallback_ResetRenderStatehands rendering back to the ImGui pipeline.mvViewport_apple.mmstashes the currentMTLRenderCommandEncoderonmvGraphics_Metaleach frame so the callback can reach it. Astatic_assertonsizeof(ImDrawVert)guards against future vertex-format drift.Fully backwards compatible. Default path is linear and has zero overhead — the
ImDrawList::AddCallbackpair is only emitted when the bound texture has_filter == mvTextureFilter_Nearest.Tested end-to-end on all three backends:
Each platform ran side-by-side comparisons of linear vs. nearest across
add_image,draw_image, andadd_image_series, plus runtime toggling viaconfigure_item. Surrounding ImGui widgets (text, buttons, plot axes) render with linear filtering every frame regardless of any texture's filter state. The callback swap is scoped per image andImDrawCallback_ResetRenderStaterestores the backend's defaults. Additional verification on bare-metal x64 Windows and native Linux hardware from contributors with access to those machines would be welcome.Demo
Concerning Areas:
Metal shader duplication. The DPG-owned pipeline in
mvGraphics_apple.mmduplicates the vertex layout and blend state of ImGui's Metal shader. A future change to ImGui'sImDrawVertlayout would require updating the DPG shader as thestatic_assertonsizeof(ImDrawVert) == 20turns this into a build-time failure rather than a runtime one. The cleaner long-term fix is an upstream PR toimgui_impl_metal.mmmaking its sampler bindable, after which this parallel pipeline can be removed. Happy to follow up separately if desired.Draw-call batching on nearest-filtered images. Each nearest-filtered draw emits two
ImDrawList::AddCallbackentries, which break ImGui's draw-call merging. Cost is bounded (a few hundred extra GPU commands for a few hundred nearest images — sub-millisecond on any modern GPU) and is only paid on the nearest path. The default linear path is unchanged.ImFontAtlasFlags_NoBakedLinesnot set. The ImGui DX11 backend comments that bilinear sampling is required for baked antialiased lines (imgui_impl_dx11.cpp:578), but our sampler swap is scoped per-image-draw andImDrawCallback_ResetRenderStaterestores linear before the AA-line textures are sampled. Worth flagging in case DX11 testing on native machines surfaces an interaction not reproducible on macOS-native host.