7 Commits

Author SHA1 Message Date
ebdd35b1e1 1.1 2022-07-22 01:49:30 +02:00
b986bfde39 fix missing tray icon bug 2022-07-22 01:48:26 +02:00
ecfb2f206d 1.0 2022-07-21 17:48:52 +02:00
1ab7fd1129 more cleanup 2022-07-21 17:39:25 +02:00
d5d4521f58 autostart + more cleanup 2022-07-21 17:29:43 +02:00
27be3fb0d4 cleanup 2022-07-21 16:18:54 +02:00
df939555b9 load settings on start 2022-07-21 15:56:44 +02:00
16 changed files with 863 additions and 619 deletions

View File

@@ -39,14 +39,16 @@ public:
class ApplicationSettings { class ApplicationSettings {
public: public:
bool autostart = false;
bool docked = false; bool docked = false;
bool showDisabledDevices = false; bool showDisabledDevices = false;
bool fitWindowHeight = true;
bool minimizeToTray = true;
}; };
class ApplicationData { class ApplicationData {
public: public:
ApplicationSettings settings = {}; ApplicationSettings settings = {};
std::shared_ptr<AudioData> audioData = std::make_shared<AudioData>(); std::shared_ptr<AudioData> audioData = std::make_shared<AudioData>();
ApplicationData(const ApplicationData&) = delete;
ApplicationData& operator=(const ApplicationData&) = delete;
}; };

View File

