diff --git a/CMakeLists.txt b/CMakeLists.txt index a5e91fb00..18ec2d0ef 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -65,6 +65,11 @@ if (WIN32) else() set(WITH_RENDER_DX11_DEFAULT OFF) endif() +if (APPLE) + set(WITH_RENDER_METAL_DEFAULT ON) +else() + set(WITH_RENDER_METAL_DEFAULT OFF) +endif() if (ANDROID) set(USE_GLES_DEFAULT ON) @@ -81,6 +86,7 @@ option(WITH_JACK "Whether to build with JACK support. Auto-detects if JACK is av option(WITH_RENDER_SDL "Whether to build with the SDL_Renderer render backend." ${WITH_RENDER_SDL_DEFAULT}) option(WITH_RENDER_OPENGL "Whether to build with the OpenGL render backend." ${WITH_RENDER_OPENGL_DEFAULT}) option(WITH_RENDER_DX11 "Whether to build with the DirectX 11 render backend." ${WITH_RENDER_DX11_DEFAULT}) +option(WITH_RENDER_METAL "Whether to build with the Metal render backend." ${WITH_RENDER_METAL_DEFAULT}) option(USE_GLES "Use OpenGL ES for the OpenGL render backend." ${USE_GLES_DEFAULT}) option(SYSTEM_FFTW "Use a system-installed version of FFTW instead of the vendored one" OFF) option(SYSTEM_FMT "Use a system-installed version of fmt instead of the vendored one" OFF) @@ -308,7 +314,7 @@ else() endif() if (BUILD_GUI) - if (NOT WITH_RENDER_SDL AND NOT WITH_RENDER_OPENGL AND NOT WITH_RENDER_DX11) + if (NOT WITH_RENDER_SDL AND NOT WITH_RENDER_OPENGL AND NOT WITH_RENDER_DX11 AND NOT WITH_RENDER_METAL) message(FATAL_ERROR "No render backends selected!") endif() endif() @@ -753,6 +759,19 @@ if (WITH_RENDER_DX11) endif() endif() +if (WITH_RENDER_METAL) + if (APPLE) + list(APPEND GUI_SOURCES src/gui/render/renderMetal.cpp) + list(APPEND GUI_SOURCES extern/imgui_patched/backends/imgui_impl_metal.cpp) + list(APPEND DEPENDENCIES_DEFINES HAVE_RENDER_METAL) + # TODO: what is being used? + #list(APPEND DEPENDENCIES_LIBRARIES d3d11) + message(STATUS "UI render backend: Metal") + else() + message(FATAL_ERROR "Metal render backend only for Apple operating systems!") + endif() +endif() + if (NOT WIN32 AND NOT APPLE) CHECK_INCLUDE_FILE(sys/io.h SYS_IO_FOUND) CHECK_INCLUDE_FILE(linux/input.h LINUX_INPUT_FOUND) diff --git a/src/gui/gui.h b/src/gui/gui.h index a8befff29..f53b1a8d4 100644 --- a/src/gui/gui.h +++ b/src/gui/gui.h @@ -72,7 +72,8 @@ enum FurnaceGUIRenderBackend { GUI_BACKEND_SDL=0, GUI_BACKEND_GL, - GUI_BACKEND_DX11 + GUI_BACKEND_DX11, + GUI_BACKEND_METAL }; #ifdef HAVE_RENDER_SDL @@ -83,10 +84,15 @@ enum FurnaceGUIRenderBackend { #define GUI_BACKEND_DEFAULT GUI_BACKEND_DX11 #define GUI_BACKEND_DEFAULT_NAME "DirectX 11" #else +#ifdef HAVE_RENDER_METAL +#define GUI_BACKEND_DEFAULT GUI_BACKEND_METAL +#define GUI_BACKEND_DEFAULT_NAME "Metal" +#else #define GUI_BACKEND_DEFAULT GUI_BACKEND_GL #define GUI_BACKEND_DEFAULT_NAME "OpenGL" #endif #endif +#endif // TODO: // - add colors for FM envelope and waveform diff --git a/src/gui/render.cpp b/src/gui/render.cpp index 5ad753c23..6ed2430e4 100644 --- a/src/gui/render.cpp +++ b/src/gui/render.cpp @@ -28,6 +28,9 @@ #ifdef HAVE_RENDER_DX11 #include "render/renderDX11.h" #endif +#ifdef HAVE_RENDER_METAL +#include "render/renderMetal.h" +#endif bool FurnaceGUI::initRender() { if (rend!=NULL) return false; @@ -36,6 +39,8 @@ bool FurnaceGUI::initRender() { renderBackend=GUI_BACKEND_GL; } else if (settings.renderBackend=="DirectX 11") { renderBackend=GUI_BACKEND_DX11; + } else if (settings.renderBackend=="Metal") { + renderBackend=GUI_BACKEND_METAL; } else if (settings.renderBackend=="SDL") { renderBackend=GUI_BACKEND_SDL; } else { @@ -55,6 +60,12 @@ bool FurnaceGUI::initRender() { rend=new FurnaceGUIRenderDX11; break; #endif +#ifdef HAVE_RENDER_METAL + case GUI_BACKEND_METAL: + logI("render backend: Metal"); + rend=new FurnaceGUIRenderMetal; + break; +#endif #ifdef HAVE_RENDER_SDL case GUI_BACKEND_SDL: logI("render backend: SDL_Renderer"); diff --git a/src/gui/render/renderMetal.h b/src/gui/render/renderMetal.h new file mode 100644 index 000000000..f19135841 --- /dev/null +++ b/src/gui/render/renderMetal.h @@ -0,0 +1,49 @@ +/** + * Furnace Tracker - multi-system chiptune tracker + * Copyright (C) 2021-2023 tildearrow and contributors + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License along + * with this program; if not, write to the Free Software Foundation, Inc., + * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. + */ + +#include "../gui.h" + +class FurnaceGUIRenderSDL: public FurnaceGUIRender { + SDL_Renderer* sdlRend; + public: + ImTextureID getTextureID(FurnaceGUITexture* which); + bool lockTexture(FurnaceGUITexture* which, void** data, int* pitch); + bool unlockTexture(FurnaceGUITexture* which); + bool updateTexture(FurnaceGUITexture* which, void* data, int pitch); + FurnaceGUITexture* createTexture(bool dynamic, int width, int height); + bool destroyTexture(FurnaceGUITexture* which); + void setTextureBlendMode(FurnaceGUITexture* which, FurnaceGUIBlendMode mode); + void setBlendMode(FurnaceGUIBlendMode mode); + void clear(ImVec4 color); + bool newFrame(); + void createFontsTexture(); + void destroyFontsTexture(); + void renderGUI(); + void wipe(float alpha); + void present(); + bool getOutputSize(int& w, int& h); + int getWindowFlags(); + void preInit(); + bool init(SDL_Window* win); + void initGUI(SDL_Window* win); + void quitGUI(); + bool quit(); + FurnaceGUIRenderSDL(): + sdlRend(NULL) {} +}; \ No newline at end of file diff --git a/src/gui/render/renderMetal.mm b/src/gui/render/renderMetal.mm new file mode 100644 index 000000000..84053d057 --- /dev/null +++ b/src/gui/render/renderMetal.mm @@ -0,0 +1,171 @@ +/** + * Furnace Tracker - multi-system chiptune tracker + * Copyright (C) 2021-2023 tildearrow and contributors + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License along + * with this program; if not, write to the Free Software Foundation, Inc., + * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. + */ + +// TODO: everything + +#include "renderMetal.h" +#include "backends/imgui_impl_metal.h" +#include +#include + +class FurnaceMetalTexture: public FurnaceGUITexture { + public: + MTLTexture* tex; + FurnaceMetalTexture(): + tex(NULL) {} +}; + +ImTextureID FurnaceGUIRenderMetal::getTextureID(FurnaceGUITexture* which) { + FurnaceSDLTexture* t=(FurnaceSDLTexture*)which; + return t->tex; +} + +bool FurnaceGUIRenderMetal::lockTexture(FurnaceGUITexture* which, void** data, int* pitch) { + FurnaceSDLTexture* t=(FurnaceSDLTexture*)which; + return SDL_LockTexture(t->tex,NULL,data,pitch)==0; +} + +bool FurnaceGUIRenderMetal::unlockTexture(FurnaceGUITexture* which) { + FurnaceSDLTexture* t=(FurnaceSDLTexture*)which; + SDL_UnlockTexture(t->tex); + return true; +} + +bool FurnaceGUIRenderMetal::updateTexture(FurnaceGUITexture* which, void* data, int pitch) { + FurnaceSDLTexture* t=(FurnaceSDLTexture*)which; + return SDL_UpdateTexture(t->tex,NULL,data,pitch)==0; +} + +FurnaceGUITexture* FurnaceGUIRenderMetal::createTexture(bool dynamic, int width, int height) { + SDL_Texture* t=SDL_CreateTexture(sdlRend,SDL_PIXELFORMAT_ABGR8888,dynamic?SDL_TEXTUREACCESS_STREAMING:SDL_TEXTUREACCESS_STATIC,width,height); + + if (t==NULL) return NULL; + FurnaceSDLTexture* ret=new FurnaceSDLTexture; + ret->tex=t; + return ret; +} + +bool FurnaceGUIRenderMetal::destroyTexture(FurnaceGUITexture* which) { + FurnaceSDLTexture* t=(FurnaceSDLTexture*)which; + + SDL_DestroyTexture(t->tex); + delete t; + return true; +} + +void FurnaceGUIRenderMetal::setTextureBlendMode(FurnaceGUITexture* which, FurnaceGUIBlendMode mode) { + FurnaceSDLTexture* t=(FurnaceSDLTexture*)which; + switch (mode) { + case GUI_BLEND_MODE_NONE: + SDL_SetTextureBlendMode(t->tex,SDL_BLENDMODE_NONE); + break; + case GUI_BLEND_MODE_BLEND: + SDL_SetTextureBlendMode(t->tex,SDL_BLENDMODE_BLEND); + break; + case GUI_BLEND_MODE_ADD: + SDL_SetTextureBlendMode(t->tex,SDL_BLENDMODE_ADD); + break; + case GUI_BLEND_MODE_MULTIPLY: + SDL_SetTextureBlendMode(t->tex,SDL_BLENDMODE_MOD); + break; + } +} + +void FurnaceGUIRenderMetal::setBlendMode(FurnaceGUIBlendMode mode) { + switch (mode) { + case GUI_BLEND_MODE_NONE: + SDL_SetRenderDrawBlendMode(sdlRend,SDL_BLENDMODE_NONE); + break; + case GUI_BLEND_MODE_BLEND: + SDL_SetRenderDrawBlendMode(sdlRend,SDL_BLENDMODE_BLEND); + break; + case GUI_BLEND_MODE_ADD: + SDL_SetRenderDrawBlendMode(sdlRend,SDL_BLENDMODE_ADD); + break; + case GUI_BLEND_MODE_MULTIPLY: + SDL_SetRenderDrawBlendMode(sdlRend,SDL_BLENDMODE_MOD); + break; + } +} + +void FurnaceGUIRenderMetal::clear(ImVec4 color) { + SDL_SetRenderDrawColor(sdlRend,color.x*255,color.y*255,color.z*255,color.w*255); + SDL_RenderClear(sdlRend); +} + +bool FurnaceGUIRenderMetal::newFrame() { + return ImGui_ImplSDLRenderer2_NewFrame(); +} + +void FurnaceGUIRenderMetal::createFontsTexture() { + ImGui_ImplSDLRenderer2_CreateFontsTexture(); +} + +void FurnaceGUIRenderMetal::destroyFontsTexture() { + ImGui_ImplSDLRenderer2_DestroyFontsTexture(); +} + +void FurnaceGUIRenderMetal::renderGUI() { + ImGui_ImplSDLRenderer2_RenderDrawData(ImGui::GetDrawData()); +} + +void FurnaceGUIRenderMetal::wipe(float alpha) { + SDL_SetRenderDrawBlendMode(sdlRend,SDL_BLENDMODE_BLEND); + SDL_SetRenderDrawColor(sdlRend,0,0,0,255*alpha); + SDL_RenderFillRect(sdlRend,NULL); +} + +void FurnaceGUIRenderMetal::present() { + SDL_RenderPresent(sdlRend); +} + +bool FurnaceGUIRenderMetal::getOutputSize(int& w, int& h) { + return SDL_GetRendererOutputSize(sdlRend,&w,&h)==0; +} + +int FurnaceGUIRenderMetal::getWindowFlags() { + return 0; +} + +void FurnaceGUIRenderMetal::preInit() { + SDL_SetHint(SDL_HINT_RENDER_DRIVER,"metal"); +} + +bool FurnaceGUIRenderMetal::init(SDL_Window* win) { + SDL_SetHint(SDL_HINT_RENDER_DRIVER,"metal"); + + sdlRend=SDL_CreateRenderer(win,-1,SDL_RENDERER_ACCELERATED|SDL_RENDERER_PRESENTVSYNC|SDL_RENDERER_TARGETTEXTURE); + return (sdlRend!=NULL); +} + +void FurnaceGUIRenderMetal::initGUI(SDL_Window* win) { + ImGui_ImplSDL2_InitForSDLRenderer(win,sdlRend); + ImGui_ImplSDLRenderer2_Init(sdlRend); +} + +void FurnaceGUIRenderMetal::quitGUI() { + ImGui_ImplSDLRenderer2_Shutdown(); +} + +bool FurnaceGUIRenderMetal::quit() { + if (sdlRend==NULL) return false; + SDL_DestroyRenderer(sdlRend); + sdlRend=NULL; + return true; +} diff --git a/src/gui/settings.cpp b/src/gui/settings.cpp index 882bc8075..2e1861eb7 100644 --- a/src/gui/settings.cpp +++ b/src/gui/settings.cpp @@ -1316,6 +1316,11 @@ void FurnaceGUI::drawSettings() { settings.renderBackend="DirectX 11"; } #endif +#ifdef HAVE_RENDER_METAL + if (ImGui::Selectable("Metal",curRenderBackend=="Metal")) { + settings.renderBackend="Metal"; + } +#endif #ifdef HAVE_RENDER_GL if (ImGui::Selectable("OpenGL",curRenderBackend=="OpenGL")) { settings.renderBackend="OpenGL";