diff --git a/src/hello_imgui/internal/backend_impls/rendering_metal.h b/src/hello_imgui/internal/backend_impls/rendering_metal.h index 4a5b9153..244a51b3 100644 --- a/src/hello_imgui/internal/backend_impls/rendering_metal.h +++ b/src/hello_imgui/internal/backend_impls/rendering_metal.h @@ -27,6 +27,7 @@ namespace HelloImGui id mtlCommandQueue = nullptr; MTLRenderPassDescriptor* mtlRenderPassDescriptor = nullptr; id mtlRenderCommandEncoder = nullptr; + id mtlDevice = nullptr; }; MetalGlobals& GetMetalGlobals(); @@ -56,7 +57,6 @@ namespace HelloImGui struct GlfwMetalGlobals { GLFWwindow* glfwWindow = nullptr; - id mtlDevice = nullptr; }; GlfwMetalGlobals& GetGlfwMetalGlobals(); #endif diff --git a/src/hello_imgui/internal/backend_impls/rendering_metal_glfw.mm b/src/hello_imgui/internal/backend_impls/rendering_metal_glfw.mm index 6ca985d3..933988dc 100644 --- a/src/hello_imgui/internal/backend_impls/rendering_metal_glfw.mm +++ b/src/hello_imgui/internal/backend_impls/rendering_metal_glfw.mm @@ -33,16 +33,16 @@ void PrepareGlfwForMetal(GLFWwindow* glfwWindow) auto& gGlfwMetalGlobals = GetGlfwMetalGlobals(); { gGlfwMetalGlobals.glfwWindow = glfwWindow; - gGlfwMetalGlobals.mtlDevice = MTLCreateSystemDefaultDevice(); - gMetalGlobals.mtlCommandQueue = [gGlfwMetalGlobals.mtlDevice newCommandQueue]; + gMetalGlobals.mtlDevice = MTLCreateSystemDefaultDevice(); + gMetalGlobals.mtlCommandQueue = [gMetalGlobals.mtlDevice newCommandQueue]; } { ImGui_ImplGlfw_InitForOther(gGlfwMetalGlobals.glfwWindow, true); - ImGui_ImplMetal_Init(gGlfwMetalGlobals.mtlDevice); + ImGui_ImplMetal_Init(gMetalGlobals.mtlDevice); NSWindow* nswin = glfwGetCocoaWindow(gGlfwMetalGlobals.glfwWindow); gMetalGlobals.caMetalLayer = [CAMetalLayer layer]; - gMetalGlobals.caMetalLayer.device = gGlfwMetalGlobals.mtlDevice; + gMetalGlobals.caMetalLayer.device = gMetalGlobals.mtlDevice; gMetalGlobals.caMetalLayer.pixelFormat = MTLPixelFormatBGRA8Unorm; nswin.contentView.layer = gMetalGlobals.caMetalLayer; nswin.contentView.wantsLayer = YES; diff --git a/src/hello_imgui/internal/backend_impls/rendering_metal_sdl.mm b/src/hello_imgui/internal/backend_impls/rendering_metal_sdl.mm index 2a3e351f..435b65b8 100644 --- a/src/hello_imgui/internal/backend_impls/rendering_metal_sdl.mm +++ b/src/hello_imgui/internal/backend_impls/rendering_metal_sdl.mm @@ -30,6 +30,7 @@ void PrepareSdlForMetal(SDL_Window* sdlWindow) { gSdlMetalGlobals.sdlWindow = sdlWindow; + gMetalGlobals.mtlDevice = MTLCreateSystemDefaultDevice(); gSdlMetalGlobals.sdlRenderer = SDL_CreateRenderer(sdlWindow, -1, SDL_RENDERER_ACCELERATED | SDL_RENDERER_PRESENTVSYNC); if (gSdlMetalGlobals.sdlRenderer == nullptr) { diff --git a/src/hello_imgui/internal/image_from_asset_metal.cpp b/src/hello_imgui/internal/image_from_asset_metal.cpp new file mode 100644 index 00000000..2b1afb80 --- /dev/null +++ b/src/hello_imgui/internal/image_from_asset_metal.cpp @@ -0,0 +1,85 @@ +#ifdef HELLOIMGUI_HAS_METAL +#include "hello_imgui/image_from_asset.h" +#include "imgui.h" +#include "hello_imgui/internal/image_metal.h" +#include "hello_imgui/internal/stb_image.h" +#include "hello_imgui/hello_imgui_assets.h" + +#include +#include + +namespace HelloImGui +{ + static std::unordered_map gImageFromAssetMap; + + + static ImageMetalPtr _GetCachedImageMetal(const char*assetPath) + { + if (gImageFromAssetMap.find(assetPath) != gImageFromAssetMap.end()) + return gImageFromAssetMap.at(assetPath); + + // Load the image using stbi_load_from_memory + auto assetData = LoadAssetFileData(assetPath); + assert(assetData.data != nullptr); + int width, height; + unsigned char*image_data_rgba = stbi_load_from_memory( + (unsigned char *)assetData.data, (int)assetData.dataSize, + &width, &height, NULL, 4); + if (image_data_rgba == NULL) + { + IM_ASSERT(false && "ImageMetal: Failed to load image!"); + return nullptr; + } + + // Create and store the VkImage + if (gImageFromAssetMap.find(assetPath) == gImageFromAssetMap.end()) + gImageFromAssetMap[assetPath] = std::make_shared(width, height, image_data_rgba); + + // Release image memory using stb + stbi_image_free(image_data_rgba); + + return gImageFromAssetMap.at(assetPath); + } + + void ImageFromAsset( + const char *assetPath, const ImVec2& size, + const ImVec2& uv0, const ImVec2& uv1, + const ImVec4& tint_col, const ImVec4& border_col) + { + auto textureId = ImTextureIdFromAsset(assetPath); + auto imageSize = ImageSizeFromAsset(assetPath); + ImVec2 displayedSize = ImageProportionalSize(size, imageSize); + ImGui::Image(textureId, displayedSize, uv0, uv1, tint_col, border_col); + } + + bool ImageButtonFromAsset(const char *assetPath, const ImVec2& size, const ImVec2& uv0, const ImVec2& uv1, int frame_padding, const ImVec4& bg_col, const ImVec4& tint_col) + { + auto textureId = ImTextureIdFromAsset(assetPath); + auto imageSize = ImageSizeFromAsset(assetPath); + ImVec2 displayedSize = ImageProportionalSize(size, imageSize); + bool clicked = ImGui::ImageButton(textureId, displayedSize, uv0, uv1, frame_padding, bg_col, tint_col); + return clicked; + } + + ImTextureID ImTextureIdFromAsset(const char *assetPath) + { + auto cachedImage = _GetCachedImageMetal(assetPath); + return (ImTextureID) cachedImage->Texture; + } + + ImVec2 ImageSizeFromAsset(const char *assetPath) + { + auto cachedImage = _GetCachedImageMetal(assetPath); + return ImVec2((float)cachedImage->Width, (float)cachedImage->Height); + } + + namespace internal + { + void Free_ImageFromAssetMap() + { + gImageFromAssetMap.clear(); + } + } + +} // namespace HelloImGui +#endif // #ifdef HELLOIMGUI_HAS_METAL diff --git a/src/hello_imgui/internal/image_from_asset_none.cpp b/src/hello_imgui/internal/image_from_asset_none.cpp index df03964a..37f656f3 100644 --- a/src/hello_imgui/internal/image_from_asset_none.cpp +++ b/src/hello_imgui/internal/image_from_asset_none.cpp @@ -1,7 +1,7 @@ #include "image_from_asset.h" -#if !defined(HELLOIMGUI_HAS_VULKAN) && !defined(HELLOIMGUI_HAS_OPENGL) +#if !defined(HELLOIMGUI_HAS_VULKAN) && !defined(HELLOIMGUI_HAS_OPENGL) && !defined(HELLOIMGUI_HAS_METAL) #include "imgui.h" @@ -38,4 +38,5 @@ namespace HelloImGui void Free_ImageFromAssetMap() {} } } // namespace HelloImGui -#endif // #if !defined(HELLOIMGUI_HAS_VULKAN) && !defined(HELLOIMGUI_HAS_OPENGL) + +#endif diff --git a/src/hello_imgui/internal/image_metal.h b/src/hello_imgui/internal/image_metal.h new file mode 100644 index 00000000..2ec2922a --- /dev/null +++ b/src/hello_imgui/internal/image_metal.h @@ -0,0 +1,23 @@ +#pragma once +#ifdef HELLOIMGUI_HAS_METAL + +#include +#include +#include + +namespace HelloImGui +{ + struct ImageMetal + { + ImageMetal(int width, int height, unsigned char* image_data_rgba); + ~ImageMetal(); + + id Texture; + int Width; + int Height; + }; + + using ImageMetalPtr = std::shared_ptr; +} + +#endif // #ifdef HELLOIMGUI_HAS_METAL diff --git a/src/hello_imgui/internal/image_metal.mm b/src/hello_imgui/internal/image_metal.mm new file mode 100644 index 00000000..a6d3c27f --- /dev/null +++ b/src/hello_imgui/internal/image_metal.mm @@ -0,0 +1,38 @@ +#ifdef HELLOIMGUI_HAS_METAL +#include "image_metal.h" + +#include "imgui.h" +#include "hello_imgui/internal/backend_impls/rendering_metal.h" + +namespace HelloImGui +{ + + ImageMetal::ImageMetal(int width, int height, unsigned char* image_data_rgba) + : Width(width), Height(height) + { + auto gMetalGlobals = GetMetalGlobals(); + + // Create a MTLTextureDescriptor + MTLTextureDescriptor* textureDescriptor = [[MTLTextureDescriptor alloc] init]; + textureDescriptor.pixelFormat = MTLPixelFormatRGBA8Unorm; // Adjust as needed + textureDescriptor.width = width; + textureDescriptor.height = height; + textureDescriptor.usage = MTLTextureUsageShaderRead | MTLTextureUsageRenderTarget; + + // Create the texture from the descriptor + Texture = [gMetalGlobals.mtlDevice newTextureWithDescriptor:textureDescriptor]; + + // Upload the image data to the texture + MTLRegion region = MTLRegionMake2D(0, 0, width, height); + [Texture replaceRegion:region mipmapLevel:0 withBytes:image_data_rgba bytesPerRow:4 * width]; + + [textureDescriptor release]; + } + + ImageMetal::~ImageMetal() + { + [Texture release]; + } +} + +#endif // #ifdef HELLOIMGUI_HAS_VULKAN