@@ -10,6 +10,7 @@
#include <windows.h> #include <windows.h>
#include <functional>
#include <iostream> #include <iostream>
#include <sstream> #include <sstream>
#include <shellapi.h> #include <shellapi.h>
@@ -18,50 +19,36 @@
#include "Util.h" #include "Util.h"
#include "AudioApi.h" #include "AudioApi.h"
#include "Settings.h"
#include "resource.h" #include "resource.h"
#include "AsuroTool.h" #include "AsuroTool.h"
class __declspec(uuid("3bc52579-15fd-43bb-9686-6273c238535e")) TrayGUID; const UINT TRAY_ID = 420;
const UINT WMAPP_NOTIFYCALLBACK = WM_APP + 1;
UINT const WMAPP_NOTIFYCALLBACK = WM_APP + 1; // Globals for use in callbacks
UINT const WMAPP_HIDEFLYOUT = WM_APP + 2; DrawData* gDrawData;
ApplicationData* gAppData;
DrawData* hackyDrawData; bool justDocked = false;
ApplicationData* hackyAppData; bool isHidden = false;
int main() int main()
{ {
ApplicationData applicationData{}; ApplicationData applicationData{};
ImGuiCallbacks callbacks{};
startImgui<ApplicationData>(applicationData, init, draw, cleanup, "Asuro's Tool", 600, 400); callbacks.initFunc = std::bind(init, std::placeholders::_1, std::ref(applicationData));
} callbacks.drawFunc = std::bind(draw, std::placeholders::_1, std::ref(applicationData));
callbacks.cleanupFunc = std::bind(cleanup, std::placeholders::_1, std::ref(applicationData));
LRESULT CALLBACK trayIconEventHandler(int code, WPARAM wParam, LPARAM lParam) startImgui(callbacks, "Audio Thingy", 600, 400);
{
if (code >= 0)
{
CWPSTRUCT* data = (CWPSTRUCT*)lParam;
if (data->message == WMAPP_NOTIFYCALLBACK)
{
switch (data->lParam)
{
case NIN_SELECT:
glfwShowWindow(hackyDrawData->window);
glfwRestoreWindow(hackyDrawData->window);
break;
}
}
}
return CallNextHookEx(NULL, code, wParam, lParam);
} }
void init(DrawData& drawData, ApplicationData& appData) void init(DrawData& drawData, ApplicationData& appData)
{ {
// sad :( // sad :(
hackyDrawData = &drawData; gDrawData = &drawData;
hackyAppData = &appData; gAppData = &appData;
// Load text font // Load text font
ImGuiIO& io = ImGui::GetIO(); ImGuiIO& io = ImGui::GetIO();
@@ -74,20 +61,6 @@ void init(DrawData& drawData, ApplicationData& appData)
icons_config.PixelSnapH = true; icons_config.PixelSnapH = true;
io.Fonts->AddFontFromFileTTF("remixicon.ttf", 14.0f, &icons_config, icons_ranges); io.Fonts->AddFontFromFileTTF("remixicon.ttf", 14.0f, &icons_config, icons_ranges);
// Set up audio device api
HRESULT audioResult;
audioResult = CoInitializeEx(NULL, COINIT_MULTITHREADED);
isError(audioResult, "Failed to initialize COM: ");
audioResult = CoCreateInstance(__uuidof(MMDeviceEnumerator), NULL, CLSCTX_INPROC_SERVER, IID_PPV_ARGS(&appData.audioData->deviceEnumerator));
isError(audioResult, "Failed to set up audio device enumerator: ");
appData.audioData->audioNotificationListener = new AudioNotificationListener(appData.audioData);
audioResult = appData.audioData->deviceEnumerator->RegisterEndpointNotificationCallback(appData.audioData->audioNotificationListener);
isError(audioResult, "Failed to register audio notification listener: ");
reloadDeviceLists(*appData.audioData);
// Set window icon // Set window icon
HINSTANCE instance = GetModuleHandle(NULL); HINSTANCE instance = GetModuleHandle(NULL);
LPWSTR iconId = MAKEINTRESOURCE(IDI_ICON1); LPWSTR iconId = MAKEINTRESOURCE(IDI_ICON1);
@@ -100,8 +73,8 @@ void init(DrawData& drawData, ApplicationData& appData)
// Set tray icon // Set tray icon
NOTIFYICONDATA nid = { sizeof(nid) }; NOTIFYICONDATA nid = { sizeof(nid) };
nid.hWnd = drawData.window_handle; nid.hWnd = drawData.window_handle;
nid.uFlags = NIF_ICON | NIF_TIP | NIF_MESSAGE | NIF_SHOWTIP | NIF_GUID; nid.uFlags = NIF_ICON | NIF_TIP | NIF_MESSAGE | NIF_SHOWTIP;
nid.guidItem = __uuidof(TrayGUID); nid.uID = TRAY_ID;
nid.uCallbackMessage = WMAPP_NOTIFYCALLBACK; nid.uCallbackMessage = WMAPP_NOTIFYCALLBACK;
HRESULT iconResult; HRESULT iconResult;
@@ -123,15 +96,38 @@ void init(DrawData& drawData, ApplicationData& appData)
// Set window minimize behavior // Set window minimize behavior
glfwSetWindowIconifyCallback(drawData.window, [](GLFWwindow* window, int isIconified) { glfwSetWindowIconifyCallback(drawData.window, [](GLFWwindow* window, int isIconified) {
if (isIconified == GLFW_TRUE && hackyAppData->settings.minimizeToTray) if (isIconified && gAppData->settings.docked)
{ {
glfwHideWindow(window); glfwHideWindow(window);
} }
isHidden = isIconified;
}); });
glfwSetWindowFocusCallback(drawData.window, [](GLFWwindow* window, int isFocused) {
if (!isFocused && gAppData->settings.docked && !justDocked)
{
glfwIconifyWindow(window);
}
});
// Load settings
initSettings(drawData, appData);
// Set up audio device api
initAudio(appData);
} }
void draw(DrawData& drawData, ApplicationData& appData) void draw(DrawData& drawData, ApplicationData& appData)
{ {
justDocked = false;
// Actual Drawing
if (isHidden)
{
glfwWaitEvents();
return;
}
float customYCursor = 0; float customYCursor = 0;
ImVec2 viewportSize = ImGui::GetMainViewport()->Size; ImVec2 viewportSize = ImGui::GetMainViewport()->Size;
@@ -151,10 +147,7 @@ void draw(DrawData& drawData, ApplicationData& appData)
customYCursor += audioDeviceWindow(appData, appData.audioData->recordingDevices, " \xEE\xBD\x8F Recording").y; customYCursor += audioDeviceWindow(appData, appData.audioData->recordingDevices, " \xEE\xBD\x8F Recording").y;
// Resize viewport // Resize viewport
if (appData.settings.fitWindowHeight)
{
drawData.window_size.y = customYCursor; drawData.window_size.y = customYCursor;
}
if (appData.settings.docked) if (appData.settings.docked)
{ {
@@ -168,29 +161,33 @@ void cleanup(DrawData& drawData, ApplicationData& appData)
{ {
// Remove tray icon // Remove tray icon
NOTIFYICONDATA nid = { sizeof(nid) }; NOTIFYICONDATA nid = { sizeof(nid) };
nid.uFlags = NIF_GUID; nid.hWnd = drawData.window_handle;
nid.guidItem = __uuidof(TrayGUID); nid.uID = TRAY_ID;
bool test = Shell_NotifyIcon(NIM_DELETE, &nid); Shell_NotifyIcon(NIM_DELETE, &nid);
} }
ImVec2 menuBar(DrawData& drawData, ApplicationData& appData) ImVec2 menuBar(DrawData& drawData, ApplicationData& appData)
{ {
ImVec2 size{}; ImVec2 size{};
bool closeMenu = false;
if (ImGui::BeginMainMenuBar()) if (ImGui::BeginMainMenuBar())
{ {
if (ImGui::BeginMenu("Settings")) if (ImGui::BeginMenu("Settings"))
{ {
bool prevDocked = appData.settings.docked; if (ImGui::Checkbox("Docked", &appData.settings.docked))
ImGui::Checkbox("Docked", &appData.settings.docked);
if (appData.settings.docked != prevDocked)
{ {
glfwSetWindowAttrib(drawData.window, GLFW_DECORATED, !appData.settings.docked); closeMenu = true;
updateDocked(drawData, appData);
} }
ImGui::Checkbox("Show Disabled Devices", &appData.settings.showDisabledDevices); ImGui::Checkbox("Show Disabled Devices", &appData.settings.showDisabledDevices);
ImGui::Checkbox("Fit Window Height", &appData.settings.fitWindowHeight);
ImGui::Checkbox("Minimize To Tray", &appData.settings.minimizeToTray); if (ImGui::Checkbox("Autostart", &appData.settings.autostart))
{
setAutostart(appData.settings.autostart);
}
ImGui::EndMenu(); ImGui::EndMenu();
} }
@@ -223,6 +220,11 @@ ImVec2 menuBar(DrawData& drawData, ApplicationData& appData)
ImGui::EndMainMenuBar(); ImGui::EndMainMenuBar();
} }
if (closeMenu)
{
ImGui::SetWindowFocus();
}
return size; return size;
} }
@@ -296,7 +298,7 @@ ImVec2 audioDeviceWindow(ApplicationData& appData, std::vector<AudioDevice>& dev
const float linePaddingX = 3.; const float linePaddingX = 3.;
cursorPos.x += linePaddingX; cursorPos.x += linePaddingX;
drawList->AddLine(ImVec2(cursorPos.x, lineY), ImVec2(cursorPos.x + (space.x - 2. * linePaddingX) , lineY), IM_COL32(120, 120, 120, 255), 2.); drawList->AddLine(ImVec2(cursorPos.x, lineY), ImVec2(cursorPos.x + (space.x - 2. * linePaddingX), lineY), IM_COL32(120, 120, 120, 255), 2.);
drawList->AddLine(ImVec2(cursorPos.x, lineY), ImVec2(cursorPos.x + (space.x - 2. * linePaddingX) * meterValue, lineY), IM_COL32(200, 200, 255, 255), 3.); drawList->AddLine(ImVec2(cursorPos.x, lineY), ImVec2(cursorPos.x + (space.x - 2. * linePaddingX) * meterValue, lineY), IM_COL32(200, 200, 255, 255), 3.);
float volume = getVolume(dev.volumeInterface); float volume = getVolume(dev.volumeInterface);
@@ -359,3 +361,25 @@ void drawCircle(float radius, ImU32 color)
ImVec2 windowPos = ImGui::GetWindowPos(); ImVec2 windowPos = ImGui::GetWindowPos();
drawList->AddCircleFilled(ImVec2(cursorPos.x + windowPos.x, cursorPos.y + windowPos.y + ImGui::GetTextLineHeight() / 2.), radius, color); drawList->AddCircleFilled(ImVec2(cursorPos.x + windowPos.x, cursorPos.y + windowPos.y + ImGui::GetTextLineHeight() / 2.), radius, color);
} }
LRESULT CALLBACK trayIconEventHandler(int code, WPARAM wParam, LPARAM lParam)
{
if (code >= 0)
{
CWPSTRUCT* data = (CWPSTRUCT*)lParam;
if (data->message == WMAPP_NOTIFYCALLBACK)
{
auto id = HIWORD(data->lParam);
auto trayEvent = LOWORD(data->lParam);
if (id == TRAY_ID && trayEvent == WM_LBUTTONUP)
{
glfwShowWindow(gDrawData->window);
glfwRestoreWindow(gDrawData->window);
}
}
}
return CallNextHookEx(NULL, code, wParam, lParam);
}

View File

@@ -8,6 +8,10 @@
void init(DrawData& drawData, ApplicationData& customData); void init(DrawData& drawData, ApplicationData& customData);
void draw(DrawData& drawData, ApplicationData& customData); void draw(DrawData& drawData, ApplicationData& customData);
void cleanup(DrawData& drawData, ApplicationData& appData); void cleanup(DrawData& drawData, ApplicationData& appData);
ImVec2 menuBar(DrawData& drawData, ApplicationData& appData); ImVec2 menuBar(DrawData& drawData, ApplicationData& appData);
ImVec2 audioDeviceWindow(ApplicationData& appData, std::vector<AudioDevice>& deviceList, const char* title); ImVec2 audioDeviceWindow(ApplicationData& appData, std::vector<AudioDevice>& deviceList, const char* title);
void drawCircle(float radius, ImU32 color); void drawCircle(float radius, ImU32 color);
void GenerateTrayUUID(UUID* uuid);
LRESULT trayIconEventHandler(int code, WPARAM wParam, LPARAM lParam);

View File

@@ -65,6 +65,45 @@ LANGUAGE LANG_ENGLISH, SUBLANG_ENGLISH_UK
IDI_ICON1 ICON "kaiju.ico" IDI_ICON1 ICON "kaiju.ico"
/////////////////////////////////////////////////////////////////////////////
//
// Version
//
VS_VERSION_INFO VERSIONINFO
FILEVERSION 1,0,1,1
PRODUCTVERSION 1,0,1,1
FILEFLAGSMASK 0x3fL
#ifdef _DEBUG
FILEFLAGS 0x1L
#else
FILEFLAGS 0x0L
#endif
FILEOS 0x40004L
FILETYPE 0x1L
FILESUBTYPE 0x0L
BEGIN
BLOCK "StringFileInfo"
BEGIN
BLOCK "200004b0"
BEGIN
VALUE "CompanyName", "Asuro"
VALUE "FileDescription", "Audio Thingy"
VALUE "FileVersion", "1.0.1.1"
VALUE "InternalName", "AsuroTool.exe"
VALUE "LegalCopyright", "Copyright (C) 2022"
VALUE "OriginalFilename", "AsuroTool.exe"
VALUE "ProductName", "Audio Thingy"
VALUE "ProductVersion", "1.0.1.1"
END
END
BLOCK "VarFileInfo"
BEGIN
VALUE "Translation", 0x2000, 1200
END
END
///////////////////////////////////////////////////////////////////////////// /////////////////////////////////////////////////////////////////////////////
// //
// String Table // String Table

View File

@@ -92,6 +92,7 @@
<SDLCheck>true</SDLCheck> <SDLCheck>true</SDLCheck>
<PreprocessorDefinitions>WIN32;_DEBUG;_CONSOLE;%(PreprocessorDefinitions)</PreprocessorDefinitions> <PreprocessorDefinitions>WIN32;_DEBUG;_CONSOLE;%(PreprocessorDefinitions)</PreprocessorDefinitions>
<ConformanceMode>true</ConformanceMode> <ConformanceMode>true</ConformanceMode>
<DisableSpecificWarnings>4305; 4244</DisableSpecificWarnings>
</ClCompile> </ClCompile>
<Link> <Link>
<SubSystem>Console</SubSystem> <SubSystem>Console</SubSystem>
@@ -106,6 +107,7 @@
<SDLCheck>true</SDLCheck> <SDLCheck>true</SDLCheck>
<PreprocessorDefinitions>WIN32;NDEBUG;_CONSOLE;%(PreprocessorDefinitions)</PreprocessorDefinitions> <PreprocessorDefinitions>WIN32;NDEBUG;_CONSOLE;%(PreprocessorDefinitions)</PreprocessorDefinitions>
<ConformanceMode>true</ConformanceMode> <ConformanceMode>true</ConformanceMode>
<DisableSpecificWarnings>4305; 4244</DisableSpecificWarnings>
</ClCompile> </ClCompile>
<Link> <Link>
<SubSystem>Console</SubSystem> <SubSystem>Console</SubSystem>
@@ -120,6 +122,7 @@
<SDLCheck>true</SDLCheck> <SDLCheck>true</SDLCheck>
<PreprocessorDefinitions>_DEBUG;_CONSOLE;%(PreprocessorDefinitions)</PreprocessorDefinitions> <PreprocessorDefinitions>_DEBUG;_CONSOLE;%(PreprocessorDefinitions)</PreprocessorDefinitions>
<ConformanceMode>true</ConformanceMode> <ConformanceMode>true</ConformanceMode>
<DisableSpecificWarnings>4305; 4244</DisableSpecificWarnings>
</ClCompile> </ClCompile>
<Link> <Link>
<SubSystem>Console</SubSystem> <SubSystem>Console</SubSystem>
@@ -134,6 +137,7 @@
<SDLCheck>true</SDLCheck> <SDLCheck>true</SDLCheck>
<PreprocessorDefinitions>NDEBUG;_CONSOLE;%(PreprocessorDefinitions)</PreprocessorDefinitions> <PreprocessorDefinitions>NDEBUG;_CONSOLE;%(PreprocessorDefinitions)</PreprocessorDefinitions>
<ConformanceMode>true</ConformanceMode> <ConformanceMode>true</ConformanceMode>
<DisableSpecificWarnings>4305; 4244</DisableSpecificWarnings>
</ClCompile> </ClCompile>
<Link> <Link>
<SubSystem>Console</SubSystem> <SubSystem>Console</SubSystem>
@@ -146,6 +150,7 @@
<ClCompile Include="ApplicationData.cpp" /> <ClCompile Include="ApplicationData.cpp" />
<ClCompile Include="AsuroTool.cpp" /> <ClCompile Include="AsuroTool.cpp" />
<ClCompile Include="AudioNotificationListener.cpp" /> <ClCompile Include="AudioNotificationListener.cpp" />
<ClCompile Include="Settings.cpp" />
<ClCompile Include="Util.cpp" /> <ClCompile Include="Util.cpp" />
</ItemGroup> </ItemGroup>
<ItemGroup> <ItemGroup>
@@ -156,6 +161,7 @@
<ClInclude Include="AudioNotificationListener.h" /> <ClInclude Include="AudioNotificationListener.h" />
<ClInclude Include="PolicyConfig.h" /> <ClInclude Include="PolicyConfig.h" />
<ClInclude Include="resource.h" /> <ClInclude Include="resource.h" />
<ClInclude Include="Settings.h" />
<ClInclude Include="Util.h" /> <ClInclude Include="Util.h" />
</ItemGroup> </ItemGroup>
<ItemGroup> <ItemGroup>

View File

@@ -36,6 +36,9 @@
<ClCompile Include="AudioNotificationListener.cpp"> <ClCompile Include="AudioNotificationListener.cpp">
<Filter>Source Files\Audio</Filter> <Filter>Source Files\Audio</Filter>
</ClCompile> </ClCompile>
<ClCompile Include="Settings.cpp">
<Filter>Source Files</Filter>
</ClCompile>
</ItemGroup> </ItemGroup>
<ItemGroup> <ItemGroup>
<ClInclude Include="AsuroTool.h"> <ClInclude Include="AsuroTool.h">
@@ -44,9 +47,6 @@
<ClInclude Include="Util.h"> <ClInclude Include="Util.h">
<Filter>Header Files</Filter> <Filter>Header Files</Filter>
</ClInclude> </ClInclude>
<ClInclude Include="PolicyConfig.h">
<Filter>Header Files</Filter>
</ClInclude>
<ClInclude Include="ApplicationData.h"> <ClInclude Include="ApplicationData.h">
<Filter>Header Files</Filter> <Filter>Header Files</Filter>
</ClInclude> </ClInclude>
@@ -59,6 +59,12 @@
<ClInclude Include="AudioNotificationListener.h"> <ClInclude Include="AudioNotificationListener.h">
<Filter>Header Files\Audio</Filter> <Filter>Header Files\Audio</Filter>
</ClInclude> </ClInclude>
<ClInclude Include="Settings.h">
<Filter>Header Files</Filter>
</ClInclude>
<ClInclude Include="PolicyConfig.h">
<Filter>Header Files\Audio</Filter>
</ClInclude>
</ItemGroup> </ItemGroup>
<ItemGroup> <ItemGroup>
<CopyFileToFolders Include="Montserrat-Regular.ttf"> <CopyFileToFolders Include="Montserrat-Regular.ttf">

View File

@@ -10,31 +10,20 @@
#include "AudioApi.h" #include "AudioApi.h"
#include "PolicyConfig.h" #include "PolicyConfig.h"
HRESULT getDeviceProperty(IPropertyStore* propertyStore, const PROPERTYKEY propertyKey, PROPVARIANT* outData) void initAudio(ApplicationData& appData)
{ {
PropVariantInit(outData); HRESULT audioResult;
HRESULT nameResult = propertyStore->GetValue(propertyKey, outData); audioResult = CoInitializeEx(NULL, COINIT_MULTITHREADED);
return nameResult; isError(audioResult, "Failed to initialize COM: ");
}
HRESULT getDevicePropertyString(IPropertyStore* propertyStore, const PROPERTYKEY propertyKey, PROPVARIANT* outData, const wchar_t*& outString, const wchar_t* defaultStr) audioResult = CoCreateInstance(__uuidof(MMDeviceEnumerator), NULL, CLSCTX_INPROC_SERVER, IID_PPV_ARGS(&appData.audioData->deviceEnumerator));
{ isError(audioResult, "Failed to set up audio device enumerator: ");
HRESULT result = getDeviceProperty(propertyStore, propertyKey, outData);
outString = outData->vt != VT_LPWSTR ? defaultStr : outData->pwszVal;
return result;
}
void setDefaultAudioDevice(AudioData& audioData, const wchar_t* deviceId, ERole role) appData.audioData->audioNotificationListener = new AudioNotificationListener(appData.audioData);
{ audioResult = appData.audioData->deviceEnumerator->RegisterEndpointNotificationCallback(appData.audioData->audioNotificationListener);
IPolicyConfigVista* pPolicyConfig; isError(audioResult, "Failed to register audio notification listener: ");
HRESULT hr = CoCreateInstance(__uuidof(CPolicyConfigVistaClient), NULL, CLSCTX_ALL, __uuidof(IPolicyConfigVista), (LPVOID*)&pPolicyConfig); reloadDeviceLists(*appData.audioData);
if (!isError(hr, "Failed to set default audio device: "))
{
hr = pPolicyConfig->SetDefaultEndpoint(deviceId, role);
pPolicyConfig->Release();
reloadDeviceLists(audioData);
}
} }
void loadAudioDevices(AudioData& audioData, std::vector<AudioDevice>& deviceList, EDataFlow deviceType) void loadAudioDevices(AudioData& audioData, std::vector<AudioDevice>& deviceList, EDataFlow deviceType)
@@ -42,7 +31,7 @@ void loadAudioDevices(AudioData& audioData, std::vector<AudioDevice>& deviceList
deviceList.clear(); deviceList.clear();
HRESULT err; HRESULT err;
IMMDeviceCollection* deviceCollection = NULL; IMMDeviceCollection* deviceCollection = nullptr;
err = audioData.deviceEnumerator->EnumAudioEndpoints(deviceType, DEVICE_STATE_ACTIVE | DEVICE_STATE_DISABLED, &deviceCollection); err = audioData.deviceEnumerator->EnumAudioEndpoints(deviceType, DEVICE_STATE_ACTIVE | DEVICE_STATE_DISABLED, &deviceCollection);
if (isError(err, "Failed to enumerate audio devices: ")) return; if (isError(err, "Failed to enumerate audio devices: ")) return;
@@ -51,7 +40,7 @@ void loadAudioDevices(AudioData& audioData, std::vector<AudioDevice>& deviceList
err = deviceCollection->GetCount(&deviceCount); err = deviceCollection->GetCount(&deviceCount);
if (isError(err, "Failed to count audio devices: ")) return; if (isError(err, "Failed to count audio devices: ")) return;
IMMDevice* defaultConsoleDevice = NULL; IMMDevice* defaultConsoleDevice = nullptr;
LPWSTR defaultConsoleId = nullptr; LPWSTR defaultConsoleId = nullptr;
err = audioData.deviceEnumerator->GetDefaultAudioEndpoint(deviceType, ERole::eConsole, &defaultConsoleDevice); err = audioData.deviceEnumerator->GetDefaultAudioEndpoint(deviceType, ERole::eConsole, &defaultConsoleDevice);
if (!FAILED(err)) if (!FAILED(err))
@@ -59,7 +48,7 @@ void loadAudioDevices(AudioData& audioData, std::vector<AudioDevice>& deviceList
defaultConsoleDevice->GetId(&defaultConsoleId); defaultConsoleDevice->GetId(&defaultConsoleId);
} }
IMMDevice* defaultMediaOutput = NULL; IMMDevice* defaultMediaOutput = nullptr;
LPWSTR defaultMediaId = nullptr; LPWSTR defaultMediaId = nullptr;
err = audioData.deviceEnumerator->GetDefaultAudioEndpoint(deviceType, ERole::eMultimedia, &defaultMediaOutput); err = audioData.deviceEnumerator->GetDefaultAudioEndpoint(deviceType, ERole::eMultimedia, &defaultMediaOutput);
if (!FAILED(err)) if (!FAILED(err))
@@ -67,7 +56,7 @@ void loadAudioDevices(AudioData& audioData, std::vector<AudioDevice>& deviceList
defaultMediaOutput->GetId(&defaultMediaId); defaultMediaOutput->GetId(&defaultMediaId);
} }
IMMDevice* defaultCommunicationOutput = NULL; IMMDevice* defaultCommunicationOutput = nullptr;
LPWSTR defaultCommunicationId = nullptr; LPWSTR defaultCommunicationId = nullptr;
err = audioData.deviceEnumerator->GetDefaultAudioEndpoint(deviceType, ERole::eCommunications, &defaultCommunicationOutput); err = audioData.deviceEnumerator->GetDefaultAudioEndpoint(deviceType, ERole::eCommunications, &defaultCommunicationOutput);
if (!FAILED(err)) if (!FAILED(err))
@@ -106,7 +95,6 @@ void loadAudioDevices(AudioData& audioData, std::vector<AudioDevice>& deviceList
err = deviceData.device->Activate(__uuidof(IAudioEndpointVolume), CLSCTX_INPROC_SERVER, NULL, (LPVOID*)&deviceData.volumeInterface); err = deviceData.device->Activate(__uuidof(IAudioEndpointVolume), CLSCTX_INPROC_SERVER, NULL, (LPVOID*)&deviceData.volumeInterface);
isError(err, "Failed to get audio endpoint volume interface: "); isError(err, "Failed to get audio endpoint volume interface: ");
IAudioMeterInformation* meterInterface;
err = deviceData.device->Activate(__uuidof(IAudioMeterInformation), CLSCTX_INPROC_SERVER, NULL, (LPVOID*)&deviceData.meterInterface); err = deviceData.device->Activate(__uuidof(IAudioMeterInformation), CLSCTX_INPROC_SERVER, NULL, (LPVOID*)&deviceData.meterInterface);
isError(err, "Failed to get audio meter interface: "); isError(err, "Failed to get audio meter interface: ");
@@ -146,6 +134,33 @@ void reloadDeviceLists(AudioData& audioData)
loadAudioDevices(audioData, audioData.recordingDevices, EDataFlow::eCapture); loadAudioDevices(audioData, audioData.recordingDevices, EDataFlow::eCapture);
} }
HRESULT getDeviceProperty(IPropertyStore* propertyStore, const PROPERTYKEY propertyKey, PROPVARIANT* outData)
{
PropVariantInit(outData);
HRESULT nameResult = propertyStore->GetValue(propertyKey, outData);
return nameResult;
}
HRESULT getDevicePropertyString(IPropertyStore* propertyStore, const PROPERTYKEY propertyKey, PROPVARIANT* outData, const wchar_t*& outString, const wchar_t* defaultStr)
{
HRESULT result = getDeviceProperty(propertyStore, propertyKey, outData);
outString = outData->vt != VT_LPWSTR ? defaultStr : outData->pwszVal;
return result;
}
void setDefaultAudioDevice(AudioData& audioData, const wchar_t* deviceId, ERole role)
{
IPolicyConfigVista* pPolicyConfig = nullptr;
HRESULT hr = CoCreateInstance(__uuidof(CPolicyConfigVistaClient), NULL, CLSCTX_ALL, __uuidof(IPolicyConfigVista), (LPVOID*)&pPolicyConfig);
if (!isError(hr, "Failed to set default audio device: "))
{
hr = pPolicyConfig->SetDefaultEndpoint(deviceId, role);
pPolicyConfig->Release();
reloadDeviceLists(audioData);
}
}
float getVolume(IAudioEndpointVolume* volumeInterface) float getVolume(IAudioEndpointVolume* volumeInterface)
{ {
float volume; float volume;

View File

@@ -4,11 +4,14 @@
#include "ApplicationData.h" #include "ApplicationData.h"
void initAudio(ApplicationData& appData);
void loadAudioDevices(AudioData& audioData, std::vector<AudioDevice>& deviceList, EDataFlow deviceType);
void reloadDeviceLists(AudioData& audioData);
HRESULT getDeviceProperty(IPropertyStore* propertyStore, const PROPERTYKEY propertyKey, PROPVARIANT* outData); HRESULT getDeviceProperty(IPropertyStore* propertyStore, const PROPERTYKEY propertyKey, PROPVARIANT* outData);
HRESULT getDevicePropertyString(IPropertyStore* propertyStore, const PROPERTYKEY propertyKey, PROPVARIANT* outData, const wchar_t*& outString, const wchar_t* defaultStr = L"Unknown"); HRESULT getDevicePropertyString(IPropertyStore* propertyStore, const PROPERTYKEY propertyKey, PROPVARIANT* outData, const wchar_t*& outString, const wchar_t* defaultStr = L"Unknown");
void setDefaultAudioDevice(AudioData& audioData, const wchar_t* deviceId, ERole role); void setDefaultAudioDevice(AudioData& audioData, const wchar_t* deviceId, ERole role);
void loadAudioDevices(AudioData& audioData, std::vector<AudioDevice>& deviceList, EDataFlow deviceType);
void reloadDeviceLists(AudioData& audioData);
float getVolume(IAudioEndpointVolume* volumeInterface); float getVolume(IAudioEndpointVolume* volumeInterface);
void setVolume(IAudioEndpointVolume* volumeInterface, float newVolume); void setVolume(IAudioEndpointVolume* volumeInterface, float newVolume);
float getMeterValue(IAudioMeterInformation* meterInterface); float getMeterValue(IAudioMeterInformation* meterInterface);

View File

@@ -126,7 +126,7 @@ HRESULT __stdcall AudioNotificationListener::QueryInterface(REFIID riid, void**
} }
else else
{ {
*ppvObject = NULL; *ppvObject = nullptr;
return E_NOINTERFACE; return E_NOINTERFACE;
} }

97
AsuroTool/Settings.cpp Normal file
View File

@@ -0,0 +1,97 @@
#include "ApplicationData.h"
#include "ImguiBase.h"
#include "Util.h"
#include "Settings.h"
extern bool justDocked;
extern DrawData* gDrawData;
extern ApplicationData* gAppData;
void initSettings(DrawData& drawData, ApplicationData& appData)
{
ImGuiSettingsHandler ini_handler;
ini_handler.TypeName = "ApplicationSettings";
ini_handler.TypeHash = ImHashStr("ApplicationSettings");
ini_handler.ReadOpenFn = settingsReadOpen;
ini_handler.ReadLineFn = settingsReadLine;
ini_handler.WriteAllFn = settingsWriteAll;
GImGui->SettingsHandlers.push_back(ini_handler);
ImGui::LoadIniSettingsFromDisk("imgui.ini");
applySettings(drawData, appData);
}
void* settingsReadOpen(ImGuiContext* ctx, ImGuiSettingsHandler* handler, const char* name)
{
ApplicationSettings* settings = &gAppData->settings;
*settings = ApplicationSettings();
return (void*)settings;
}
void settingsReadLine(ImGuiContext* ctx, ImGuiSettingsHandler* handler, void* entry, const char* line)
{
ApplicationSettings* settings = (ApplicationSettings*)entry;
int docked;
if (sscanf_s(line, "docked=%i", &docked))
{
settings->docked = (bool)docked;
}
int autostart;
if (sscanf_s(line, "autostart=%i", &autostart))
{
settings->autostart = (bool)autostart;
}
}
void settingsWriteAll(ImGuiContext* ctx, ImGuiSettingsHandler* handler, ImGuiTextBuffer* outBuf)
{
outBuf->appendf("[%s][%s]\n", "ApplicationSettings", "ApplicationSettings");
outBuf->appendf("docked=%i\n", (int)gAppData->settings.docked);
outBuf->appendf("autostart=%i\n", (int)gAppData->settings.autostart);
outBuf->append("\n");
}
void applySettings(DrawData& drawData, ApplicationData& appData)
{
updateDocked(drawData, appData);
setAutostart(appData.settings.autostart);
}
void updateDocked(DrawData& drawData, ApplicationData& appData)
{
justDocked = true;
glfwSetWindowAttrib(drawData.window, GLFW_DECORATED, !appData.settings.docked);
ShowWindow(drawData.window_handle, SW_HIDE);
SetWindowLongPtr(drawData.window_handle, GWL_EXSTYLE, appData.settings.docked ? WS_EX_TOOLWINDOW : 0);
ShowWindow(drawData.window_handle, SW_SHOW);
}
void setAutostart(bool newValue)
{
const size_t MAX_PATH_LENGTH = 2048;
const wchar_t* KEY_APP_NAME = L"AsuroTool";
HRESULT hr;
HKEY runKey = nullptr;
hr = RegCreateKey(HKEY_CURRENT_USER, L"SOFTWARE\\Microsoft\\Windows\\CurrentVersion\\Run", &runKey);
if (isError(hr, "Failed to find/create autostart run key: ")) return;
if (newValue)
{
std::wstring appPath;
appPath.resize(MAX_PATH_LENGTH);
hr = GetModuleFileName(NULL, &appPath[0], static_cast<DWORD>(appPath.size()));
if (isError(hr, "Failed to get executable name: ")) return;
appPath.resize(wcslen(appPath.data()));
hr = RegSetValueEx(runKey, KEY_APP_NAME, 0, REG_SZ, (BYTE*)appPath.c_str(), (appPath.size() + 1) * sizeof(wchar_t));
if (isError(hr, "Failed to write autostart key: ")) return;
}
else
{
hr = RegDeleteValue(runKey, KEY_APP_NAME);
if (isError(hr, "Failed to delete autostart key: ")) return;
}
}

15
AsuroTool/Settings.h Normal file
View File

@@ -0,0 +1,15 @@
#pragma once
#include "ImguiBase.h"
#include <imgui_internal.h>
#include "ApplicationData.h"
void initSettings(DrawData& drawData, ApplicationData& appData);
void* settingsReadOpen(ImGuiContext* ctx, ImGuiSettingsHandler* handler, const char* name);
void settingsReadLine(ImGuiContext* ctx, ImGuiSettingsHandler* handler, void* entry, const char* line);
void settingsWriteAll(ImGuiContext* ctx, ImGuiSettingsHandler* handler, ImGuiTextBuffer* outBuf);
void applySettings(DrawData& drawData, ApplicationData& appData);
void updateDocked(DrawData& drawData, ApplicationData& appData);
void setAutostart(bool newValue);

534
ImguiBase/ImguiBase.cpp Normal file
View File

@@ -0,0 +1,534 @@
#include "ImguiBase.h"
#include "imgui_impl_glfw.h"
#include "imgui_impl_vulkan.h"
#include <iostream>
#include <GLFW/glfw3native.h>
#include <vulkan/vulkan.h>
static VkAllocationCallbacks* g_Allocator = NULL;
static VkInstance g_Instance = VK_NULL_HANDLE;
static VkPhysicalDevice g_PhysicalDevice = VK_NULL_HANDLE;
static VkDevice g_Device = VK_NULL_HANDLE;
static uint32_t g_QueueFamily = (uint32_t)-1;
static VkQueue g_Queue = VK_NULL_HANDLE;
static VkDebugReportCallbackEXT g_DebugReport = VK_NULL_HANDLE;
static VkPipelineCache g_PipelineCache = VK_NULL_HANDLE;
static VkDescriptorPool g_DescriptorPool = VK_NULL_HANDLE;
static ImGui_ImplVulkanH_Window g_MainWindowData;
static int g_MinImageCount = 2;
static bool g_SwapChainRebuild = false;
static void check_vk_result(VkResult err)
{
if (err == 0)
return;
fprintf(stderr, "[vulkan] Error: VkResult = %d\n", err);
if (err < 0)
abort();
}
#ifdef IMGUI_VULKAN_DEBUG_REPORT
static VKAPI_ATTR VkBool32 VKAPI_CALL debug_report(VkDebugReportFlagsEXT flags, VkDebugReportObjectTypeEXT objectType, uint64_t object, size_t location, int32_t messageCode, const char* pLayerPrefix, const char* pMessage, void* pUserData)
{
(void)flags; (void)object; (void)location; (void)messageCode; (void)pUserData; (void)pLayerPrefix; // Unused arguments
fprintf(stderr, "[vulkan] Debug report from ObjectType: %i\nMessage: %s\n\n", objectType, pMessage);
return VK_FALSE;
}
#endif // IMGUI_VULKAN_DEBUG_REPORT
static void SetupVulkan(const char** extensions, uint32_t extensions_count)
{
VkResult err;
// Create Vulkan Instance
{
VkInstanceCreateInfo create_info = {};
create_info.sType = VK_STRUCTURE_TYPE_INSTANCE_CREATE_INFO;
create_info.enabledExtensionCount = extensions_count;
create_info.ppEnabledExtensionNames = extensions;
#ifdef IMGUI_VULKAN_DEBUG_REPORT
// Enabling validation layers
const char* layers[] = { "VK_LAYER_KHRONOS_validation" };
create_info.enabledLayerCount = 1;
create_info.ppEnabledLayerNames = layers;
// Enable debug report extension (we need additional storage, so we duplicate the user array to add our new extension to it)
const char** extensions_ext = (const char**)malloc(sizeof(const char*) * (extensions_count + 1));
memcpy(extensions_ext, extensions, extensions_count * sizeof(const char*));
extensions_ext[extensions_count] = "VK_EXT_debug_report";
create_info.enabledExtensionCount = extensions_count + 1;
create_info.ppEnabledExtensionNames = extensions_ext;
// Create Vulkan Instance
err = vkCreateInstance(&create_info, g_Allocator, &g_Instance);
check_vk_result(err);
free(extensions_ext);
// Get the function pointer (required for any extensions)
auto vkCreateDebugReportCallbackEXT = (PFN_vkCreateDebugReportCallbackEXT)vkGetInstanceProcAddr(g_Instance, "vkCreateDebugReportCallbackEXT");
IM_ASSERT(vkCreateDebugReportCallbackEXT != NULL);
// Setup the debug report callback
VkDebugReportCallbackCreateInfoEXT debug_report_ci = {};
debug_report_ci.sType = VK_STRUCTURE_TYPE_DEBUG_REPORT_CALLBACK_CREATE_INFO_EXT;
debug_report_ci.flags = VK_DEBUG_REPORT_ERROR_BIT_EXT | VK_DEBUG_REPORT_WARNING_BIT_EXT | VK_DEBUG_REPORT_PERFORMANCE_WARNING_BIT_EXT;
debug_report_ci.pfnCallback = debug_report;
debug_report_ci.pUserData = NULL;
err = vkCreateDebugReportCallbackEXT(g_Instance, &debug_report_ci, g_Allocator, &g_DebugReport);
check_vk_result(err);
#else
// Create Vulkan Instance without any debug feature
err = vkCreateInstance(&create_info, g_Allocator, &g_Instance);
check_vk_result(err);
IM_UNUSED(g_DebugReport);
#endif
}
// Select GPU
{
uint32_t gpu_count;
err = vkEnumeratePhysicalDevices(g_Instance, &gpu_count, NULL);
check_vk_result(err);
IM_ASSERT(gpu_count > 0);
VkPhysicalDevice* gpus = (VkPhysicalDevice*)malloc(sizeof(VkPhysicalDevice) * gpu_count);
err = vkEnumeratePhysicalDevices(g_Instance, &gpu_count, gpus);
check_vk_result(err);
// If a number >1 of GPUs got reported, find discrete GPU if present, or use first one available. This covers
// most common cases (multi-gpu/integrated+dedicated graphics). Handling more complicated setups (multiple
// dedicated GPUs) is out of scope of this sample.
int use_gpu = 0;
for (int i = 0; i < (int)gpu_count; i++)
{
VkPhysicalDeviceProperties properties;
vkGetPhysicalDeviceProperties(gpus[i], &properties);
if (properties.deviceType == VK_PHYSICAL_DEVICE_TYPE_DISCRETE_GPU)
{
use_gpu = i;
break;
}
}
g_PhysicalDevice = gpus[use_gpu];
free(gpus);
}
// Select graphics queue family
{
uint32_t count;
vkGetPhysicalDeviceQueueFamilyProperties(g_PhysicalDevice, &count, NULL);
VkQueueFamilyProperties* queues = (VkQueueFamilyProperties*)malloc(sizeof(VkQueueFamilyProperties) * count);
vkGetPhysicalDeviceQueueFamilyProperties(g_PhysicalDevice, &count, queues);
for (uint32_t i = 0; i < count; i++)
if (queues[i].queueFlags & VK_QUEUE_GRAPHICS_BIT)
{
g_QueueFamily = i;
break;
}
free(queues);
IM_ASSERT(g_QueueFamily != (uint32_t)-1);
}
// Create Logical Device (with 1 queue)
{
int device_extension_count = 1;
const char* device_extensions[] = { "VK_KHR_swapchain" };
const float queue_priority[] = { 1.0f };
VkDeviceQueueCreateInfo queue_info[1] = {};
queue_info[0].sType = VK_STRUCTURE_TYPE_DEVICE_QUEUE_CREATE_INFO;
queue_info[0].queueFamilyIndex = g_QueueFamily;
queue_info[0].queueCount = 1;
queue_info[0].pQueuePriorities = queue_priority;
VkDeviceCreateInfo create_info = {};
create_info.sType = VK_STRUCTURE_TYPE_DEVICE_CREATE_INFO;
create_info.queueCreateInfoCount = sizeof(queue_info) / sizeof(queue_info[0]);
create_info.pQueueCreateInfos = queue_info;
create_info.enabledExtensionCount = device_extension_count;
create_info.ppEnabledExtensionNames = device_extensions;
err = vkCreateDevice(g_PhysicalDevice, &create_info, g_Allocator, &g_Device);
check_vk_result(err);
vkGetDeviceQueue(g_Device, g_QueueFamily, 0, &g_Queue);
}
// Create Descriptor Pool
{
VkDescriptorPoolSize pool_sizes[] =
{
{ VK_DESCRIPTOR_TYPE_SAMPLER, 1000 },
{ VK_DESCRIPTOR_TYPE_COMBINED_IMAGE_SAMPLER, 1000 },
{ VK_DESCRIPTOR_TYPE_SAMPLED_IMAGE, 1000 },
{ VK_DESCRIPTOR_TYPE_STORAGE_IMAGE, 1000 },
{ VK_DESCRIPTOR_TYPE_UNIFORM_TEXEL_BUFFER, 1000 },
{ VK_DESCRIPTOR_TYPE_STORAGE_TEXEL_BUFFER, 1000 },
{ VK_DESCRIPTOR_TYPE_UNIFORM_BUFFER, 1000 },
{ VK_DESCRIPTOR_TYPE_STORAGE_BUFFER, 1000 },
{ VK_DESCRIPTOR_TYPE_UNIFORM_BUFFER_DYNAMIC, 1000 },
{ VK_DESCRIPTOR_TYPE_STORAGE_BUFFER_DYNAMIC, 1000 },
{ VK_DESCRIPTOR_TYPE_INPUT_ATTACHMENT, 1000 }
};
VkDescriptorPoolCreateInfo pool_info = {};
pool_info.sType = VK_STRUCTURE_TYPE_DESCRIPTOR_POOL_CREATE_INFO;
pool_info.flags = VK_DESCRIPTOR_POOL_CREATE_FREE_DESCRIPTOR_SET_BIT;
pool_info.maxSets = 1000 * IM_ARRAYSIZE(pool_sizes);
pool_info.poolSizeCount = (uint32_t)IM_ARRAYSIZE(pool_sizes);
pool_info.pPoolSizes = pool_sizes;
err = vkCreateDescriptorPool(g_Device, &pool_info, g_Allocator, &g_DescriptorPool);
check_vk_result(err);
}
}
// All the ImGui_ImplVulkanH_XXX structures/functions are optional helpers used by the demo.
// Your real engine/app may not use them.
static void SetupVulkanWindow(ImGui_ImplVulkanH_Window* wd, VkSurfaceKHR surface, int width, int height)
{
wd->Surface = surface;
// Check for WSI support
VkBool32 res;
vkGetPhysicalDeviceSurfaceSupportKHR(g_PhysicalDevice, g_QueueFamily, wd->Surface, &res);
if (res != VK_TRUE)
{
fprintf(stderr, "Error no WSI support on physical device 0\n");
exit(-1);
}
// Select Surface Format
const VkFormat requestSurfaceImageFormat[] = { VK_FORMAT_B8G8R8A8_UNORM, VK_FORMAT_R8G8B8A8_UNORM, VK_FORMAT_B8G8R8_UNORM, VK_FORMAT_R8G8B8_UNORM };
const VkColorSpaceKHR requestSurfaceColorSpace = VK_COLORSPACE_SRGB_NONLINEAR_KHR;
wd->SurfaceFormat = ImGui_ImplVulkanH_SelectSurfaceFormat(g_PhysicalDevice, wd->Surface, requestSurfaceImageFormat, (size_t)IM_ARRAYSIZE(requestSurfaceImageFormat), requestSurfaceColorSpace);
// Select Present Mode
#ifdef IMGUI_UNLIMITED_FRAME_RATE
VkPresentModeKHR present_modes[] = { VK_PRESENT_MODE_MAILBOX_KHR, VK_PRESENT_MODE_IMMEDIATE_KHR, VK_PRESENT_MODE_FIFO_KHR };
#else
VkPresentModeKHR present_modes[] = { VK_PRESENT_MODE_FIFO_KHR };
#endif
wd->PresentMode = ImGui_ImplVulkanH_SelectPresentMode(g_PhysicalDevice, wd->Surface, &present_modes[0], IM_ARRAYSIZE(present_modes));
//printf("[vulkan] Selected PresentMode = %d\n", wd->PresentMode);
// Create SwapChain, RenderPass, Framebuffer, etc.
IM_ASSERT(g_MinImageCount >= 2);
ImGui_ImplVulkanH_CreateOrResizeWindow(g_Instance, g_PhysicalDevice, g_Device, wd, g_QueueFamily, g_Allocator, width, height, g_MinImageCount);
}
static void CleanupVulkan()
{
vkDestroyDescriptorPool(g_Device, g_DescriptorPool, g_Allocator);
#ifdef IMGUI_VULKAN_DEBUG_REPORT
// Remove the debug report callback
auto vkDestroyDebugReportCallbackEXT = (PFN_vkDestroyDebugReportCallbackEXT)vkGetInstanceProcAddr(g_Instance, "vkDestroyDebugReportCallbackEXT");
vkDestroyDebugReportCallbackEXT(g_Instance, g_DebugReport, g_Allocator);
#endif // IMGUI_VULKAN_DEBUG_REPORT
vkDestroyDevice(g_Device, g_Allocator);
vkDestroyInstance(g_Instance, g_Allocator);
}
static void CleanupVulkanWindow()
{
ImGui_ImplVulkanH_DestroyWindow(g_Instance, g_Device, &g_MainWindowData, g_Allocator);
}
static void FrameRender(ImGui_ImplVulkanH_Window* wd, ImDrawData* draw_data)
{
VkResult err;
VkSemaphore image_acquired_semaphore = wd->FrameSemaphores[wd->SemaphoreIndex].ImageAcquiredSemaphore;
VkSemaphore render_complete_semaphore = wd->FrameSemaphores[wd->SemaphoreIndex].RenderCompleteSemaphore;
err = vkAcquireNextImageKHR(g_Device, wd->Swapchain, UINT64_MAX, image_acquired_semaphore, VK_NULL_HANDLE, &wd->FrameIndex);
if (err == VK_ERROR_OUT_OF_DATE_KHR || err == VK_SUBOPTIMAL_KHR)
{
g_SwapChainRebuild = true;
return;
}
check_vk_result(err);
ImGui_ImplVulkanH_Frame* fd = &wd->Frames[wd->FrameIndex];
{
err = vkWaitForFences(g_Device, 1, &fd->Fence, VK_TRUE, UINT64_MAX); // wait indefinitely instead of periodically checking
check_vk_result(err);
err = vkResetFences(g_Device, 1, &fd->Fence);
check_vk_result(err);
}
{
err = vkResetCommandPool(g_Device, fd->CommandPool, 0);
check_vk_result(err);
VkCommandBufferBeginInfo info = {};
info.sType = VK_STRUCTURE_TYPE_COMMAND_BUFFER_BEGIN_INFO;
info.flags |= VK_COMMAND_BUFFER_USAGE_ONE_TIME_SUBMIT_BIT;
err = vkBeginCommandBuffer(fd->CommandBuffer, &info);
check_vk_result(err);
}
{
VkRenderPassBeginInfo info = {};
info.sType = VK_STRUCTURE_TYPE_RENDER_PASS_BEGIN_INFO;
info.renderPass = wd->RenderPass;
info.framebuffer = fd->Framebuffer;
info.renderArea.extent.width = wd->Width;
info.renderArea.extent.height = wd->Height;
info.clearValueCount = 1;
info.pClearValues = &wd->ClearValue;
vkCmdBeginRenderPass(fd->CommandBuffer, &info, VK_SUBPASS_CONTENTS_INLINE);
}
// Record dear imgui primitives into command buffer
ImGui_ImplVulkan_RenderDrawData(draw_data, fd->CommandBuffer);
// Submit command buffer
vkCmdEndRenderPass(fd->CommandBuffer);
{
VkPipelineStageFlags wait_stage = VK_PIPELINE_STAGE_COLOR_ATTACHMENT_OUTPUT_BIT;
VkSubmitInfo info = {};
info.sType = VK_STRUCTURE_TYPE_SUBMIT_INFO;
info.waitSemaphoreCount = 1;
info.pWaitSemaphores = &image_acquired_semaphore;
info.pWaitDstStageMask = &wait_stage;
info.commandBufferCount = 1;
info.pCommandBuffers = &fd->CommandBuffer;
info.signalSemaphoreCount = 1;
info.pSignalSemaphores = &render_complete_semaphore;
err = vkEndCommandBuffer(fd->CommandBuffer);
check_vk_result(err);
err = vkQueueSubmit(g_Queue, 1, &info, fd->Fence);
check_vk_result(err);
}
}
static void FramePresent(ImGui_ImplVulkanH_Window* wd)
{
if (g_SwapChainRebuild)
return;
VkSemaphore render_complete_semaphore = wd->FrameSemaphores[wd->SemaphoreIndex].RenderCompleteSemaphore;
VkPresentInfoKHR info = {};
info.sType = VK_STRUCTURE_TYPE_PRESENT_INFO_KHR;
info.waitSemaphoreCount = 1;
info.pWaitSemaphores = &render_complete_semaphore;
info.swapchainCount = 1;
info.pSwapchains = &wd->Swapchain;
info.pImageIndices = &wd->FrameIndex;
VkResult err = vkQueuePresentKHR(g_Queue, &info);
if (err == VK_ERROR_OUT_OF_DATE_KHR || err == VK_SUBOPTIMAL_KHR)
{
g_SwapChainRebuild = true;
return;
}
check_vk_result(err);
wd->SemaphoreIndex = (wd->SemaphoreIndex + 1) % wd->ImageCount; // Now we can use the next set of semaphores
}
static void glfw_error_callback(int error, const char* description)
{
fprintf(stderr, "Glfw Error %d: %s\n", error, description);
}
int startImgui(ImGuiCallbacks& callbacks, const char* title, int windowWidth, int windowHeight)
{
// Setup GLFW window
glfwSetErrorCallback(glfw_error_callback);
if (!glfwInit())
return 1;
glfwWindowHint(GLFW_CLIENT_API, GLFW_NO_API);
glfwWindowHint(GLFW_VISIBLE, GLFW_FALSE);
GLFWwindow* window = glfwCreateWindow(windowWidth, windowHeight, title, NULL, NULL);
// Setup Vulkan
if (!glfwVulkanSupported())
{
printf("GLFW: Vulkan Not Supported\n");
return 1;
}
uint32_t extensions_count = 0;
const char** extensions = glfwGetRequiredInstanceExtensions(&extensions_count);
SetupVulkan(extensions, extensions_count);
// Create Window Surface
VkSurfaceKHR surface;
VkResult err = glfwCreateWindowSurface(g_Instance, window, g_Allocator, &surface);
check_vk_result(err);
// Create Framebuffers
int w, h;
glfwGetFramebufferSize(window, &w, &h);
ImGui_ImplVulkanH_Window* wd = &g_MainWindowData;
SetupVulkanWindow(wd, surface, w, h);
// Setup Dear ImGui context
IMGUI_CHECKVERSION();
ImGui::CreateContext();
ImGuiIO& io = ImGui::GetIO(); (void)io;
//io.ConfigFlags |= ImGuiConfigFlags_NavEnableKeyboard; // Enable Keyboard Controls
//io.ConfigFlags |= ImGuiConfigFlags_NavEnableGamepad; // Enable Gamepad Controls
// Setup Dear ImGui style
ImGui::StyleColorsDark();
//ImGui::StyleColorsClassic();
// Setup Platform/Renderer backends
ImGui_ImplGlfw_InitForVulkan(window, true);
ImGui_ImplVulkan_InitInfo init_info = {};
init_info.Instance = g_Instance;
init_info.PhysicalDevice = g_PhysicalDevice;
init_info.Device = g_Device;
init_info.QueueFamily = g_QueueFamily;
init_info.Queue = g_Queue;
init_info.PipelineCache = g_PipelineCache;
init_info.DescriptorPool = g_DescriptorPool;
init_info.Subpass = 0;
init_info.MinImageCount = g_MinImageCount;
init_info.ImageCount = wd->ImageCount;
init_info.MSAASamples = VK_SAMPLE_COUNT_1_BIT;
init_info.Allocator = g_Allocator;
init_info.CheckVkResultFn = check_vk_result;
ImGui_ImplVulkan_Init(&init_info, wd->RenderPass);
// Load Fonts
// - If no fonts are loaded, dear imgui will use the default font. You can also load multiple fonts and use ImGui::PushFont()/PopFont() to select them.
// - AddFontFromFileTTF() will return the ImFont* so you can store it if you need to select the font among multiple.
// - If the file cannot be loaded, the function will return NULL. Please handle those errors in your application (e.g. use an assertion, or display an error and quit).
// - The fonts will be rasterized at a given size (w/ oversampling) and stored into a texture when calling ImFontAtlas::Build()/GetTexDataAsXXXX(), which ImGui_ImplXXXX_NewFrame below will call.
// - Read 'docs/FONTS.md' for more instructions and details.
// - Remember that in C/C++ if you want to include a backslash \ in a string literal you need to write a double backslash \\ !
//io.Fonts->AddFontDefault();
//io.Fonts->AddFontFromFileTTF("../../misc/fonts/Roboto-Medium.ttf", 16.0f);
//io.Fonts->AddFontFromFileTTF("../../misc/fonts/Cousine-Regular.ttf", 15.0f);
//io.Fonts->AddFontFromFileTTF("../../misc/fonts/DroidSans.ttf", 16.0f);
//io.Fonts->AddFontFromFileTTF("../../misc/fonts/ProggyTiny.ttf", 10.0f);
//ImFont* font = io.Fonts->AddFontFromFileTTF("c:\\Windows\\Fonts\\ArialUni.ttf", 18.0f, NULL, io.Fonts->GetGlyphRangesJapanese());
//IM_ASSERT(font != NULL);
// Our state
DrawData drawData{};
drawData.window = window;
drawData.window_handle = glfwGetWin32Window(window);
drawData.window_size = getWindowSize(window);
if (callbacks.initFunc)
{
callbacks.initFunc(drawData);
}
glfwShowWindow(window);
// Upload Fonts
{
// Use any command queue
VkCommandPool command_pool = wd->Frames[wd->FrameIndex].CommandPool;
VkCommandBuffer command_buffer = wd->Frames[wd->FrameIndex].CommandBuffer;
err = vkResetCommandPool(g_Device, command_pool, 0);
check_vk_result(err);
VkCommandBufferBeginInfo begin_info = {};
begin_info.sType = VK_STRUCTURE_TYPE_COMMAND_BUFFER_BEGIN_INFO;
begin_info.flags |= VK_COMMAND_BUFFER_USAGE_ONE_TIME_SUBMIT_BIT;
err = vkBeginCommandBuffer(command_buffer, &begin_info);
check_vk_result(err);
ImGui_ImplVulkan_CreateFontsTexture(command_buffer);
VkSubmitInfo end_info = {};
end_info.sType = VK_STRUCTURE_TYPE_SUBMIT_INFO;
end_info.commandBufferCount = 1;
end_info.pCommandBuffers = &command_buffer;
err = vkEndCommandBuffer(command_buffer);
check_vk_result(err);
err = vkQueueSubmit(g_Queue, 1, &end_info, VK_NULL_HANDLE);
check_vk_result(err);
err = vkDeviceWaitIdle(g_Device);
check_vk_result(err);
ImGui_ImplVulkan_DestroyFontUploadObjects();
}
// Main loop
while (!glfwWindowShouldClose(window))
{
// Poll and handle events (inputs, window resize, etc.)
// You can read the io.WantCaptureMouse, io.WantCaptureKeyboard flags to tell if dear imgui wants to use your inputs.
// - When io.WantCaptureMouse is true, do not dispatch mouse input data to your main application.
// - When io.WantCaptureKeyboard is true, do not dispatch keyboard input data to your main application.
// Generally you may always pass all inputs to dear imgui, and hide them from your application based on those two flags.
glfwPollEvents();
// Resize swap chain?
if (g_SwapChainRebuild)
{
int width, height;
glfwGetFramebufferSize(window, &width, &height);
if (width > 0 && height > 0)
{
ImGui_ImplVulkan_SetMinImageCount(g_MinImageCount);
ImGui_ImplVulkanH_CreateOrResizeWindow(g_Instance, g_PhysicalDevice, g_Device, &g_MainWindowData, g_QueueFamily, g_Allocator, width, height, g_MinImageCount);
g_MainWindowData.FrameIndex = 0;
g_SwapChainRebuild = false;
}
}
// Start the Dear ImGui frame
ImGui_ImplVulkan_NewFrame();
ImGui_ImplGlfw_NewFrame();
ImGui::NewFrame();
ImVec2 prevWindowSize = getWindowSize(window);
drawData.window_size = prevWindowSize;
if (callbacks.drawFunc)
{
callbacks.drawFunc(drawData);
}
if (drawData.window_size.x != prevWindowSize.x || drawData.window_size.y != prevWindowSize.y)
{
glfwSetWindowSize(window, drawData.window_size.x, drawData.window_size.y);
}
// Rendering
ImGui::Render();
ImDrawData* draw_data = ImGui::GetDrawData();
const bool is_minimized = (draw_data->DisplaySize.x <= 0.0f || draw_data->DisplaySize.y <= 0.0f);
if (!is_minimized)
{
wd->ClearValue.color.float32[0] = drawData.clear_color.x * drawData.clear_color.w;
wd->ClearValue.color.float32[1] = drawData.clear_color.y * drawData.clear_color.w;
wd->ClearValue.color.float32[2] = drawData.clear_color.z * drawData.clear_color.w;
wd->ClearValue.color.float32[3] = drawData.clear_color.w;
FrameRender(wd, draw_data);
FramePresent(wd);
}
}
// Cleanup
if (callbacks.cleanupFunc)
{
callbacks.cleanupFunc(drawData);
}
err = vkDeviceWaitIdle(g_Device);
check_vk_result(err);
ImGui_ImplVulkan_Shutdown();
ImGui_ImplGlfw_Shutdown();
ImGui::DestroyContext();
CleanupVulkanWindow();
CleanupVulkan();
glfwDestroyWindow(window);
glfwTerminate();
return 0;
}
ImVec2 getWindowSize(GLFWwindow* window)
{
int window_width;
int window_height;
glfwGetWindowSize(window, &window_width, &window_height);
return ImVec2(window_width, window_height);
}

View File

@@ -1,17 +1,14 @@
#pragma once
#include <Windows.h> #include <Windows.h>
#include <functional>
#include "imgui.h"
#include "imgui_impl_glfw.h"
#include "imgui_impl_vulkan.h"
#include <iostream>
#define GLFW_INCLUDE_NONE #define GLFW_INCLUDE_NONE
#define GLFW_INCLUDE_VULKAN #define GLFW_INCLUDE_VULKAN
#define GLFW_EXPOSE_NATIVE_WIN32 #define GLFW_EXPOSE_NATIVE_WIN32
#include <GLFW/glfw3.h> #include <GLFW/glfw3.h>
#include <GLFW/glfw3native.h>
#include <vulkan/vulkan.h> #include "imgui.h"
class DrawData { class DrawData {
public: public:
@@ -21,518 +18,13 @@ public:
ImVec2 window_size = {}; ImVec2 window_size = {};
}; };
class ImGuiCallbacks {
public:
std::function<void(DrawData&)> initFunc = nullptr;
std::function<void(DrawData&)> drawFunc = nullptr;
std::function<void(DrawData&)> cleanupFunc = nullptr;
};
int startImgui(ImGuiCallbacks& callbacks, const char* title, int windowWidth, int windowHeight);
ImVec2 getWindowSize(GLFWwindow* window); ImVec2 getWindowSize(GLFWwindow* window);
static VkAllocationCallbacks* g_Allocator = NULL;
static VkInstance g_Instance = VK_NULL_HANDLE;
static VkPhysicalDevice g_PhysicalDevice = VK_NULL_HANDLE;
static VkDevice g_Device = VK_NULL_HANDLE;
static uint32_t g_QueueFamily = (uint32_t)-1;
static VkQueue g_Queue = VK_NULL_HANDLE;
static VkDebugReportCallbackEXT g_DebugReport = VK_NULL_HANDLE;
static VkPipelineCache g_PipelineCache = VK_NULL_HANDLE;
static VkDescriptorPool g_DescriptorPool = VK_NULL_HANDLE;
static ImGui_ImplVulkanH_Window g_MainWindowData;
static int g_MinImageCount = 2;
static bool g_SwapChainRebuild = false;
static void check_vk_result(VkResult err)
{
if (err == 0)
return;
fprintf(stderr, "[vulkan] Error: VkResult = %d\n", err);
if (err < 0)
abort();
}
#ifdef IMGUI_VULKAN_DEBUG_REPORT
static VKAPI_ATTR VkBool32 VKAPI_CALL debug_report(VkDebugReportFlagsEXT flags, VkDebugReportObjectTypeEXT objectType, uint64_t object, size_t location, int32_t messageCode, const char* pLayerPrefix, const char* pMessage, void* pUserData)
{
(void)flags; (void)object; (void)location; (void)messageCode; (void)pUserData; (void)pLayerPrefix; // Unused arguments
fprintf(stderr, "[vulkan] Debug report from ObjectType: %i\nMessage: %s\n\n", objectType, pMessage);
return VK_FALSE;
}
#endif // IMGUI_VULKAN_DEBUG_REPORT
static void SetupVulkan(const char** extensions, uint32_t extensions_count)
{
VkResult err;
// Create Vulkan Instance
{
VkInstanceCreateInfo create_info = {};
create_info.sType = VK_STRUCTURE_TYPE_INSTANCE_CREATE_INFO;
create_info.enabledExtensionCount = extensions_count;
create_info.ppEnabledExtensionNames = extensions;
#ifdef IMGUI_VULKAN_DEBUG_REPORT
// Enabling validation layers
const char* layers[] = { "VK_LAYER_KHRONOS_validation" };
create_info.enabledLayerCount = 1;
create_info.ppEnabledLayerNames = layers;
// Enable debug report extension (we need additional storage, so we duplicate the user array to add our new extension to it)
const char** extensions_ext = (const char**)malloc(sizeof(const char*) * (extensions_count + 1));
memcpy(extensions_ext, extensions, extensions_count * sizeof(const char*));
extensions_ext[extensions_count] = "VK_EXT_debug_report";
create_info.enabledExtensionCount = extensions_count + 1;
create_info.ppEnabledExtensionNames = extensions_ext;
// Create Vulkan Instance
err = vkCreateInstance(&create_info, g_Allocator, &g_Instance);
check_vk_result(err);
free(extensions_ext);
// Get the function pointer (required for any extensions)
auto vkCreateDebugReportCallbackEXT = (PFN_vkCreateDebugReportCallbackEXT)vkGetInstanceProcAddr(g_Instance, "vkCreateDebugReportCallbackEXT");
IM_ASSERT(vkCreateDebugReportCallbackEXT != NULL);
// Setup the debug report callback
VkDebugReportCallbackCreateInfoEXT debug_report_ci = {};
debug_report_ci.sType = VK_STRUCTURE_TYPE_DEBUG_REPORT_CALLBACK_CREATE_INFO_EXT;
debug_report_ci.flags = VK_DEBUG_REPORT_ERROR_BIT_EXT | VK_DEBUG_REPORT_WARNING_BIT_EXT | VK_DEBUG_REPORT_PERFORMANCE_WARNING_BIT_EXT;
debug_report_ci.pfnCallback = debug_report;
debug_report_ci.pUserData = NULL;
err = vkCreateDebugReportCallbackEXT(g_Instance, &debug_report_ci, g_Allocator, &g_DebugReport);
check_vk_result(err);
#else
// Create Vulkan Instance without any debug feature
err = vkCreateInstance(&create_info, g_Allocator, &g_Instance);
check_vk_result(err);
IM_UNUSED(g_DebugReport);
#endif
}
// Select GPU
{
uint32_t gpu_count;
err = vkEnumeratePhysicalDevices(g_Instance, &gpu_count, NULL);
check_vk_result(err);
IM_ASSERT(gpu_count > 0);
VkPhysicalDevice* gpus = (VkPhysicalDevice*)malloc(sizeof(VkPhysicalDevice) * gpu_count);
err = vkEnumeratePhysicalDevices(g_Instance, &gpu_count, gpus);
check_vk_result(err);
// If a number >1 of GPUs got reported, find discrete GPU if present, or use first one available. This covers
// most common cases (multi-gpu/integrated+dedicated graphics). Handling more complicated setups (multiple
// dedicated GPUs) is out of scope of this sample.
int use_gpu = 0;
for (int i = 0; i < (int)gpu_count; i++)
{
VkPhysicalDeviceProperties properties;
vkGetPhysicalDeviceProperties(gpus[i], &properties);
if (properties.deviceType == VK_PHYSICAL_DEVICE_TYPE_DISCRETE_GPU)
{
use_gpu = i;
break;
}
}
g_PhysicalDevice = gpus[use_gpu];
free(gpus);
}
// Select graphics queue family
{
uint32_t count;
vkGetPhysicalDeviceQueueFamilyProperties(g_PhysicalDevice, &count, NULL);
VkQueueFamilyProperties* queues = (VkQueueFamilyProperties*)malloc(sizeof(VkQueueFamilyProperties) * count);
vkGetPhysicalDeviceQueueFamilyProperties(g_PhysicalDevice, &count, queues);
for (uint32_t i = 0; i < count; i++)
if (queues[i].queueFlags & VK_QUEUE_GRAPHICS_BIT)
{
g_QueueFamily = i;
break;
}
free(queues);
IM_ASSERT(g_QueueFamily != (uint32_t)-1);
}
// Create Logical Device (with 1 queue)
{
int device_extension_count = 1;
const char* device_extensions[] = { "VK_KHR_swapchain" };
const float queue_priority[] = { 1.0f };
VkDeviceQueueCreateInfo queue_info[1] = {};
queue_info[0].sType = VK_STRUCTURE_TYPE_DEVICE_QUEUE_CREATE_INFO;
queue_info[0].queueFamilyIndex = g_QueueFamily;
queue_info[0].queueCount = 1;
queue_info[0].pQueuePriorities = queue_priority;
VkDeviceCreateInfo create_info = {};
create_info.sType = VK_STRUCTURE_TYPE_DEVICE_CREATE_INFO;
create_info.queueCreateInfoCount = sizeof(queue_info) / sizeof(queue_info[0]);
create_info.pQueueCreateInfos = queue_info;
create_info.enabledExtensionCount = device_extension_count;
create_info.ppEnabledExtensionNames = device_extensions;
err = vkCreateDevice(g_PhysicalDevice, &create_info, g_Allocator, &g_Device);
check_vk_result(err);
vkGetDeviceQueue(g_Device, g_QueueFamily, 0, &g_Queue);
}
// Create Descriptor Pool
{
VkDescriptorPoolSize pool_sizes[] =
{
{ VK_DESCRIPTOR_TYPE_SAMPLER, 1000 },
{ VK_DESCRIPTOR_TYPE_COMBINED_IMAGE_SAMPLER, 1000 },
{ VK_DESCRIPTOR_TYPE_SAMPLED_IMAGE, 1000 },
{ VK_DESCRIPTOR_TYPE_STORAGE_IMAGE, 1000 },
{ VK_DESCRIPTOR_TYPE_UNIFORM_TEXEL_BUFFER, 1000 },
{ VK_DESCRIPTOR_TYPE_STORAGE_TEXEL_BUFFER, 1000 },
{ VK_DESCRIPTOR_TYPE_UNIFORM_BUFFER, 1000 },
{ VK_DESCRIPTOR_TYPE_STORAGE_BUFFER, 1000 },
{ VK_DESCRIPTOR_TYPE_UNIFORM_BUFFER_DYNAMIC, 1000 },
{ VK_DESCRIPTOR_TYPE_STORAGE_BUFFER_DYNAMIC, 1000 },
{ VK_DESCRIPTOR_TYPE_INPUT_ATTACHMENT, 1000 }
};
VkDescriptorPoolCreateInfo pool_info = {};
pool_info.sType = VK_STRUCTURE_TYPE_DESCRIPTOR_POOL_CREATE_INFO;
pool_info.flags = VK_DESCRIPTOR_POOL_CREATE_FREE_DESCRIPTOR_SET_BIT;
pool_info.maxSets = 1000 * IM_ARRAYSIZE(pool_sizes);
pool_info.poolSizeCount = (uint32_t)IM_ARRAYSIZE(pool_sizes);
pool_info.pPoolSizes = pool_sizes;
err = vkCreateDescriptorPool(g_Device, &pool_info, g_Allocator, &g_DescriptorPool);
check_vk_result(err);
}
}
// All the ImGui_ImplVulkanH_XXX structures/functions are optional helpers used by the demo.
// Your real engine/app may not use them.
static void SetupVulkanWindow(ImGui_ImplVulkanH_Window* wd, VkSurfaceKHR surface, int width, int height)
{
wd->Surface = surface;
// Check for WSI support
VkBool32 res;
vkGetPhysicalDeviceSurfaceSupportKHR(g_PhysicalDevice, g_QueueFamily, wd->Surface, &res);
if (res != VK_TRUE)
{
fprintf(stderr, "Error no WSI support on physical device 0\n");
exit(-1);
}
// Select Surface Format
const VkFormat requestSurfaceImageFormat[] = { VK_FORMAT_B8G8R8A8_UNORM, VK_FORMAT_R8G8B8A8_UNORM, VK_FORMAT_B8G8R8_UNORM, VK_FORMAT_R8G8B8_UNORM };
const VkColorSpaceKHR requestSurfaceColorSpace = VK_COLORSPACE_SRGB_NONLINEAR_KHR;
wd->SurfaceFormat = ImGui_ImplVulkanH_SelectSurfaceFormat(g_PhysicalDevice, wd->Surface, requestSurfaceImageFormat, (size_t)IM_ARRAYSIZE(requestSurfaceImageFormat), requestSurfaceColorSpace);
// Select Present Mode
#ifdef IMGUI_UNLIMITED_FRAME_RATE
VkPresentModeKHR present_modes[] = { VK_PRESENT_MODE_MAILBOX_KHR, VK_PRESENT_MODE_IMMEDIATE_KHR, VK_PRESENT_MODE_FIFO_KHR };
#else
VkPresentModeKHR present_modes[] = { VK_PRESENT_MODE_FIFO_KHR };
#endif
wd->PresentMode = ImGui_ImplVulkanH_SelectPresentMode(g_PhysicalDevice, wd->Surface, &present_modes[0], IM_ARRAYSIZE(present_modes));
//printf("[vulkan] Selected PresentMode = %d\n", wd->PresentMode);
// Create SwapChain, RenderPass, Framebuffer, etc.
IM_ASSERT(g_MinImageCount >= 2);
ImGui_ImplVulkanH_CreateOrResizeWindow(g_Instance, g_PhysicalDevice, g_Device, wd, g_QueueFamily, g_Allocator, width, height, g_MinImageCount);
}
static void CleanupVulkan()
{
vkDestroyDescriptorPool(g_Device, g_DescriptorPool, g_Allocator);
#ifdef IMGUI_VULKAN_DEBUG_REPORT
// Remove the debug report callback
auto vkDestroyDebugReportCallbackEXT = (PFN_vkDestroyDebugReportCallbackEXT)vkGetInstanceProcAddr(g_Instance, "vkDestroyDebugReportCallbackEXT");
vkDestroyDebugReportCallbackEXT(g_Instance, g_DebugReport, g_Allocator);
#endif // IMGUI_VULKAN_DEBUG_REPORT
vkDestroyDevice(g_Device, g_Allocator);
vkDestroyInstance(g_Instance, g_Allocator);
}
static void CleanupVulkanWindow()
{
ImGui_ImplVulkanH_DestroyWindow(g_Instance, g_Device, &g_MainWindowData, g_Allocator);
}
static void FrameRender(ImGui_ImplVulkanH_Window* wd, ImDrawData* draw_data)
{
VkResult err;
VkSemaphore image_acquired_semaphore = wd->FrameSemaphores[wd->SemaphoreIndex].ImageAcquiredSemaphore;
VkSemaphore render_complete_semaphore = wd->FrameSemaphores[wd->SemaphoreIndex].RenderCompleteSemaphore;
err = vkAcquireNextImageKHR(g_Device, wd->Swapchain, UINT64_MAX, image_acquired_semaphore, VK_NULL_HANDLE, &wd->FrameIndex);
if (err == VK_ERROR_OUT_OF_DATE_KHR || err == VK_SUBOPTIMAL_KHR)
{
g_SwapChainRebuild = true;
return;
}
check_vk_result(err);
ImGui_ImplVulkanH_Frame* fd = &wd->Frames[wd->FrameIndex];
{
err = vkWaitForFences(g_Device, 1, &fd->Fence, VK_TRUE, UINT64_MAX); // wait indefinitely instead of periodically checking
check_vk_result(err);
err = vkResetFences(g_Device, 1, &fd->Fence);
check_vk_result(err);
}
{
err = vkResetCommandPool(g_Device, fd->CommandPool, 0);
check_vk_result(err);
VkCommandBufferBeginInfo info = {};
info.sType = VK_STRUCTURE_TYPE_COMMAND_BUFFER_BEGIN_INFO;
info.flags |= VK_COMMAND_BUFFER_USAGE_ONE_TIME_SUBMIT_BIT;
err = vkBeginCommandBuffer(fd->CommandBuffer, &info);
check_vk_result(err);
}
{
VkRenderPassBeginInfo info = {};
info.sType = VK_STRUCTURE_TYPE_RENDER_PASS_BEGIN_INFO;
info.renderPass = wd->RenderPass;
info.framebuffer = fd->Framebuffer;
info.renderArea.extent.width = wd->Width;
info.renderArea.extent.height = wd->Height;
info.clearValueCount = 1;
info.pClearValues = &wd->ClearValue;
vkCmdBeginRenderPass(fd->CommandBuffer, &info, VK_SUBPASS_CONTENTS_INLINE);
}
// Record dear imgui primitives into command buffer
ImGui_ImplVulkan_RenderDrawData(draw_data, fd->CommandBuffer);
// Submit command buffer
vkCmdEndRenderPass(fd->CommandBuffer);
{
VkPipelineStageFlags wait_stage = VK_PIPELINE_STAGE_COLOR_ATTACHMENT_OUTPUT_BIT;
VkSubmitInfo info = {};
info.sType = VK_STRUCTURE_TYPE_SUBMIT_INFO;
info.waitSemaphoreCount = 1;
info.pWaitSemaphores = &image_acquired_semaphore;
info.pWaitDstStageMask = &wait_stage;
info.commandBufferCount = 1;
info.pCommandBuffers = &fd->CommandBuffer;
info.signalSemaphoreCount = 1;
info.pSignalSemaphores = &render_complete_semaphore;
err = vkEndCommandBuffer(fd->CommandBuffer);
check_vk_result(err);
err = vkQueueSubmit(g_Queue, 1, &info, fd->Fence);
check_vk_result(err);
}
}
static void FramePresent(ImGui_ImplVulkanH_Window* wd)
{
if (g_SwapChainRebuild)
return;
VkSemaphore render_complete_semaphore = wd->FrameSemaphores[wd->SemaphoreIndex].RenderCompleteSemaphore;
VkPresentInfoKHR info = {};
info.sType = VK_STRUCTURE_TYPE_PRESENT_INFO_KHR;
info.waitSemaphoreCount = 1;
info.pWaitSemaphores = &render_complete_semaphore;
info.swapchainCount = 1;
info.pSwapchains = &wd->Swapchain;
info.pImageIndices = &wd->FrameIndex;
VkResult err = vkQueuePresentKHR(g_Queue, &info);
if (err == VK_ERROR_OUT_OF_DATE_KHR || err == VK_SUBOPTIMAL_KHR)
{
g_SwapChainRebuild = true;
return;
}
check_vk_result(err);
wd->SemaphoreIndex = (wd->SemaphoreIndex + 1) % wd->ImageCount; // Now we can use the next set of semaphores
}
static void glfw_error_callback(int error, const char* description)
{
fprintf(stderr, "Glfw Error %d: %s\n", error, description);
}
template<typename D>
int startImgui(D& customState, void (*const initFunc)(DrawData&, D&), void (*const drawFunc)(DrawData&, D&), void (*const cleanupFunc)(DrawData&, D&), const char* title, int windowWidth, int windowHeight)
{
// Setup GLFW window
glfwSetErrorCallback(glfw_error_callback);
if (!glfwInit())
return 1;
glfwWindowHint(GLFW_CLIENT_API, GLFW_NO_API);
GLFWwindow* window = glfwCreateWindow(windowWidth, windowHeight, title, NULL, NULL);
// Setup Vulkan
if (!glfwVulkanSupported())
{
printf("GLFW: Vulkan Not Supported\n");
return 1;
}
uint32_t extensions_count = 0;
const char** extensions = glfwGetRequiredInstanceExtensions(&extensions_count);
SetupVulkan(extensions, extensions_count);
// Create Window Surface
VkSurfaceKHR surface;
VkResult err = glfwCreateWindowSurface(g_Instance, window, g_Allocator, &surface);
check_vk_result(err);
// Create Framebuffers
int w, h;
glfwGetFramebufferSize(window, &w, &h);
ImGui_ImplVulkanH_Window* wd = &g_MainWindowData;
SetupVulkanWindow(wd, surface, w, h);
// Setup Dear ImGui context
IMGUI_CHECKVERSION();
ImGui::CreateContext();
ImGuiIO& io = ImGui::GetIO(); (void)io;
//io.ConfigFlags |= ImGuiConfigFlags_NavEnableKeyboard; // Enable Keyboard Controls
//io.ConfigFlags |= ImGuiConfigFlags_NavEnableGamepad; // Enable Gamepad Controls
// Setup Dear ImGui style
ImGui::StyleColorsDark();
//ImGui::StyleColorsClassic();
// Setup Platform/Renderer backends
ImGui_ImplGlfw_InitForVulkan(window, true);
ImGui_ImplVulkan_InitInfo init_info = {};
init_info.Instance = g_Instance;
init_info.PhysicalDevice = g_PhysicalDevice;
init_info.Device = g_Device;
init_info.QueueFamily = g_QueueFamily;
init_info.Queue = g_Queue;
init_info.PipelineCache = g_PipelineCache;
init_info.DescriptorPool = g_DescriptorPool;
init_info.Subpass = 0;
init_info.MinImageCount = g_MinImageCount;
init_info.ImageCount = wd->ImageCount;
init_info.MSAASamples = VK_SAMPLE_COUNT_1_BIT;
init_info.Allocator = g_Allocator;
init_info.CheckVkResultFn = check_vk_result;
ImGui_ImplVulkan_Init(&init_info, wd->RenderPass);
// Load Fonts
// - If no fonts are loaded, dear imgui will use the default font. You can also load multiple fonts and use ImGui::PushFont()/PopFont() to select them.
// - AddFontFromFileTTF() will return the ImFont* so you can store it if you need to select the font among multiple.
// - If the file cannot be loaded, the function will return NULL. Please handle those errors in your application (e.g. use an assertion, or display an error and quit).
// - The fonts will be rasterized at a given size (w/ oversampling) and stored into a texture when calling ImFontAtlas::Build()/GetTexDataAsXXXX(), which ImGui_ImplXXXX_NewFrame below will call.
// - Read 'docs/FONTS.md' for more instructions and details.
// - Remember that in C/C++ if you want to include a backslash \ in a string literal you need to write a double backslash \\ !
//io.Fonts->AddFontDefault();
//io.Fonts->AddFontFromFileTTF("../../misc/fonts/Roboto-Medium.ttf", 16.0f);
//io.Fonts->AddFontFromFileTTF("../../misc/fonts/Cousine-Regular.ttf", 15.0f);
//io.Fonts->AddFontFromFileTTF("../../misc/fonts/DroidSans.ttf", 16.0f);
//io.Fonts->AddFontFromFileTTF("../../misc/fonts/ProggyTiny.ttf", 10.0f);
//ImFont* font = io.Fonts->AddFontFromFileTTF("c:\\Windows\\Fonts\\ArialUni.ttf", 18.0f, NULL, io.Fonts->GetGlyphRangesJapanese());
//IM_ASSERT(font != NULL);
// Our state
DrawData drawData{};
drawData.window = window;
drawData.window_handle = glfwGetWin32Window(window);
drawData.window_size = getWindowSize(window);
initFunc(drawData, customState);
// Upload Fonts
{
// Use any command queue
VkCommandPool command_pool = wd->Frames[wd->FrameIndex].CommandPool;
VkCommandBuffer command_buffer = wd->Frames[wd->FrameIndex].CommandBuffer;
err = vkResetCommandPool(g_Device, command_pool, 0);
check_vk_result(err);
VkCommandBufferBeginInfo begin_info = {};
begin_info.sType = VK_STRUCTURE_TYPE_COMMAND_BUFFER_BEGIN_INFO;
begin_info.flags |= VK_COMMAND_BUFFER_USAGE_ONE_TIME_SUBMIT_BIT;
err = vkBeginCommandBuffer(command_buffer, &begin_info);
check_vk_result(err);
ImGui_ImplVulkan_CreateFontsTexture(command_buffer);
VkSubmitInfo end_info = {};
end_info.sType = VK_STRUCTURE_TYPE_SUBMIT_INFO;
end_info.commandBufferCount = 1;
end_info.pCommandBuffers = &command_buffer;
err = vkEndCommandBuffer(command_buffer);
check_vk_result(err);
err = vkQueueSubmit(g_Queue, 1, &end_info, VK_NULL_HANDLE);
check_vk_result(err);
err = vkDeviceWaitIdle(g_Device);
check_vk_result(err);
ImGui_ImplVulkan_DestroyFontUploadObjects();
}
// Main loop
while (!glfwWindowShouldClose(window))
{
// Poll and handle events (inputs, window resize, etc.)
// You can read the io.WantCaptureMouse, io.WantCaptureKeyboard flags to tell if dear imgui wants to use your inputs.
// - When io.WantCaptureMouse is true, do not dispatch mouse input data to your main application.
// - When io.WantCaptureKeyboard is true, do not dispatch keyboard input data to your main application.
// Generally you may always pass all inputs to dear imgui, and hide them from your application based on those two flags.
glfwPollEvents();
// Resize swap chain?
if (g_SwapChainRebuild)
{
int width, height;
glfwGetFramebufferSize(window, &width, &height);
if (width > 0 && height > 0)
{
ImGui_ImplVulkan_SetMinImageCount(g_MinImageCount);
ImGui_ImplVulkanH_CreateOrResizeWindow(g_Instance, g_PhysicalDevice, g_Device, &g_MainWindowData, g_QueueFamily, g_Allocator, width, height, g_MinImageCount);
g_MainWindowData.FrameIndex = 0;
g_SwapChainRebuild = false;
}
}
// Start the Dear ImGui frame
ImGui_ImplVulkan_NewFrame();
ImGui_ImplGlfw_NewFrame();
ImGui::NewFrame();
ImVec2 prevWindowSize = getWindowSize(window);
drawData.window_size = prevWindowSize;
drawFunc(drawData, customState);
if (drawData.window_size.x != prevWindowSize.x || drawData.window_size.y != prevWindowSize.y)
{
glfwSetWindowSize(window, drawData.window_size.x, drawData.window_size.y);
}
// Rendering
ImGui::Render();
ImDrawData* draw_data = ImGui::GetDrawData();
const bool is_minimized = (draw_data->DisplaySize.x <= 0.0f || draw_data->DisplaySize.y <= 0.0f);
if (!is_minimized)
{
wd->ClearValue.color.float32[0] = drawData.clear_color.x * drawData.clear_color.w;
wd->ClearValue.color.float32[1] = drawData.clear_color.y * drawData.clear_color.w;
wd->ClearValue.color.float32[2] = drawData.clear_color.z * drawData.clear_color.w;
wd->ClearValue.color.float32[3] = drawData.clear_color.w;
FrameRender(wd, draw_data);
FramePresent(wd);
}
}
// Cleanup
cleanupFunc(drawData, customState);
err = vkDeviceWaitIdle(g_Device);
check_vk_result(err);
ImGui_ImplVulkan_Shutdown();
ImGui_ImplGlfw_Shutdown();
ImGui::DestroyContext();
CleanupVulkanWindow();
CleanupVulkan();
glfwDestroyWindow(window);
glfwTerminate();
return 0;
}
ImVec2 getWindowSize(GLFWwindow* window)
{
int window_width;
int window_height;
glfwGetWindowSize(window, &window_width, &window_height);
return ImVec2(window_width, window_height);
}

View File

@@ -175,7 +175,7 @@
</Lib> </Lib>
</ItemDefinitionGroup> </ItemDefinitionGroup>
<ItemGroup> <ItemGroup>
<ClCompile Include="ImguiBase.h" /> <ClCompile Include="ImguiBase.cpp" />
<ClCompile Include="imgui\code\imgui.cpp" /> <ClCompile Include="imgui\code\imgui.cpp" />
<ClCompile Include="imgui\code\imgui_demo.cpp" /> <ClCompile Include="imgui\code\imgui_demo.cpp" />
<ClCompile Include="imgui\code\imgui_draw.cpp" /> <ClCompile Include="imgui\code\imgui_draw.cpp" />
@@ -185,6 +185,7 @@
<ClCompile Include="imgui\code\imgui_widgets.cpp" /> <ClCompile Include="imgui\code\imgui_widgets.cpp" />
</ItemGroup> </ItemGroup>
<ItemGroup> <ItemGroup>
<ClInclude Include="ImguiBase.h" />
<ClInclude Include="imgui\headers\imconfig.h" /> <ClInclude Include="imgui\headers\imconfig.h" />
<ClInclude Include="imgui\headers\imgui.h" /> <ClInclude Include="imgui\headers\imgui.h" />
<ClInclude Include="imgui\headers\imgui_impl_glfw.h" /> <ClInclude Include="imgui\headers\imgui_impl_glfw.h" />

View File

@@ -21,7 +21,7 @@
</Filter> </Filter>
</ItemGroup> </ItemGroup>
<ItemGroup> <ItemGroup>
<ClCompile Include="ImguiBase.h"> <ClCompile Include="ImguiBase.cpp">
<Filter>Source Files</Filter> <Filter>Source Files</Filter>
</ClCompile> </ClCompile>
<ClCompile Include="imgui\code\imgui_impl_glfw.cpp"> <ClCompile Include="imgui\code\imgui_impl_glfw.cpp">
@@ -71,6 +71,9 @@
<ClInclude Include="imgui\headers\imstb_truetype.h"> <ClInclude Include="imgui\headers\imstb_truetype.h">
<Filter>Header Files\imgui</Filter> <Filter>Header Files\imgui</Filter>
</ClInclude> </ClInclude>
<ClInclude Include="ImguiBase.h">
<Filter>Header Files</Filter>
</ClInclude>
</ItemGroup> </ItemGroup>
<ItemGroup> <ItemGroup>
<Font Include="Montserrat-Regular.ttf"> <Font Include="Montserrat-Regular.ttf">

View File

@@ -11,8 +11,11 @@
int main() int main()
{ {
ApplicationData applicationData{}; ApplicationData applicationData{};
ImGuiCallbacks callbacks{};
startImgui<ApplicationData>(applicationData, init, draw, nullptr, "Node Test", 1280, 720); callbacks.initFunc = std::bind(init, std::placeholders::_1, applicationData);
callbacks.drawFunc = std::bind(draw, std::placeholders::_1, applicationData);
startImgui(callbacks, "Node Test", 1280, 720);
} }
/// <summary> /// <summary>