Skip to content

Commit

Permalink
implement ImageFromAsset for Metal
Browse files Browse the repository at this point in the history
  • Loading branch information
pthom committed Dec 30, 2023
1 parent c31bffc commit b6f53c7
Show file tree
Hide file tree
Showing 7 changed files with 155 additions and 7 deletions.
2 changes: 1 addition & 1 deletion src/hello_imgui/internal/backend_impls/rendering_metal.h
Original file line number Diff line number Diff line change
Expand Up @@ -27,6 +27,7 @@ namespace HelloImGui
id<MTLCommandQueue> mtlCommandQueue = nullptr;
MTLRenderPassDescriptor* mtlRenderPassDescriptor = nullptr;
id <MTLRenderCommandEncoder> mtlRenderCommandEncoder = nullptr;
id<MTLDevice> mtlDevice = nullptr;
};
MetalGlobals& GetMetalGlobals();

Expand Down Expand Up @@ -56,7 +57,6 @@ namespace HelloImGui
struct GlfwMetalGlobals
{
GLFWwindow* glfwWindow = nullptr;
id<MTLDevice> mtlDevice = nullptr;
};
GlfwMetalGlobals& GetGlfwMetalGlobals();
#endif
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -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;
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -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)
{
Expand Down
85 changes: 85 additions & 0 deletions src/hello_imgui/internal/image_from_asset_metal.cpp
Original file line number Diff line number Diff line change
@@ -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 <string>
#include <unordered_map>

namespace HelloImGui
{
static std::unordered_map<std::string, ImageMetalPtr> 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<ImageMetal>(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
5 changes: 3 additions & 2 deletions src/hello_imgui/internal/image_from_asset_none.cpp
Original file line number Diff line number Diff line change
@@ -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"

Expand Down Expand Up @@ -38,4 +38,5 @@ namespace HelloImGui
void Free_ImageFromAssetMap() {}
}
} // namespace HelloImGui
#endif // #if !defined(HELLOIMGUI_HAS_VULKAN) && !defined(HELLOIMGUI_HAS_OPENGL)

#endif
23 changes: 23 additions & 0 deletions src/hello_imgui/internal/image_metal.h
Original file line number Diff line number Diff line change
@@ -0,0 +1,23 @@
#pragma once
#ifdef HELLOIMGUI_HAS_METAL

#include <Metal/Metal.h>
#include <QuartzCore/CAMetalLayer.h>
#include <memory>

namespace HelloImGui
{
struct ImageMetal
{
ImageMetal(int width, int height, unsigned char* image_data_rgba);
~ImageMetal();

id<MTLTexture> Texture;
int Width;
int Height;
};

using ImageMetalPtr = std::shared_ptr<ImageMetal>;
}

#endif // #ifdef HELLOIMGUI_HAS_METAL
38 changes: 38 additions & 0 deletions src/hello_imgui/internal/image_metal.mm
Original file line number Diff line number Diff line change
@@ -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

0 comments on commit b6f53c7

Please sign in to comment.