summaryrefslogtreecommitdiffstats
path: root/examples/example-base
diff options
context:
space:
mode:
authorJay Kwak <82421531+jkwak-work@users.noreply.github.com>2025-07-21 19:15:07 -0700
committerGitHub <noreply@github.com>2025-07-21 19:15:07 -0700
commit13dd01489efd89268d15751c5299e5783015bbcd (patch)
tree13208541f8838a16e69d38645f7eebfc76326394 /examples/example-base
parenta77d22bf3bfc3098b01e4886c3a63fb751edc704 (diff)
Add command-line arguments to examples (#7835)
* Add command-line arguments to triangle example - Add -h/--help flag to show usage information - Add -api flag to select rendering API (d3d11 < /dev/null | d3d12|vulkan|metal|cpu|cuda|webgpu) - Platform-specific API availability (D3D on Windows, Metal on macOS) - Output works for both console and WIN32 applications
Diffstat (limited to 'examples/example-base')
-rw-r--r--examples/example-base/example-base.cpp12
-rw-r--r--examples/example-base/example-base.h7
-rw-r--r--examples/example-base/test-base.cpp190
-rw-r--r--examples/example-base/test-base.h9
4 files changed, 214 insertions, 4 deletions
diff --git a/examples/example-base/example-base.cpp b/examples/example-base/example-base.cpp
index 9759e9f42..95864b0af 100644
--- a/examples/example-base/example-base.cpp
+++ b/examples/example-base/example-base.cpp
@@ -1,5 +1,7 @@
#include "example-base.h"
+#include "slang.h"
+
#include <chrono>
#ifdef _WIN32
@@ -23,6 +25,16 @@ Slang::Result WindowedAppBase::initializeBase(
#ifdef _DEBUG
deviceDesc.enableValidation = true;
#endif
+
+ slang::CompilerOptionEntry slangOptions[] = {
+ {slang::CompilerOptionName::EmitSpirvDirectly, {slang::CompilerOptionValueKind::Int, 1}},
+ {slang::CompilerOptionName::DebugInformation,
+ {slang::CompilerOptionValueKind::Int, SLANG_DEBUG_INFO_LEVEL_STANDARD}}};
+ deviceDesc.slang.compilerOptionEntries = slangOptions;
+ // When in test mode, don't include debug information to avoid altering hash values during
+ // testing Otherwise, include debug information for better debugging experience
+ deviceDesc.slang.compilerOptionEntryCount = isTestMode() ? 1 : 2;
+
gDevice = getRHI()->createDevice(deviceDesc);
if (!gDevice)
{
diff --git a/examples/example-base/example-base.h b/examples/example-base/example-base.h
index 06970aaba..fbb54748a 100644
--- a/examples/example-base/example-base.h
+++ b/examples/example-base/example-base.h
@@ -136,6 +136,13 @@ int innerMain(int argc, char** argv)
TApp app;
app.parseOption(argc, argv);
+
+ if (app.shouldShowHelp())
+ {
+ app.printUsage(argc > 0 ? argv[0] : "example");
+ return 0;
+ }
+
if (SLANG_FAILED(app.initialize()))
{
return -1;
diff --git a/examples/example-base/test-base.cpp b/examples/example-base/test-base.cpp
index bfbfd6b15..3ab6fe54b 100644
--- a/examples/example-base/test-base.cpp
+++ b/examples/example-base/test-base.cpp
@@ -5,12 +5,111 @@
// include ordering sensitive
# include <windows.h>
# include <shellapi.h>
+# include <io.h>
+# include <fcntl.h>
// clang-format on
+#include <iostream>
+#include <string>
+#endif
+
+static rhi::DeviceType parseApiString(const char* apiStr)
+{
+ static const struct
+ {
+ const char* name;
+ rhi::DeviceType type;
+ } apiTable[] = {
+#ifdef _WIN32
+ {"d3d11", rhi::DeviceType::D3D11},
+ {"d3d12", rhi::DeviceType::D3D12},
+#endif
+ {"vulkan", rhi::DeviceType::Vulkan},
+#ifdef __APPLE__
+ {"metal", rhi::DeviceType::Metal},
+#endif
+ {"cpu", rhi::DeviceType::CPU},
+ {"cuda", rhi::DeviceType::CUDA},
+ {"webgpu", rhi::DeviceType::WGPU}};
+
+ for (auto& api : apiTable)
+ {
+ if (strcmp(apiStr, api.name) == 0)
+ {
+ return api.type;
+ }
+ }
+ return rhi::DeviceType::Default; // Invalid/unknown
+}
+
+#ifdef _WIN32
+static std::string wstringToString(const std::wstring& wstr)
+{
+ char buffer[256];
+ wcstombs(buffer, wstr.c_str(), sizeof(buffer));
+ return std::string(buffer);
+}
+#endif
+
+// Simple output function that works for both console and WIN32 apps
+static void printOutput(const char* text)
+{
+ printf("%s", text);
+#ifdef _WIN32
+ OutputDebugStringA(text);
+#endif
+}
+
+#ifdef _WIN32
+// For WIN32 apps, allocate a console window to show help text
+static bool ensureConsoleVisible()
+{
+ // Check if we already have a console (running from cmd.exe or already allocated)
+ if (GetConsoleWindow() != NULL)
+ {
+ // We already have a console, just use it
+ return false; // No new window created
+ }
+
+ // Check if stdout is connected to a console or redirected
+ HANDLE hStdOut = GetStdHandle(STD_OUTPUT_HANDLE);
+ if (hStdOut != INVALID_HANDLE_VALUE)
+ {
+ DWORD fileType = GetFileType(hStdOut);
+ switch (fileType)
+ {
+ case FILE_TYPE_CHAR: // Console
+ case FILE_TYPE_DISK: // File redirection
+ case FILE_TYPE_PIPE: // Pipe redirection
+ // Output goes to console or is redirected, don't create a console
+ return false;
+ }
+ // FILE_TYPE_UNKNOWN or other cases: proceed to create console
+ }
+
+ // No console exists and output isn't redirected, so allocate a new one
+ if (AllocConsole())
+ {
+ // Redirect stdout to the console
+ freopen_s((FILE**)stdout, "CONOUT$", "w", stdout);
+ freopen_s((FILE**)stderr, "CONOUT$", "w", stderr);
+ freopen_s((FILE**)stdin, "CONIN$", "r", stdin);
+
+ // Make sure cout, wcout, cin, wcin, wcerr, cerr, wclog and clog
+ // point to console as well
+ std::ios::sync_with_stdio(true);
+
+ // Set console title
+ SetConsoleTitleA("Triangle Example - Help");
+ return true; // New window was created
+ }
+
+ return false; // Failed to create console
+}
#endif
int TestBase::parseOption(int argc, char** argv)
{
- // We only make the parse in a very loose way for only extracting the test option.
+ // Parse command line arguments for help, API selection, and test mode
#ifdef _WIN32
wchar_t** szArglist;
szArglist = CommandLineToArgvW(GetCommandLineW(), &argc);
@@ -19,13 +118,44 @@ int TestBase::parseOption(int argc, char** argv)
for (int i = 0; i < argc; i++)
{
#ifdef _WIN32
- if (wcscmp(szArglist[i], L"--test-mode") == 0)
+ std::string arg = wstringToString(szArglist[i]);
#else
- if (strcmp(argv[i], "--test-mode") == 0)
+ std::string arg = argv[i];
#endif
+
+ if (arg == "--test-mode" || arg == "-test-mode")
{
m_isTestMode = true;
}
+ else if (arg == "-h" || arg == "--help")
+ {
+ m_showHelp = true;
+ }
+ else if ((arg == "-api" || arg == "--api") && i + 1 < argc)
+ {
+ i++; // Move to the next argument which should be the API name
+#ifdef _WIN32
+ std::string apiStr = wstringToString(szArglist[i]);
+#else
+ std::string apiStr = argv[i];
+#endif
+
+ rhi::DeviceType newType = parseApiString(apiStr.c_str());
+ if (newType != rhi::DeviceType::Default)
+ {
+ m_deviceType = newType;
+ }
+ else
+ {
+#ifdef _WIN32
+ // For WIN32 apps, ensure we have a visible console window for errors too
+ ensureConsoleVisible();
+#endif
+ std::string errorMsg = "Unknown API: " + apiStr + "\n";
+ printOutput(errorMsg.c_str());
+ m_showHelp = true;
+ }
+ }
}
#ifdef _WIN32
@@ -64,3 +194,57 @@ void TestBase::printEntrypointHashes(
}
}
}
+
+void TestBase::printUsage(const char* programName) const
+{
+#ifdef _WIN32
+ // For WIN32 apps, ensure we have a visible console window
+ bool createdNewWindow = ensureConsoleVisible();
+#endif
+
+ // Use printOutput to ensure output appears in both console and debug output (for WIN32 apps)
+ std::string usage = "Usage: " + std::string(programName) + " [options]\n\n";
+ printOutput(usage.c_str());
+ printOutput("Options:\n");
+ printOutput(" -h, --help Show this help message\n");
+ printOutput(" -api (");
+#ifdef _WIN32
+ printOutput("d3d11|d3d12|");
+#endif
+ printOutput("vulkan");
+#ifdef __APPLE__
+ printOutput("|metal");
+#endif
+ printOutput("|cpu|cuda|webgpu)\n");
+#ifdef _WIN32
+ printOutput(" Use a given rendering API (Default: d3d12)\n");
+#elif defined(__APPLE__)
+ printOutput(" Use a given rendering API (Default: metal)\n");
+#else
+ printOutput(" Use a given rendering API (Default: vulkan)\n");
+#endif
+ printOutput(" -test-mode Print hash values of compiled shader entry points "
+ "and skip rendering\n");
+ printOutput("\n");
+ printOutput("Supported APIs:\n");
+#ifdef _WIN32
+ printOutput(" d3d11 - Direct3D 11\n");
+ printOutput(" d3d12 - Direct3D 12\n");
+#endif
+ printOutput(" vulkan - Vulkan\n");
+#ifdef __APPLE__
+ printOutput(" metal - Metal (macOS/iOS)\n");
+#endif
+ printOutput(" cpu - CPU execution\n");
+ printOutput(" cuda - CUDA\n");
+ printOutput(" webgpu - WebGPU\n");
+
+#ifdef _WIN32
+ // For WIN32 apps, only prompt if we created a new console window
+ if (createdNewWindow)
+ {
+ printOutput("\nPress Enter to continue or close this window...\n");
+ getchar(); // Wait for user input
+ }
+#endif
+}
diff --git a/examples/example-base/test-base.h b/examples/example-base/test-base.h
index 293c9f0d8..2705baa26 100644
--- a/examples/example-base/test-base.h
+++ b/examples/example-base/test-base.h
@@ -2,6 +2,7 @@
#include "core/slang-string-util.h"
#include "slang-com-ptr.h"
+#include "slang-rhi.h"
#include "slang.h"
using Slang::ComPtr;
@@ -10,7 +11,7 @@ class TestBase
{
public:
- // Parses command line options. This example only has one option for testing purpose.
+ // Parses command line options including help, API selection, and test mode.
int parseOption(int argc, char** argv);
void printEntrypointHashes(
@@ -19,8 +20,14 @@ public:
ComPtr<slang::IComponentType>& composedProgram);
bool isTestMode() const { return m_isTestMode; }
+ bool shouldShowHelp() const { return m_showHelp; }
+ rhi::DeviceType getDeviceType() const { return m_deviceType; }
+
+ void printUsage(const char* programName) const;
private:
bool m_isTestMode = false;
+ bool m_showHelp = false;
+ rhi::DeviceType m_deviceType = rhi::DeviceType::Default;
uint64_t m_globalCounter = 0;
};