diff --git a/dearpygui/_dearpygui.pyi b/dearpygui/_dearpygui.pyi index 456a4b845..a310dc819 100644 --- a/dearpygui/_dearpygui.pyi +++ b/dearpygui/_dearpygui.pyi @@ -178,7 +178,7 @@ def add_drawlist(width : int, height : int, *, label: str ='', user_data: Any =' """Adds a drawing canvas.""" ... -def add_dynamic_texture(width : int, height : int, default_value : Union[List[float], Tuple[float, ...]], *, label: str ='', user_data: Any ='', use_internal_label: bool ='', tag: Union[int, str] ='', parent: Union[int, str] ='') -> Union[int, str]: +def add_dynamic_texture(width : int, height : int, default_value : Union[List[float], Tuple[float, ...]], *, label: str ='', user_data: Any ='', use_internal_label: bool ='', tag: Union[int, str] ='', parent: Union[int, str] ='', filter: int ='') -> Union[int, str]: """Adds a dynamic texture.""" ... @@ -455,7 +455,7 @@ def add_radio_button(items : Union[List[str], Tuple[str, ...]] ='', *, label: st """Adds a set of radio buttons. If items keyword is empty, nothing will be shown.""" ... -def add_raw_texture(width : int, height : int, default_value : Union[List[float], Tuple[float, ...]], *, label: str ='', user_data: Any ='', use_internal_label: bool ='', tag: Union[int, str] ='', format: int ='', parent: Union[int, str] ='') -> Union[int, str]: +def add_raw_texture(width : int, height : int, default_value : Union[List[float], Tuple[float, ...]], *, label: str ='', user_data: Any ='', use_internal_label: bool ='', tag: Union[int, str] ='', format: int ='', parent: Union[int, str] ='', filter: int ='') -> Union[int, str]: """Adds a raw texture.""" ... @@ -519,7 +519,7 @@ def add_stair_series(x : Union[List[float], Tuple[float, ...]], y : Union[List[f """Adds a stair series to a plot.""" ... -def add_static_texture(width : int, height : int, default_value : Union[List[float], Tuple[float, ...]], *, label: str ='', user_data: Any ='', use_internal_label: bool ='', tag: Union[int, str] ='', parent: Union[int, str] ='') -> Union[int, str]: +def add_static_texture(width : int, height : int, default_value : Union[List[float], Tuple[float, ...]], *, label: str ='', user_data: Any ='', use_internal_label: bool ='', tag: Union[int, str] ='', parent: Union[int, str] ='', filter: int ='') -> Union[int, str]: """Adds a static texture.""" ... @@ -1553,6 +1553,8 @@ mvTreeLines_Full=0 mvTreeLines_ToNodes=0 mvFormat_Float_rgba=0 mvFormat_Float_rgb=0 +mvTextureFilter_Linear=0 +mvTextureFilter_Nearest=0 mvThemeCat_Core=0 mvThemeCat_Plots=0 mvThemeCat_Nodes=0 diff --git a/dearpygui/_dearpygui_RTD.py b/dearpygui/_dearpygui_RTD.py index 2cd741717..af2881854 100644 --- a/dearpygui/_dearpygui_RTD.py +++ b/dearpygui/_dearpygui_RTD.py @@ -4101,6 +4101,7 @@ def add_dynamic_texture(width, height, default_value, **kwargs): use_internal_label (bool, optional): Use generated internal label instead of user specified (appends ### uuid). tag (Union[int, str], optional): Unique id used to programmatically refer to the item.If label is unused this will be the label. parent (Union[int, str], optional): Parent to add this item to. (runtime adding) + filter (int, optional): Texture sampling mode (mvTextureFilter_Linear or mvTextureFilter_Nearest). id (Union[int, str], optional): (deprecated) Returns: Union[int, str] @@ -5853,6 +5854,7 @@ def add_raw_texture(width, height, default_value, **kwargs): tag (Union[int, str], optional): Unique id used to programmatically refer to the item.If label is unused this will be the label. format (int, optional): Data format. parent (Union[int, str], optional): Parent to add this item to. (runtime adding) + filter (int, optional): Texture sampling mode (mvTextureFilter_Linear or mvTextureFilter_Nearest). id (Union[int, str], optional): (deprecated) Returns: Union[int, str] @@ -6308,6 +6310,7 @@ def add_static_texture(width, height, default_value, **kwargs): use_internal_label (bool, optional): Use generated internal label instead of user specified (appends ### uuid). tag (Union[int, str], optional): Unique id used to programmatically refer to the item.If label is unused this will be the label. parent (Union[int, str], optional): Parent to add this item to. (runtime adding) + filter (int, optional): Texture sampling mode (mvTextureFilter_Linear or mvTextureFilter_Nearest). id (Union[int, str], optional): (deprecated) Returns: Union[int, str] @@ -9218,6 +9221,8 @@ def unstage(item): mvTreeLines_ToNodes=internal_dpg.mvTreeLines_ToNodes mvFormat_Float_rgba=internal_dpg.mvFormat_Float_rgba mvFormat_Float_rgb=internal_dpg.mvFormat_Float_rgb +mvTextureFilter_Linear=internal_dpg.mvTextureFilter_Linear +mvTextureFilter_Nearest=internal_dpg.mvTextureFilter_Nearest mvThemeCat_Core=internal_dpg.mvThemeCat_Core mvThemeCat_Plots=internal_dpg.mvThemeCat_Plots mvThemeCat_Nodes=internal_dpg.mvThemeCat_Nodes diff --git a/dearpygui/dearpygui.py b/dearpygui/dearpygui.py index 1e23f10c3..ea445a290 100644 --- a/dearpygui/dearpygui.py +++ b/dearpygui/dearpygui.py @@ -4536,7 +4536,7 @@ def add_drawlist(width : int, height : int, *, label: str =None, user_data: Any return internal_dpg.add_drawlist(width, height, label=label, user_data=user_data, use_internal_label=use_internal_label, tag=tag, parent=parent, before=before, callback=callback, show=show, pos=pos, filter_key=filter_key, tracked=tracked, track_offset=track_offset, **kwargs) -def add_dynamic_texture(width : int, height : int, default_value : Union[List[float], Tuple[float, ...]], *, label: str =None, user_data: Any =None, use_internal_label: bool =True, tag: Union[int, str] =0, parent: Union[int, str] =internal_dpg.mvReservedUUID_2, **kwargs) -> Union[int, str]: +def add_dynamic_texture(width : int, height : int, default_value : Union[List[float], Tuple[float, ...]], *, label: str =None, user_data: Any =None, use_internal_label: bool =True, tag: Union[int, str] =0, parent: Union[int, str] =internal_dpg.mvReservedUUID_2, filter: int =0, **kwargs) -> Union[int, str]: """ Adds a dynamic texture. Args: @@ -4548,6 +4548,7 @@ def add_dynamic_texture(width : int, height : int, default_value : Union[List[fl use_internal_label (bool, optional): Use generated internal label instead of user specified (appends ### uuid). tag (Union[int, str], optional): Unique id used to programmatically refer to the item.If label is unused this will be the label. parent (Union[int, str], optional): Parent to add this item to. (runtime adding) + filter (int, optional): Texture sampling mode (mvTextureFilter_Linear or mvTextureFilter_Nearest). id (Union[int, str], optional): (deprecated) Returns: Union[int, str] @@ -4557,7 +4558,7 @@ def add_dynamic_texture(width : int, height : int, default_value : Union[List[fl warnings.warn('id keyword renamed to tag', DeprecationWarning, 2) tag=kwargs['id'] - return internal_dpg.add_dynamic_texture(width, height, default_value, label=label, user_data=user_data, use_internal_label=use_internal_label, tag=tag, parent=parent, **kwargs) + return internal_dpg.add_dynamic_texture(width, height, default_value, label=label, user_data=user_data, use_internal_label=use_internal_label, tag=tag, parent=parent, filter=filter, **kwargs) def add_error_series(x : Union[List[float], Tuple[float, ...]], y : Union[List[float], Tuple[float, ...]], negative : Union[List[float], Tuple[float, ...]], positive : Union[List[float], Tuple[float, ...]], *, label: str =None, user_data: Any =None, use_internal_label: bool =True, tag: Union[int, str] =0, parent: Union[int, str] =0, before: Union[int, str] =0, source: Union[int, str] =0, show: bool =True, contribute_to_bounds: bool =True, horizontal: bool =False, **kwargs) -> Union[int, str]: """ Adds an error series to a plot. @@ -6639,7 +6640,7 @@ def add_radio_button(items : Union[List[str], Tuple[str, ...]] =(), *, label: st return internal_dpg.add_radio_button(items, label=label, user_data=user_data, use_internal_label=use_internal_label, tag=tag, indent=indent, parent=parent, before=before, source=source, payload_type=payload_type, callback=callback, drag_callback=drag_callback, drop_callback=drop_callback, show=show, enabled=enabled, pos=pos, filter_key=filter_key, tracked=tracked, track_offset=track_offset, default_value=default_value, horizontal=horizontal, **kwargs) -def add_raw_texture(width : int, height : int, default_value : Union[List[float], Tuple[float, ...]], *, label: str =None, user_data: Any =None, use_internal_label: bool =True, tag: Union[int, str] =0, format: int =internal_dpg.mvFormat_Float_rgba, parent: Union[int, str] =internal_dpg.mvReservedUUID_2, **kwargs) -> Union[int, str]: +def add_raw_texture(width : int, height : int, default_value : Union[List[float], Tuple[float, ...]], *, label: str =None, user_data: Any =None, use_internal_label: bool =True, tag: Union[int, str] =0, format: int =internal_dpg.mvFormat_Float_rgba, parent: Union[int, str] =internal_dpg.mvReservedUUID_2, filter: int =0, **kwargs) -> Union[int, str]: """ Adds a raw texture. Args: @@ -6652,6 +6653,7 @@ def add_raw_texture(width : int, height : int, default_value : Union[List[float] tag (Union[int, str], optional): Unique id used to programmatically refer to the item.If label is unused this will be the label. format (int, optional): Data format. parent (Union[int, str], optional): Parent to add this item to. (runtime adding) + filter (int, optional): Texture sampling mode (mvTextureFilter_Linear or mvTextureFilter_Nearest). id (Union[int, str], optional): (deprecated) Returns: Union[int, str] @@ -6661,7 +6663,7 @@ def add_raw_texture(width : int, height : int, default_value : Union[List[float] warnings.warn('id keyword renamed to tag', DeprecationWarning, 2) tag=kwargs['id'] - return internal_dpg.add_raw_texture(width, height, default_value, label=label, user_data=user_data, use_internal_label=use_internal_label, tag=tag, format=format, parent=parent, **kwargs) + return internal_dpg.add_raw_texture(width, height, default_value, label=label, user_data=user_data, use_internal_label=use_internal_label, tag=tag, format=format, parent=parent, filter=filter, **kwargs) def add_scatter_series(x : Union[List[float], Tuple[float, ...]], y : Union[List[float], Tuple[float, ...]], *, label: str =None, user_data: Any =None, use_internal_label: bool =True, tag: Union[int, str] =0, parent: Union[int, str] =0, before: Union[int, str] =0, source: Union[int, str] =0, show: bool =True, no_clip: bool =False, **kwargs) -> Union[int, str]: """ Adds a scatter series to a plot. @@ -7159,7 +7161,7 @@ def add_stair_series(x : Union[List[float], Tuple[float, ...]], y : Union[List[f return internal_dpg.add_stair_series(x, y, label=label, user_data=user_data, use_internal_label=use_internal_label, tag=tag, parent=parent, before=before, source=source, show=show, pre_step=pre_step, shaded=shaded, **kwargs) -def add_static_texture(width : int, height : int, default_value : Union[List[float], Tuple[float, ...]], *, label: str =None, user_data: Any =None, use_internal_label: bool =True, tag: Union[int, str] =0, parent: Union[int, str] =internal_dpg.mvReservedUUID_2, **kwargs) -> Union[int, str]: +def add_static_texture(width : int, height : int, default_value : Union[List[float], Tuple[float, ...]], *, label: str =None, user_data: Any =None, use_internal_label: bool =True, tag: Union[int, str] =0, parent: Union[int, str] =internal_dpg.mvReservedUUID_2, filter: int =0, **kwargs) -> Union[int, str]: """ Adds a static texture. Args: @@ -7171,6 +7173,7 @@ def add_static_texture(width : int, height : int, default_value : Union[List[flo use_internal_label (bool, optional): Use generated internal label instead of user specified (appends ### uuid). tag (Union[int, str], optional): Unique id used to programmatically refer to the item.If label is unused this will be the label. parent (Union[int, str], optional): Parent to add this item to. (runtime adding) + filter (int, optional): Texture sampling mode (mvTextureFilter_Linear or mvTextureFilter_Nearest). id (Union[int, str], optional): (deprecated) Returns: Union[int, str] @@ -7180,7 +7183,7 @@ def add_static_texture(width : int, height : int, default_value : Union[List[flo warnings.warn('id keyword renamed to tag', DeprecationWarning, 2) tag=kwargs['id'] - return internal_dpg.add_static_texture(width, height, default_value, label=label, user_data=user_data, use_internal_label=use_internal_label, tag=tag, parent=parent, **kwargs) + return internal_dpg.add_static_texture(width, height, default_value, label=label, user_data=user_data, use_internal_label=use_internal_label, tag=tag, parent=parent, filter=filter, **kwargs) def add_stem_series(x : Union[List[float], Tuple[float, ...]], y : Union[List[float], Tuple[float, ...]], *, label: str =None, user_data: Any =None, use_internal_label: bool =True, tag: Union[int, str] =0, indent: int =-1, parent: Union[int, str] =0, before: Union[int, str] =0, source: Union[int, str] =0, show: bool =True, horizontal: bool =False, **kwargs) -> Union[int, str]: """ Adds a stem series to a plot. @@ -10311,6 +10314,8 @@ def unstage(item : Union[int, str], **kwargs) -> None: mvTreeLines_ToNodes=internal_dpg.mvTreeLines_ToNodes mvFormat_Float_rgba=internal_dpg.mvFormat_Float_rgba mvFormat_Float_rgb=internal_dpg.mvFormat_Float_rgb +mvTextureFilter_Linear=internal_dpg.mvTextureFilter_Linear +mvTextureFilter_Nearest=internal_dpg.mvTextureFilter_Nearest mvThemeCat_Core=internal_dpg.mvThemeCat_Core mvThemeCat_Plots=internal_dpg.mvThemeCat_Plots mvThemeCat_Nodes=internal_dpg.mvThemeCat_Nodes diff --git a/src/dearpygui.cpp b/src/dearpygui.cpp index 48e10493d..4a24a5335 100644 --- a/src/dearpygui.cpp +++ b/src/dearpygui.cpp @@ -229,6 +229,9 @@ GetModuleConstants() ModuleConstants.push_back({ "mvFormat_Float_rgba", 0L }); ModuleConstants.push_back({ "mvFormat_Float_rgb", 1L }); + ModuleConstants.push_back({ "mvTextureFilter_Linear", mvTextureFilter_Linear }); + ModuleConstants.push_back({ "mvTextureFilter_Nearest", mvTextureFilter_Nearest }); + ModuleConstants.push_back({ "mvThemeCat_Core", 0L }); ModuleConstants.push_back({ "mvThemeCat_Plots", 1L}); ModuleConstants.push_back({ "mvThemeCat_Nodes", 2L}); diff --git a/src/mvAppItem.cpp b/src/mvAppItem.cpp index 10fd1b73d..3b58a6abe 100644 --- a/src/mvAppItem.cpp +++ b/src/mvAppItem.cpp @@ -4636,7 +4636,7 @@ DearPyGui::GetEntityParser(mvAppItemType type) setup.createContextManager = true; break; } - case mvAppItemType::mvStaticTexture: + case mvAppItemType::mvStaticTexture: { AddCommonArgs(args, (CommonParserArgs)( MV_PARSER_ARG_ID) @@ -4646,12 +4646,13 @@ DearPyGui::GetEntityParser(mvAppItemType type) args.push_back({ mvPyDataType::Integer, "height" }); args.push_back({ mvPyDataType::FloatList, "default_value" }); args.push_back({ mvPyDataType::UUID, "parent", mvArgType::KEYWORD_ARG, "internal_dpg.mvReservedUUID_2", "Parent to add this item to. (runtime adding)" }); + args.push_back({ mvPyDataType::Integer, "filter", mvArgType::KEYWORD_ARG, "0", "Texture sampling mode (mvTextureFilter_Linear or mvTextureFilter_Nearest)." }); setup.about = "Adds a static texture."; setup.category = { "Textures", "Widgets" }; break; } - case mvAppItemType::mvDynamicTexture: + case mvAppItemType::mvDynamicTexture: { AddCommonArgs(args, (CommonParserArgs)( MV_PARSER_ARG_ID) @@ -4661,6 +4662,7 @@ DearPyGui::GetEntityParser(mvAppItemType type) args.push_back({ mvPyDataType::Integer, "height" }); args.push_back({ mvPyDataType::FloatList, "default_value" }); args.push_back({ mvPyDataType::UUID, "parent", mvArgType::KEYWORD_ARG, "internal_dpg.mvReservedUUID_2", "Parent to add this item to. (runtime adding)" }); + args.push_back({ mvPyDataType::Integer, "filter", mvArgType::KEYWORD_ARG, "0", "Texture sampling mode (mvTextureFilter_Linear or mvTextureFilter_Nearest)." }); setup.about = "Adds a dynamic texture."; setup.category = { "Textures", "Widgets" }; @@ -5414,7 +5416,7 @@ DearPyGui::GetEntityParser(mvAppItemType type) setup.category = { "Widgets", "Values" }; break; } - case mvAppItemType::mvRawTexture: + case mvAppItemType::mvRawTexture: { AddCommonArgs(args, (CommonParserArgs)( MV_PARSER_ARG_ID) @@ -5425,6 +5427,7 @@ DearPyGui::GetEntityParser(mvAppItemType type) args.push_back({ mvPyDataType::FloatList, "default_value" }); args.push_back({ mvPyDataType::Integer, "format", mvArgType::KEYWORD_ARG, "internal_dpg.mvFormat_Float_rgba", "Data format." }); args.push_back({ mvPyDataType::UUID, "parent", mvArgType::KEYWORD_ARG, "internal_dpg.mvReservedUUID_2", "Parent to add this item to. (runtime adding)" }); + args.push_back({ mvPyDataType::Integer, "filter", mvArgType::KEYWORD_ARG, "0", "Texture sampling mode (mvTextureFilter_Linear or mvTextureFilter_Nearest)." }); setup.about = "Adds a raw texture."; setup.category = { "Textures", "Widgets" }; diff --git a/src/mvAppleSpecifics.h b/src/mvAppleSpecifics.h index b44a8bb2c..f3444483b 100644 --- a/src/mvAppleSpecifics.h +++ b/src/mvAppleSpecifics.h @@ -16,7 +16,11 @@ struct mvViewportData struct mvGraphics_Metal { - MTLRenderPassDescriptor* renderPassDescriptor; - id commandQueue; - id device; + MTLRenderPassDescriptor* renderPassDescriptor; + id commandQueue; + id device; + + id nearestPipelineState; + id nearestSampler; + id __unsafe_unretained currentEncoder; }; \ No newline at end of file diff --git a/src/mvBasicWidgets.cpp b/src/mvBasicWidgets.cpp index 6111f2f6c..075bf8a0b 100644 --- a/src/mvBasicWidgets.cpp +++ b/src/mvBasicWidgets.cpp @@ -8,6 +8,7 @@ #include "mvContainers.h" #include "mvItemHandlers.h" #include "mvTextureItems.h" +#include "mvUtilities.h" #include #include @@ -5604,6 +5605,9 @@ DearPyGui::draw_image(ImDrawList* drawlist, mvAppItem& item, mvImageConfig& conf ImGuiContext& g = *GImGui; ImGui::PushStyleVar(ImGuiStyleVar_ImageBorderSize, (config.borderColor.a > 0.0f) ? ImMax(1.0f, g.Style.ImageBorderSize) : 0.0f); ImGui::PushStyleColor(ImGuiCol_Border, config.borderColor.toVec4()); + const bool wantNearest = (static_cast(config.texture.get())->_filter == mvTextureFilter_Nearest); + ImDrawList* windowDrawList = wantNearest ? ImGui::GetWindowDrawList() : nullptr; + if (wantNearest) EnterNearestFilterScope(windowDrawList); ImGui::ImageWithBg( texture, ImVec2((float)item.config.width, (float)item.config.height), @@ -5611,6 +5615,7 @@ DearPyGui::draw_image(ImDrawList* drawlist, mvAppItem& item, mvImageConfig& conf ImVec2(config.uv_max.x, config.uv_max.y), ImVec4(0, 0, 0, 0), config.tintColor.toVec4()); + if (wantNearest) LeaveNearestFilterScope(windowDrawList); ImGui::PopStyleColor(); ImGui::PopStyleVar(); } @@ -5717,12 +5722,16 @@ DearPyGui::draw_image_button(ImDrawList* drawlist, mvAppItem& item, mvImageButto if (config.framePadding >= 0) ImGui::PushStyleVar(ImGuiStyleVar_FramePadding, ImVec2((float)config.framePadding, (float)config.framePadding)); + const bool wantNearest = (static_cast(config.texture.get())->_filter == mvTextureFilter_Nearest); + ImDrawList* windowDrawList = wantNearest ? ImGui::GetWindowDrawList() : nullptr; + if (wantNearest) EnterNearestFilterScope(windowDrawList); if (ImGui::ImageButton(item.info.internalLabel.c_str(), texture, ImVec2((float)item.config.width, (float)item.config.height), ImVec2(config.uv_min.x, config.uv_min.y), ImVec2(config.uv_max.x, config.uv_max.y), config.backgroundColor, config.tintColor)) { item.submitCallback(); } + if (wantNearest) LeaveNearestFilterScope(windowDrawList); if (config.framePadding >= 0) ImGui::PopStyleVar(); diff --git a/src/mvDrawings.cpp b/src/mvDrawings.cpp index be3dc6232..4884b9952 100644 --- a/src/mvDrawings.cpp +++ b/src/mvDrawings.cpp @@ -8,6 +8,7 @@ #include "mvFontItems.h" #include "mvItemHandlers.h" #include "mvTextureItems.h" +#include "mvUtilities.h" #include "mvCustomTypes.h" #include @@ -561,6 +562,8 @@ void mvDrawImage::draw(ImDrawList* drawlist, float x, float y) if (mvClipPoint(drawInfo->clipViewport, tpmax)) return; } + const bool wantNearest = (static_cast(_texture.get())->_filter == mvTextureFilter_Nearest); + if (wantNearest) EnterNearestFilterScope(drawlist); if (ImPlot::GetCurrentContext()->CurrentPlot) drawlist->AddImage(texture, ImPlot::PlotToPixels(tpmin), ImPlot::PlotToPixels(tpmax), _uv_min, _uv_max, _color); else @@ -568,6 +571,7 @@ void mvDrawImage::draw(ImDrawList* drawlist, float x, float y) mvVec2 start = { x, y }; drawlist->AddImage(texture, tpmin + start, tpmax + start, _uv_min, _uv_max, _color); } + if (wantNearest) LeaveNearestFilterScope(drawlist); } } @@ -679,6 +683,8 @@ void mvDrawImageQuad::draw(ImDrawList* drawlist, float x, float y) if (mvClipPoint(drawInfo->clipViewport, tp4)) return; } + const bool wantNearest = (static_cast(_texture.get())->_filter == mvTextureFilter_Nearest); + if (wantNearest) EnterNearestFilterScope(drawlist); if (ImPlot::GetCurrentContext()->CurrentPlot) drawlist->AddImageQuad(texture, ImPlot::PlotToPixels(tp1), ImPlot::PlotToPixels(tp2), ImPlot::PlotToPixels(tp3), ImPlot::PlotToPixels(tp4), @@ -688,6 +694,7 @@ void mvDrawImageQuad::draw(ImDrawList* drawlist, float x, float y) mvVec2 start = { x, y }; drawlist->AddImageQuad(texture, tp1 + start, tp2 + start, tp3 + start, tp4 + start, _uv1, _uv2, _uv3, _uv4, _color); } + if (wantNearest) LeaveNearestFilterScope(drawlist); } } diff --git a/src/mvGraphics_apple.mm b/src/mvGraphics_apple.mm index b6ce2f135..bd914004c 100644 --- a/src/mvGraphics_apple.mm +++ b/src/mvGraphics_apple.mm @@ -7,6 +7,106 @@ #include "imgui_impl_glfw.h" #include "imgui_impl_metal.h" +static void +BuildNearestFilterMetalState(mvGraphics_Metal* graphicsData, MTLPixelFormat colorPixelFormat) +{ + MTLSamplerDescriptor* samplerDesc = [MTLSamplerDescriptor new]; + samplerDesc.minFilter = MTLSamplerMinMagFilterNearest; + samplerDesc.magFilter = MTLSamplerMinMagFilterNearest; + samplerDesc.mipFilter = MTLSamplerMipFilterNearest; + samplerDesc.sAddressMode = MTLSamplerAddressModeClampToEdge; + samplerDesc.tAddressMode = MTLSamplerAddressModeClampToEdge; + samplerDesc.rAddressMode = MTLSamplerAddressModeClampToEdge; + graphicsData->nearestSampler = [graphicsData->device newSamplerStateWithDescriptor:samplerDesc]; + + // Mirrors imgui_impl_metal.mm's shader with a bindable sampler. + NSString* shaderSource = @"" + "#include \n" + "using namespace metal;\n" + "\n" + "struct Uniforms { float4x4 projectionMatrix; };\n" + "\n" + "struct VertexIn {\n" + " float2 position [[attribute(0)]];\n" + " float2 texCoords [[attribute(1)]];\n" + " uchar4 color [[attribute(2)]];\n" + "};\n" + "\n" + "struct VertexOut {\n" + " float4 position [[position]];\n" + " float2 texCoords;\n" + " float4 color;\n" + "};\n" + "\n" + "vertex VertexOut dpg_vertex_main(VertexIn in [[stage_in]],\n" + " constant Uniforms &uniforms [[buffer(1)]]) {\n" + " VertexOut out;\n" + " out.position = uniforms.projectionMatrix * float4(in.position, 0, 1);\n" + " out.texCoords = in.texCoords;\n" + " out.color = float4(in.color) / float4(255.0);\n" + " return out;\n" + "}\n" + "\n" + "fragment half4 dpg_fragment_main(VertexOut in [[stage_in]],\n" + " texture2d texture [[texture(0)]],\n" + " sampler textureSampler [[sampler(0)]]) {\n" + " half4 texColor = texture.sample(textureSampler, in.texCoords);\n" + " return half4(in.color) * texColor;\n" + "}\n"; + + NSError* error = nil; + id library = [graphicsData->device newLibraryWithSource:shaderSource options:nil error:&error]; + if (library == nil) + { + NSLog(@"DPG: failed to compile nearest-filter Metal library: %@", error); + return; + } + + id vertexFunction = [library newFunctionWithName:@"dpg_vertex_main"]; + id fragmentFunction = [library newFunctionWithName:@"dpg_fragment_main"]; + if (!vertexFunction || !fragmentFunction) + { + NSLog(@"DPG: failed to resolve nearest-filter Metal functions"); + return; + } + + MTLVertexDescriptor* vertexDescriptor = [MTLVertexDescriptor vertexDescriptor]; + vertexDescriptor.attributes[0].offset = offsetof(ImDrawVert, pos); + vertexDescriptor.attributes[0].format = MTLVertexFormatFloat2; + vertexDescriptor.attributes[0].bufferIndex = 0; + vertexDescriptor.attributes[1].offset = offsetof(ImDrawVert, uv); + vertexDescriptor.attributes[1].format = MTLVertexFormatFloat2; + vertexDescriptor.attributes[1].bufferIndex = 0; + vertexDescriptor.attributes[2].offset = offsetof(ImDrawVert, col); + vertexDescriptor.attributes[2].format = MTLVertexFormatUChar4; + vertexDescriptor.attributes[2].bufferIndex = 0; + vertexDescriptor.layouts[0].stepRate = 1; + vertexDescriptor.layouts[0].stepFunction = MTLVertexStepFunctionPerVertex; + vertexDescriptor.layouts[0].stride = sizeof(ImDrawVert); + + MTLRenderPipelineDescriptor* pipelineDescriptor = [[MTLRenderPipelineDescriptor alloc] init]; + pipelineDescriptor.vertexFunction = vertexFunction; + pipelineDescriptor.fragmentFunction = fragmentFunction; + pipelineDescriptor.vertexDescriptor = vertexDescriptor; + pipelineDescriptor.rasterSampleCount = 1; + pipelineDescriptor.colorAttachments[0].pixelFormat = colorPixelFormat; + pipelineDescriptor.colorAttachments[0].blendingEnabled = YES; + pipelineDescriptor.colorAttachments[0].rgbBlendOperation = MTLBlendOperationAdd; + pipelineDescriptor.colorAttachments[0].sourceRGBBlendFactor = MTLBlendFactorSourceAlpha; + pipelineDescriptor.colorAttachments[0].destinationRGBBlendFactor = MTLBlendFactorOneMinusSourceAlpha; + pipelineDescriptor.colorAttachments[0].alphaBlendOperation = MTLBlendOperationAdd; + pipelineDescriptor.colorAttachments[0].sourceAlphaBlendFactor= MTLBlendFactorOne; + pipelineDescriptor.colorAttachments[0].destinationAlphaBlendFactor = MTLBlendFactorOneMinusSourceAlpha; + pipelineDescriptor.depthAttachmentPixelFormat = MTLPixelFormatInvalid; + pipelineDescriptor.stencilAttachmentPixelFormat = MTLPixelFormatInvalid; + + graphicsData->nearestPipelineState = [graphicsData->device newRenderPipelineStateWithDescriptor:pipelineDescriptor error:&error]; + if (error != nil) + NSLog(@"DPG: failed to create nearest-filter pipeline state: %@", error); + + static_assert(sizeof(ImDrawVert) == 20, "ImDrawVert layout changed; update DPG nearest-filter pipeline"); +} + mvGraphics setup_graphics(mvViewport &viewport) { @@ -30,6 +130,8 @@ graphicsData->renderPassDescriptor = [MTLRenderPassDescriptor new]; + BuildNearestFilterMetalState(graphicsData, viewportData->layer.pixelFormat); + return graphics; } diff --git a/src/mvGraphics_win32.cpp b/src/mvGraphics_win32.cpp index 24da2ec19..8268bdca2 100644 --- a/src/mvGraphics_win32.cpp +++ b/src/mvGraphics_win32.cpp @@ -124,6 +124,16 @@ setup_graphics(mvViewport& viewport) ImGui_ImplDX11_Init(graphicsData->device, graphicsData->deviceContext); + { + D3D11_SAMPLER_DESC desc; + ZeroMemory(&desc, sizeof(desc)); + desc.Filter = D3D11_FILTER_MIN_MAG_MIP_POINT; + desc.AddressU = D3D11_TEXTURE_ADDRESS_CLAMP; + desc.AddressV = D3D11_TEXTURE_ADDRESS_CLAMP; + desc.AddressW = D3D11_TEXTURE_ADDRESS_CLAMP; + graphicsData->device->CreateSamplerState(&desc, &graphicsData->pTexSamplerNearest); + } + return graphics; } @@ -134,6 +144,12 @@ cleanup_graphics(mvGraphics& graphics) ImGui_ImplDX11_Shutdown(); + if (graphicsData->pTexSamplerNearest) + { + graphicsData->pTexSamplerNearest->Release(); + graphicsData->pTexSamplerNearest = nullptr; + } + if (graphicsData->target) { graphicsData->target->Release(); diff --git a/src/mvPlotting.cpp b/src/mvPlotting.cpp index d11375106..d056fecc7 100644 --- a/src/mvPlotting.cpp +++ b/src/mvPlotting.cpp @@ -10,6 +10,7 @@ #include "mvThemes.h" #include "mvContainers.h" #include "mvTextureItems.h" +#include "mvUtilities.h" #include "mvItemHandlers.h" #include @@ -1934,7 +1935,11 @@ DearPyGui::draw_image_series(ImDrawList* drawlist, mvAppItem& item, mvImageSerie // ImTextureRef texture = static_cast(config._texture.get())->getTexRef(); ImTextureID texture = static_cast(config._texture.get())->_texture; + const bool wantNearest = (static_cast(config._texture.get())->_filter == mvTextureFilter_Nearest); + ImDrawList* plotDrawList = wantNearest ? ImPlot::GetPlotDrawList() : nullptr; + if (wantNearest) EnterNearestFilterScope(plotDrawList); ImPlot::PlotImage(item.info.internalLabel.c_str(), texture, config.bounds_min, config.bounds_max, config.uv_min, config.uv_max, config.tintColor, config.flags); + if (wantNearest) LeaveNearestFilterScope(plotDrawList); // Begin a popup for a legend entry. if (ImPlot::BeginLegendPopup(item.info.internalLabel.c_str(), 1)) diff --git a/src/mvTextureItems.cpp b/src/mvTextureItems.cpp index baf606a87..db2fcf139 100644 --- a/src/mvTextureItems.cpp +++ b/src/mvTextureItems.cpp @@ -157,15 +157,22 @@ void mvDynamicTexture::draw(ImDrawList* drawlist, float x, float y) if (_dirty) { - _texture = LoadTextureFromArrayDynamic(_permWidth, _permHeight, _value->data()); + _texture = LoadTextureFromArrayDynamic(_permWidth, _permHeight, _value->data(), _filter); if (_texture == ImTextureID_Invalid) state.ok = false; _dirty = false; + _filterDirty = false; return; } + if (_filterDirty && _texture != ImTextureID_Invalid) + { + ApplyTextureFilter(_texture, _filter); + _filterDirty = false; + } + UpdateTexture(_texture, _permWidth, _permHeight, *_value); } @@ -187,12 +194,23 @@ void mvDynamicTexture::handleSpecificKeywordArgs(PyObject* dict) if (dict == nullptr) return; + if (PyObject* item = PyDict_GetItemString(dict, "filter")) + { + const int v = ToInt(item); + if (v != _filter) + { + _filter = v; + _filterDirty = true; + } + } } void mvDynamicTexture::getSpecificConfiguration(PyObject* dict) { if (dict == nullptr) return; + + PyDict_SetItemString(dict, "filter", mvPyObject(ToPyInt(_filter))); } PyObject* mvRawTexture::getPyValue() @@ -233,15 +251,22 @@ void mvRawTexture::draw(ImDrawList* drawlist, float x, float y) return; if (_componentType == ComponentType::MV_FLOAT_COMPONENT) - _texture = LoadTextureFromArrayRaw(_permWidth, _permHeight, (float*)_value, _components); + _texture = LoadTextureFromArrayRaw(_permWidth, _permHeight, (float*)_value, _components, _filter); if (_texture == ImTextureID_Invalid) state.ok = false; _dirty = false; + _filterDirty = false; return; } + if (_filterDirty && _texture != ImTextureID_Invalid) + { + ApplyTextureFilter(_texture, _filter); + _filterDirty = false; + } + if (_componentType == ComponentType::MV_FLOAT_COMPONENT) UpdateRawTexture(_texture, _permWidth, _permHeight, (float*)_value, _components); @@ -281,12 +306,24 @@ void mvRawTexture::handleSpecificKeywordArgs(PyObject* dict) _componentType = mvRawTexture::ComponentType::MV_FLOAT_COMPONENT; } } + + if (PyObject* item = PyDict_GetItemString(dict, "filter")) + { + const int v = ToInt(item); + if (v != _filter) + { + _filter = v; + _filterDirty = true; + } + } } void mvRawTexture::getSpecificConfiguration(PyObject* dict) { if (dict == nullptr) return; + + PyDict_SetItemString(dict, "filter", mvPyObject(ToPyInt(_filter))); } void mvStaticTexture::draw(ImDrawList* drawlist, float x, float y) @@ -303,13 +340,19 @@ void mvStaticTexture::draw(ImDrawList* drawlist, float x, float y) return; } + if (_filterDirty && _texture != ImTextureID_Invalid) + { + ApplyTextureFilter(_texture, _filter); + _filterDirty = false; + } + if (!_dirty) return; if (!state.ok) return; - _texture = LoadTextureFromArray(_permWidth, _permHeight, _value->data()); + _texture = LoadTextureFromArray(_permWidth, _permHeight, _value->data(), _filter); if (_texture == ImTextureID_Invalid) { @@ -320,6 +363,7 @@ void mvStaticTexture::draw(ImDrawList* drawlist, float x, float y) _dirty = false; + _filterDirty = false; } void mvStaticTexture::handleSpecificRequiredArgs(PyObject* dict) @@ -334,6 +378,30 @@ void mvStaticTexture::handleSpecificRequiredArgs(PyObject* dict) *_value = ToFloatVect(PyTuple_GetItem(dict, 2)); } +void mvStaticTexture::handleSpecificKeywordArgs(PyObject* dict) +{ + if (dict == nullptr) + return; + + if (PyObject* item = PyDict_GetItemString(dict, "filter")) + { + const int v = ToInt(item); + if (v != _filter) + { + _filter = v; + _filterDirty = true; + } + } +} + +void mvStaticTexture::getSpecificConfiguration(PyObject* dict) +{ + if (dict == nullptr) + return; + + PyDict_SetItemString(dict, "filter", mvPyObject(ToPyInt(_filter))); +} + PyObject* mvStaticTexture::getPyValue() { return ToPyList(*_value); diff --git a/src/mvTextureItems.h b/src/mvTextureItems.h index ce827adba..f5b7c64a6 100644 --- a/src/mvTextureItems.h +++ b/src/mvTextureItems.h @@ -35,6 +35,8 @@ class mvTextureItem : public mvAppItem public: ImTextureID _texture = ImTextureID_Invalid; + int _filter = 0; // mvTextureFilter_* + bool _filterDirty = false; }; @@ -47,6 +49,8 @@ class mvStaticTexture : public mvTextureItem void draw(ImDrawList* drawlist, float x, float y) override; void handleSpecificRequiredArgs(PyObject* dict) override; + void handleSpecificKeywordArgs(PyObject* dict) override; + void getSpecificConfiguration(PyObject* dict) override; // values void setDataSource(mvUUID dataSource) override; diff --git a/src/mvUtilities.h b/src/mvUtilities.h index 24aa4a747..f4c99ea52 100644 --- a/src/mvUtilities.h +++ b/src/mvUtilities.h @@ -19,21 +19,35 @@ typedef _object PyObject; #endif struct PymvBuffer; +struct ImDrawList; + +// mvTextureFilter_* constants (also registered on the Python module). +enum { + mvTextureFilter_Linear = 0, + mvTextureFilter_Nearest = 1, +}; // general void FreeTexture(ImTextureID texture); b8 UnloadTexture(const std::string& filename); - + +// Bracket ImDrawList::AddImage calls to render with nearest-neighbor sampling. +void EnterNearestFilterScope(ImDrawList* drawlist); +void LeaveNearestFilterScope(ImDrawList* drawlist); + +// Re-apply the filter state to an existing texture. Render-thread only. +void ApplyTextureFilter(ImTextureID texture, i32 filter); + // static textures ImTextureID LoadTextureFromFile(const char* filename, i32& width, i32& height); -ImTextureID LoadTextureFromArray(u32 width, u32 height, f32* data); +ImTextureID LoadTextureFromArray(u32 width, u32 height, f32* data, i32 filter = mvTextureFilter_Linear); // dynamic textures -ImTextureID LoadTextureFromArrayDynamic(u32 width, u32 height, f32* data); +ImTextureID LoadTextureFromArrayDynamic(u32 width, u32 height, f32* data, i32 filter = mvTextureFilter_Linear); void UpdateTexture(ImTextureID texture, u32 width, u32 height, std::vector& data); // raw textures -ImTextureID LoadTextureFromArrayRaw(u32 width, u32 height, f32* data, i32 components); +ImTextureID LoadTextureFromArrayRaw(u32 width, u32 height, f32* data, i32 components, i32 filter = mvTextureFilter_Linear); void UpdateRawTexture(ImTextureID texture, u32 width, u32 height, f32* data, i32 components); // framebuffer output diff --git a/src/mvUtilities_apple.mm b/src/mvUtilities_apple.mm index f6e25f1ef..bab958723 100644 --- a/src/mvUtilities_apple.mm +++ b/src/mvUtilities_apple.mm @@ -4,6 +4,7 @@ #include "mvUtilities.h" #include "mvViewport.h" +#include "mvContext.h" #include "mvAppleSpecifics.h" @@ -13,6 +14,7 @@ #define STB_IMAGE_WRITE_IMPLEMENTATION #include "stb_image_write.h" +#include #include #include #include @@ -36,8 +38,9 @@ } ImTextureID -LoadTextureFromArray(unsigned width, unsigned height, float* data) +LoadTextureFromArray(unsigned width, unsigned height, float* data, int filter) { + (void)filter; mvGraphics& graphics = GContext->graphics; auto graphicsData = (mvGraphics_Metal*)graphics.backendSpecifics; @@ -55,8 +58,9 @@ } ImTextureID -LoadTextureFromArrayDynamic(unsigned width, unsigned height, float* data) +LoadTextureFromArrayDynamic(unsigned width, unsigned height, float* data, int filter) { + (void)filter; mvGraphics& graphics = GContext->graphics; auto graphicsData = (mvGraphics_Metal*)graphics.backendSpecifics; @@ -74,8 +78,9 @@ } ImTextureID -LoadTextureFromArrayRaw(unsigned width, unsigned height, float* data, int components) +LoadTextureFromArrayRaw(unsigned width, unsigned height, float* data, int components, int filter) { + (void)filter; mvGraphics& graphics = GContext->graphics; auto graphicsData = (mvGraphics_Metal*)graphics.backendSpecifics; @@ -151,4 +156,35 @@ { id out_srv = (__bridge id )texture; [out_srv replaceRegion:MTLRegionMake2D(0, 0, width, height) mipmapLevel:0 withBytes:data bytesPerRow:width * components * 4]; +} + +static void +DpgMetalBindNearestPipeline(const ImDrawList* /*parent_list*/, const ImDrawCmd* /*cmd*/) +{ + auto* graphicsData = (mvGraphics_Metal*)GContext->graphics.backendSpecifics; + if (graphicsData == nullptr || graphicsData->currentEncoder == nil || + graphicsData->nearestPipelineState == nil || graphicsData->nearestSampler == nil) + return; + [graphicsData->currentEncoder setRenderPipelineState:graphicsData->nearestPipelineState]; + [graphicsData->currentEncoder setFragmentSamplerState:graphicsData->nearestSampler atIndex:0]; +} + +void +EnterNearestFilterScope(ImDrawList* drawlist) +{ + if (drawlist == nullptr) return; + drawlist->AddCallback(DpgMetalBindNearestPipeline, nullptr); +} + +void +LeaveNearestFilterScope(ImDrawList* drawlist) +{ + if (drawlist == nullptr) return; + drawlist->AddCallback(ImDrawCallback_ResetRenderState, nullptr); +} + +void +ApplyTextureFilter(ImTextureID, int) +{ + // No-op: the sampler is swapped per-draw via the callback above. } \ No newline at end of file diff --git a/src/mvUtilities_linux.cpp b/src/mvUtilities_linux.cpp index f1e0a02eb..b33660c20 100644 --- a/src/mvUtilities_linux.cpp +++ b/src/mvUtilities_linux.cpp @@ -92,8 +92,13 @@ OutputFrameBuffer(const char* filepath) free(data); } +static GLint FilterToGL(int filter) +{ + return (filter == mvTextureFilter_Nearest) ? GL_NEAREST : GL_LINEAR; +} + ImTextureID -LoadTextureFromArray(unsigned width, unsigned height, float* data) +LoadTextureFromArray(unsigned width, unsigned height, float* data, int filter) { // Create a OpenGL texture identifier @@ -102,8 +107,9 @@ LoadTextureFromArray(unsigned width, unsigned height, float* data) glBindTexture(GL_TEXTURE_2D, image_texture); // Setup filtering parameters for display - glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_LINEAR); - glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_LINEAR); + GLint glFilter = FilterToGL(filter); + glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, glFilter); + glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, glFilter); // Upload pixels into texture glPixelStorei(GL_UNPACK_ROW_LENGTH, 0); @@ -113,7 +119,7 @@ LoadTextureFromArray(unsigned width, unsigned height, float* data) } ImTextureID -LoadTextureFromArrayDynamic(unsigned width, unsigned height, float* data) +LoadTextureFromArrayDynamic(unsigned width, unsigned height, float* data, int filter) { // Create a OpenGL texture identifier @@ -122,8 +128,9 @@ LoadTextureFromArrayDynamic(unsigned width, unsigned height, float* data) glBindTexture(GL_TEXTURE_2D, image_texture); // Setup filtering parameters for display - glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_LINEAR); - glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_LINEAR); + GLint glFilter = FilterToGL(filter); + glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, glFilter); + glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, glFilter); // Upload pixels into texture glPixelStorei(GL_UNPACK_ROW_LENGTH, 0); @@ -140,7 +147,7 @@ LoadTextureFromArrayDynamic(unsigned width, unsigned height, float* data) } ImTextureID -LoadTextureFromArrayRaw(unsigned width, unsigned height, float* data, int components) +LoadTextureFromArrayRaw(unsigned width, unsigned height, float* data, int components, int filter) { // Create a OpenGL texture identifier @@ -149,8 +156,9 @@ LoadTextureFromArrayRaw(unsigned width, unsigned height, float* data, int compon glBindTexture(GL_TEXTURE_2D, image_texture); // Setup filtering parameters for display - glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_LINEAR); - glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_LINEAR); + GLint glFilter = FilterToGL(filter); + glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, glFilter); + glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, glFilter); // Upload pixels into texture glPixelStorei(GL_UNPACK_ROW_LENGTH, 0); @@ -207,6 +215,19 @@ UnloadTexture(const std::string& filename) return true; } +void EnterNearestFilterScope(ImDrawList*) {} +void LeaveNearestFilterScope(ImDrawList*) {} + +void ApplyTextureFilter(ImTextureID texture, int filter) +{ + GLuint tex = (GLuint)(uintptr_t)texture; + if (tex == 0) return; + glBindTexture(GL_TEXTURE_2D, tex); + GLint f = FilterToGL(filter); + glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, f); + glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, f); +} + void FreeTexture(ImTextureID texture) { diff --git a/src/mvUtilities_win32.cpp b/src/mvUtilities_win32.cpp index 989f1ff53..10a1537ad 100644 --- a/src/mvUtilities_win32.cpp +++ b/src/mvUtilities_win32.cpp @@ -5,6 +5,9 @@ #include "mvWindowsSpecifics.h" #include "mvCustomTypes.h" +#include "mvContext.h" + +#include #define STB_IMAGE_IMPLEMENTATION #include "stb_image.h" @@ -145,8 +148,9 @@ LoadTextureFromFile(const char* filename, int& width, int& height) } ImTextureID -LoadTextureFromArray(unsigned width, unsigned height, float* data) +LoadTextureFromArray(unsigned width, unsigned height, float* data, int filter) { + (void)filter; mvGraphics_D3D11* graphicsData = (mvGraphics_D3D11*)GContext->graphics.backendSpecifics; ID3D11ShaderResourceView* out_srv = nullptr; @@ -223,8 +227,9 @@ LoadTextureFromArray(unsigned width, unsigned height, int* data) } ImTextureID -LoadTextureFromArrayDynamic(unsigned width, unsigned height, float* data) +LoadTextureFromArrayDynamic(unsigned width, unsigned height, float* data, int filter) { + (void)filter; mvGraphics_D3D11* graphicsData = (mvGraphics_D3D11*)GContext->graphics.backendSpecifics; ID3D11ShaderResourceView* out_srv = nullptr; @@ -388,8 +393,9 @@ UpdateRawTexture(ImTextureID texture, unsigned width, unsigned height, float* da } ImTextureID -LoadTextureFromArrayRaw(unsigned width, unsigned height, float* data, int components) +LoadTextureFromArrayRaw(unsigned width, unsigned height, float* data, int components, int filter) { + (void)filter; mvGraphics_D3D11* graphicsData = (mvGraphics_D3D11*)GContext->graphics.backendSpecifics; ID3D11ShaderResourceView* out_srv = nullptr; @@ -453,4 +459,34 @@ UnloadTexture(const std::string& filename) { // TODO : decide if cleanup is necessary return true; +} + +static void +DpgD3D11BindNearestSampler(const ImDrawList* /*parent_list*/, const ImDrawCmd* /*cmd*/) +{ + auto* graphicsData = (mvGraphics_D3D11*)GContext->graphics.backendSpecifics; + if (graphicsData == nullptr || graphicsData->deviceContext == nullptr || + graphicsData->pTexSamplerNearest == nullptr) + return; + graphicsData->deviceContext->PSSetSamplers(0, 1, &graphicsData->pTexSamplerNearest); +} + +void +EnterNearestFilterScope(ImDrawList* drawlist) +{ + if (drawlist == nullptr) return; + drawlist->AddCallback(DpgD3D11BindNearestSampler, nullptr); +} + +void +LeaveNearestFilterScope(ImDrawList* drawlist) +{ + if (drawlist == nullptr) return; + drawlist->AddCallback(ImDrawCallback_ResetRenderState, nullptr); +} + +void +ApplyTextureFilter(ImTextureID, int) +{ + // No-op: the sampler is swapped per-draw via the callback above. } \ No newline at end of file diff --git a/src/mvViewport_apple.mm b/src/mvViewport_apple.mm index a448ae6c8..885037164 100644 --- a/src/mvViewport_apple.mm +++ b/src/mvViewport_apple.mm @@ -241,6 +241,7 @@ graphicsData->renderPassDescriptor.colorAttachments[0].loadAction = MTLLoadActionClear; graphicsData->renderPassDescriptor.colorAttachments[0].storeAction = MTLStoreActionStore; id renderEncoder = [commandBuffer renderCommandEncoderWithDescriptor:graphicsData->renderPassDescriptor]; + graphicsData->currentEncoder = renderEncoder; [renderEncoder pushDebugGroup:@"ImGui demo"]; @@ -269,6 +270,7 @@ [renderEncoder popDebugGroup]; [renderEncoder endEncoding]; + graphicsData->currentEncoder = nil; [commandBuffer presentDrawable:drawable]; [commandBuffer commit]; diff --git a/src/mvWindowsSpecifics.h b/src/mvWindowsSpecifics.h index 3d887ec1c..ab8f63d16 100644 --- a/src/mvWindowsSpecifics.h +++ b/src/mvWindowsSpecifics.h @@ -32,4 +32,5 @@ struct mvGraphics_D3D11 IDXGISwapChain* swapChain = nullptr; ID3D11RenderTargetView* target = nullptr; ID3D11Texture2D* backBuffer = nullptr; + ID3D11SamplerState* pTexSamplerNearest = nullptr; }; \ No newline at end of file