summaryrefslogtreecommitdiffstats
path: root/examples/nv-aftermath-example/main.cpp
diff options
context:
space:
mode:
authorEllie Hermaszewska <ellieh@nvidia.com>2024-10-29 14:49:26 +0800
committerGitHub <noreply@github.com>2024-10-29 14:49:26 +0800
commitf65d756bff8d4c5cbc15bd0322a2ae8e6b896a21 (patch)
treeea1d61342cd29368e19135000ec2948813096205 /examples/nv-aftermath-example/main.cpp
parenta729c15e9dce9f5116a38afc66329ab2ca4cea54 (diff)
format
* format * Minor test fixes * enable checking cpp format in ci
Diffstat (limited to 'examples/nv-aftermath-example/main.cpp')
-rw-r--r--examples/nv-aftermath-example/main.cpp264
1 files changed, 160 insertions, 104 deletions
diff --git a/examples/nv-aftermath-example/main.cpp b/examples/nv-aftermath-example/main.cpp
index 9850586c8..67754a911 100644
--- a/examples/nv-aftermath-example/main.cpp
+++ b/examples/nv-aftermath-example/main.cpp
@@ -1,16 +1,15 @@
// main.cpp
-#include "slang.h"
-#include "slang-gfx.h"
-#include "gfx-util/shader-cursor.h"
-#include "tools/platform/window.h"
-#include "slang-com-ptr.h"
#include "../../source/core/slang-io.h"
-#include "source/core/slang-basic.h"
-#include "examples/example-base/example-base.h"
-
#include "GFSDK_Aftermath.h"
#include "GFSDK_Aftermath_GpuCrashDump.h"
+#include "examples/example-base/example-base.h"
+#include "gfx-util/shader-cursor.h"
+#include "slang-com-ptr.h"
+#include "slang-gfx.h"
+#include "slang.h"
+#include "source/core/slang-basic.h"
+#include "tools/platform/window.h"
using namespace gfx;
using namespace Slang;
@@ -21,10 +20,10 @@ static const ExampleResources resourceBase("nv-aftermath-example");
//
// This examples purpose is to show how to use the aftermath SDK to capture
// a crash dump.
-//
+//
// * [nsight aftermath](https://developer.nvidia.com/nsight-aftermath)
-//
-// In addition it uses obfuscation and source maps to allow source level
+//
+// In addition it uses obfuscation and source maps to allow source level
// debugging via aftermath even with obfuscation.
//
// * [obfuscation](https://github.com/shader-slang/slang/blob/master/docs/user-guide/a1-03-obfuscation.md)
@@ -37,29 +36,28 @@ struct Vertex
};
static const int kVertexCount = 3;
-static const Vertex kVertexData[kVertexCount] =
-{
- { { 0, 0, 0.5 }, { 1, 0, 0 } },
- { { 0, 1, 0.5 }, { 0, 0, 1 } },
- { { 1, 0, 0.5 }, { 0, 1, 0 } },
+static const Vertex kVertexData[kVertexCount] = {
+ {{0, 0, 0.5}, {1, 0, 0}},
+ {{0, 1, 0.5}, {0, 0, 1}},
+ {{1, 0, 0.5}, {0, 1, 0}},
};
struct AftermathCrashExample : public WindowedAppBase
{
void diagnoseIfNeeded(slang::IBlob* diagnosticsBlob);
-
- gfx::Result loadShaderProgram( gfx::IDevice* device, gfx::IShaderProgram** outProgram);
-
+
+ gfx::Result loadShaderProgram(gfx::IDevice* device, gfx::IShaderProgram** outProgram);
+
Slang::Result initialize();
virtual void renderFrame(int frameBufferIndex) override;
-
+
void onAftermathCrash(const void* data, const uint32_t dataSizeInBytes);
void onAftermathDebugInfo(const void* pGpuCrashDump, const uint32_t gpuCrashDumpSize);
void onAftermathCrashDescription(PFN_GFSDK_Aftermath_AddGpuCrashDumpDescription description);
-
+
void onAftermathMarker(const void* pMarker, void** resolvedMarkerData, uint32_t* markerSize);
// Create accessors so we don't have to use g prefixed variables.
@@ -69,12 +67,15 @@ struct AftermathCrashExample : public WindowedAppBase
gfx::ISwapchain* getSwapChain() { return gSwapchain; }
gfx::IRenderPassLayout* getRenderPassLayout() { return gRenderPass; }
Slang::List<Slang::ComPtr<gfx::IFramebuffer>>& getFrameBuffers() { return gFramebuffers; }
- Slang::List<Slang::ComPtr<gfx::ITransientResourceHeap>>& getTransientHeaps() { return gTransientHeaps; }
+ Slang::List<Slang::ComPtr<gfx::ITransientResourceHeap>>& getTransientHeaps()
+ {
+ return gTransientHeaps;
+ }
ComPtr<gfx::IPipelineState> m_pipelineState;
ComPtr<gfx::IBufferResource> m_vertexBuffer;
- /// A counter such that we can make aftermath dump file names unique
+ /// A counter such that we can make aftermath dump file names unique
std::atomic<int> m_uniqueId = 0;
};
@@ -94,13 +95,15 @@ void AftermathCrashExample::onAftermathCrash(const void* data, const uint32_t da
// Dump out as a file
Slang::StringBuilder filename;
filename << "aftermath-dump-" << id << ".bin";
-
+
File::writeAllBytes(filename, data, dataSizeInBytes);
-
- //SLANG_BREAKPOINT(0);
+
+ // SLANG_BREAKPOINT(0);
}
-void AftermathCrashExample::onAftermathDebugInfo(const void* gpuCrashDump, const uint32_t gpuCrashDumpSize)
+void AftermathCrashExample::onAftermathDebugInfo(
+ const void* gpuCrashDump,
+ const uint32_t gpuCrashDumpSize)
{
const auto id = m_uniqueId++;
@@ -111,41 +114,49 @@ void AftermathCrashExample::onAftermathDebugInfo(const void* gpuCrashDump, const
File::writeAllBytes(filename, gpuCrashDump, gpuCrashDumpSize);
}
-void AftermathCrashExample::onAftermathCrashDescription(PFN_GFSDK_Aftermath_AddGpuCrashDumpDescription description)
+void AftermathCrashExample::onAftermathCrashDescription(
+ PFN_GFSDK_Aftermath_AddGpuCrashDumpDescription description)
{
// Ignore for now
}
-void AftermathCrashExample::onAftermathMarker(const void* marker, void** resolvedMarkerData, uint32_t* markerSize)
+void AftermathCrashExample::onAftermathMarker(
+ const void* marker,
+ void** resolvedMarkerData,
+ uint32_t* markerSize)
{
// Ignore for now
}
struct FileSystemEntry
{
- SlangPathType type; ///< The type of the entry
- String path; ///< The path to the entr
+ SlangPathType type; ///< The type of the entry
+ String path; ///< The path to the entr
};
struct CompileProduct
{
- String fileName; ///< The filename to write the compile product out to
- ComPtr<ISlangBlob> blob; ///< A blob holding the products contents
+ String fileName; ///< The filename to write the compile product out to
+ ComPtr<ISlangBlob> blob; ///< A blob holding the products contents
};
-/* Currently the mechanism to access the contents of a compilation that might consist of many products is through
-representing the contents as a "file system".
+/* Currently the mechanism to access the contents of a compilation that might consist of many
+products is through representing the contents as a "file system".
-The file system is just a somewhat convenient/simple in memory representation of the compilation products.
+The file system is just a somewhat convenient/simple in memory representation of the compilation
+products.
This function transverses the file system and adds everything found into outEntries.
*/
-static SlangResult _findFileSystemContents(ISlangFileSystemExt* fileSystem, const char* rootPath, List<FileSystemEntry>& outEntries)
+static SlangResult _findFileSystemContents(
+ ISlangFileSystemExt* fileSystem,
+ const char* rootPath,
+ List<FileSystemEntry>& outEntries)
{
{
SlangPathType type;
SLANG_RETURN_ON_FAIL(fileSystem->getPathType(rootPath, &type));
- outEntries.add(FileSystemEntry{ type, rootPath });
+ outEntries.add(FileSystemEntry{type, rootPath});
}
// A context used to hold state, when using enumeratePathContents
@@ -162,16 +173,18 @@ static SlangResult _findFileSystemContents(ISlangFileSystemExt* fileSystem, cons
// If it's a directory we want to traverse it's contents
if (entry.type == SLANG_PATH_TYPE_DIRECTORY)
{
- Context context{outEntries, entry.path };
+ Context context{outEntries, entry.path};
- fileSystem->enumeratePathContents(entry.path.getBuffer(),
- [](SlangPathType pathType, const char* name, void* userData) -> void {
+ fileSystem->enumeratePathContents(
+ entry.path.getBuffer(),
+ [](SlangPathType pathType, const char* name, void* userData) -> void
+ {
Context* context = reinterpret_cast<Context*>(userData);
const String path = Path::simplify(Path::combine(context->path, name));
context->entries.add({pathType, path});
- },
+ },
&context);
}
}
@@ -179,14 +192,18 @@ static SlangResult _findFileSystemContents(ISlangFileSystemExt* fileSystem, cons
return SLANG_OK;
}
-/* This function takes a compile results file system, and finds items that should be written out.
+/* This function takes a compile results file system, and finds items that should be written out.
-This is somewhat complicated because the names of products from different compilations might have the same names.
-So a "prefix" is passed in, and for files that don't have unique names, they are uniqified via the prefix.
+This is somewhat complicated because the names of products from different compilations might have
+the same names. So a "prefix" is passed in, and for files that don't have unique names, they are
+uniqified via the prefix.
-The same product may appear in multiple compilations, for example obfuscated source maps so a product is not added
-if there is already a product with the same name */
-static SlangResult _addCompileProducts(ISlangFileSystemExt* fileSystem, const char* prefix, List<CompileProduct>& ioProducts)
+The same product may appear in multiple compilations, for example obfuscated source maps so a
+product is not added if there is already a product with the same name */
+static SlangResult _addCompileProducts(
+ ISlangFileSystemExt* fileSystem,
+ const char* prefix,
+ List<CompileProduct>& ioProducts)
{
List<FileSystemEntry> fileSystemEntries;
SLANG_RETURN_ON_FAIL(_findFileSystemContents(fileSystem, ".", fileSystemEntries));
@@ -201,17 +218,18 @@ static SlangResult _addCompileProducts(ISlangFileSystemExt* fileSystem, const ch
const auto ext = Path::getPathExt(fileSystemEntry.path);
String outFileName;
-
+
// Some filenames need special handling, and their names are already unique
- // Others will be the same between differen fileSystem that represent the
- // compilation products.
+ // Others will be the same between differen fileSystem that represent the
+ // compilation products.
//
// Source maps that are obfuscated are unique.
{
String inFileName = Path::getFileNameWithoutExt(fileSystemEntry.path);
-
+
// If it's an obfuscated source map, it's name is already unique (it includes the hash)
- const bool isUniqueName = (ext == toSlice("map") && inFileName.endsWith(toSlice("-obfuscated")));
+ const bool isUniqueName =
+ (ext == toSlice("map") && inFileName.endsWith(toSlice("-obfuscated")));
StringBuilder buf;
// If it's not a uniquename make it unique via the prefix
@@ -224,20 +242,21 @@ static SlangResult _addCompileProducts(ISlangFileSystemExt* fileSystem, const ch
buf << inFileName << "." << ext;
outFileName = buf;
}
-
+
// If we have an output filename
if (outFileName.getLength())
{
// And that filename isn't already used
- if (ioProducts.findFirstIndex([&](const CompileProduct& product) -> bool {
- return product.fileName == outFileName;
- }) < 0)
+ if (ioProducts.findFirstIndex(
+ [&](const CompileProduct& product) -> bool
+ { return product.fileName == outFileName; }) < 0)
{
ComPtr<ISlangBlob> blob;
- SLANG_RETURN_ON_FAIL(fileSystem->loadFile(fileSystemEntry.path.getBuffer(), blob.writeRef()));
+ SLANG_RETURN_ON_FAIL(
+ fileSystem->loadFile(fileSystemEntry.path.getBuffer(), blob.writeRef()));
// Add to the results
- ioProducts.add(CompileProduct{ outFileName, blob });
+ ioProducts.add(CompileProduct{outFileName, blob});
}
}
}
@@ -252,11 +271,11 @@ gfx::Result AftermathCrashExample::loadShaderProgram(
ComPtr<slang::ISession> slangSession;
slangSession = device->getSlangSession();
- // This is a little bit of a work around.
- //
+ // This is a little bit of a work around.
+ //
// We want to set some options that are only available
// via processCommandLineArguments, but we need a request to be able to set them up
- // The setting actually sets the parameters on the Linkage, so they will be used for the later
+ // The setting actually sets the parameters on the Linkage, so they will be used for the later
// actual compilation
{
ComPtr<slang::ICompileRequest> request;
@@ -264,20 +283,20 @@ gfx::Result AftermathCrashExample::loadShaderProgram(
SLANG_RETURN_ON_FAIL(slangSession->createCompileRequest(request.writeRef()));
// Turn on obfuscation
- //
+ //
// Turns on source map as the line directive, this will lead to an "emit source map"
// and no #line directives in generated source.
//
// It isn't necessary to use the "source-map" line directive mode, and just use
// #line directives, and have source locations to obfuscated source file directly embedded.
- //
+ //
// To do this replace the line below with
//
// ```
// const char* args[] = { "-obfuscate" };
// ```
- const char* args[] = { "-obfuscate", "-line-directive-mode", "source-map" };
-
+ const char* args[] = {"-obfuscate", "-line-directive-mode", "source-map"};
+
request->processCommandLineArguments(args, SLANG_COUNT_OF(args));
// Enable debug info
@@ -296,7 +315,8 @@ gfx::Result AftermathCrashExample::loadShaderProgram(
SLANG_RETURN_ON_FAIL(module->findEntryPointByName("vertexMain", vertexEntryPoint.writeRef()));
//
ComPtr<slang::IEntryPoint> fragmentEntryPoint;
- SLANG_RETURN_ON_FAIL(module->findEntryPointByName("fragmentMain", fragmentEntryPoint.writeRef()));
+ SLANG_RETURN_ON_FAIL(
+ module->findEntryPointByName("fragmentMain", fragmentEntryPoint.writeRef()));
// At this point we have a few different Slang API objects that represent
// pieces of our code: `module`, `vertexEntryPoint`, and `fragmentEntryPoint`.
@@ -348,36 +368,53 @@ gfx::Result AftermathCrashExample::loadShaderProgram(
ComPtr<ISlangBlob> code;
ComPtr<ISlangBlob> diagnostics;
- SLANG_RETURN_ON_FAIL(linkedProgram->getEntryPointCode(vertexEntryPointIndex, targetIndex, code.writeRef(), diagnostics.writeRef()));
- SLANG_RETURN_ON_FAIL(linkedProgram->getEntryPointCode(fragmentEntryPointIndex, targetIndex, code.writeRef(), diagnostics.writeRef()));
+ SLANG_RETURN_ON_FAIL(linkedProgram->getEntryPointCode(
+ vertexEntryPointIndex,
+ targetIndex,
+ code.writeRef(),
+ diagnostics.writeRef()));
+ SLANG_RETURN_ON_FAIL(linkedProgram->getEntryPointCode(
+ fragmentEntryPointIndex,
+ targetIndex,
+ code.writeRef(),
+ diagnostics.writeRef()));
}
{
- // We want to find all the compilation products. In particular we want to get the emit source map, and the obfuscated
- // source maps
+ // We want to find all the compilation products. In particular we want to get the emit
+ // source map, and the obfuscated source maps
List<CompileProduct> compileProducts;
- // The current mechanism for getting access to compilation products other than result blob/diagnostics is to
- // return it as a compilation result "file system".
+ // The current mechanism for getting access to compilation products other than result
+ // blob/diagnostics is to return it as a compilation result "file system".
ComPtr<ISlangMutableFileSystem> vertexFileSystem;
- SLANG_RETURN_ON_FAIL(linkedProgram->getResultAsFileSystem(vertexEntryPointIndex, targetIndex, vertexFileSystem.writeRef()));
+ SLANG_RETURN_ON_FAIL(linkedProgram->getResultAsFileSystem(
+ vertexEntryPointIndex,
+ targetIndex,
+ vertexFileSystem.writeRef()));
ComPtr<ISlangMutableFileSystem> fragmentFileSystem;
- SLANG_RETURN_ON_FAIL(linkedProgram->getResultAsFileSystem(fragmentEntryPointIndex, targetIndex, fragmentFileSystem.writeRef()));
+ SLANG_RETURN_ON_FAIL(linkedProgram->getResultAsFileSystem(
+ fragmentEntryPointIndex,
+ targetIndex,
+ fragmentFileSystem.writeRef()));
// Add the contents of the compile result file systems into compileProducts
- // Some products might appear in both file systems, so compileProducts is just the unique products.
- // Additionally because some products may have the same name, we pass in a "prefix" to make the products name
- // unique.
+ // Some products might appear in both file systems, so compileProducts is just the unique
+ // products. Additionally because some products may have the same name, we pass in a
+ // "prefix" to make the products name unique.
SLANG_RETURN_ON_FAIL(_addCompileProducts(vertexFileSystem, "vertex", compileProducts));
SLANG_RETURN_ON_FAIL(_addCompileProducts(fragmentFileSystem, "fragment", compileProducts));
- // Now write all of the products out
+ // Now write all of the products out
for (const auto& product : compileProducts)
{
- SLANG_RETURN_ON_FAIL(File::writeAllBytes(product.fileName, product.blob->getBufferPointer(), product.blob->getBufferSize()));
+ SLANG_RETURN_ON_FAIL(File::writeAllBytes(
+ product.fileName,
+ product.blob->getBufferPointer(),
+ product.blob->getBufferSize()));
}
}
@@ -393,36 +430,53 @@ gfx::Result AftermathCrashExample::loadShaderProgram(
return SLANG_OK;
}
-static void GFSDK_AFTERMATH_CALL _crashCallback(const void* gpuCrashDump, const uint32_t gpuCrashDumpSize, void* userData)
+static void GFSDK_AFTERMATH_CALL
+_crashCallback(const void* gpuCrashDump, const uint32_t gpuCrashDumpSize, void* userData)
{
- reinterpret_cast<AftermathCrashExample*>(userData)->onAftermathCrash(gpuCrashDump, gpuCrashDumpSize);
+ reinterpret_cast<AftermathCrashExample*>(userData)->onAftermathCrash(
+ gpuCrashDump,
+ gpuCrashDumpSize);
}
-static void GFSDK_AFTERMATH_CALL _debugInfoCallback(const void* gpuCrashDump, const uint32_t gpuCrashDumpSize, void* userData)
+static void GFSDK_AFTERMATH_CALL
+_debugInfoCallback(const void* gpuCrashDump, const uint32_t gpuCrashDumpSize, void* userData)
{
- reinterpret_cast<AftermathCrashExample*>(userData)->onAftermathDebugInfo(gpuCrashDump, gpuCrashDumpSize);
+ reinterpret_cast<AftermathCrashExample*>(userData)->onAftermathDebugInfo(
+ gpuCrashDump,
+ gpuCrashDumpSize);
}
-static void GFSDK_AFTERMATH_CALL _crashDescriptionCallback(PFN_GFSDK_Aftermath_AddGpuCrashDumpDescription addDescription, void* userData)
+static void GFSDK_AFTERMATH_CALL _crashDescriptionCallback(
+ PFN_GFSDK_Aftermath_AddGpuCrashDumpDescription addDescription,
+ void* userData)
{
reinterpret_cast<AftermathCrashExample*>(userData)->onAftermathCrashDescription(addDescription);
}
-static void GFSDK_AFTERMATH_CALL _markerCallback(const void* marker, void* pUserData, void** resolvedMarkerData, uint32_t* markerSize)
+static void GFSDK_AFTERMATH_CALL _markerCallback(
+ const void* marker,
+ void* pUserData,
+ void** resolvedMarkerData,
+ uint32_t* markerSize)
{
- reinterpret_cast<AftermathCrashExample*>(pUserData)->onAftermathMarker(marker, resolvedMarkerData, markerSize);
+ reinterpret_cast<AftermathCrashExample*>(pUserData)->onAftermathMarker(
+ marker,
+ resolvedMarkerData,
+ markerSize);
}
Slang::Result AftermathCrashExample::initialize()
{
// Defer shader debug information callbacks until an actual GPU crash dump
// is generated. Increases memory footprint.
- const uint32_t aftermathFeatureFlags = GFSDK_Aftermath_GpuCrashDumpFeatureFlags_DeferDebugInfoCallbacks;
+ const uint32_t aftermathFeatureFlags =
+ GFSDK_Aftermath_GpuCrashDumpFeatureFlags_DeferDebugInfoCallbacks;
// As per docs must be called before any device is created
GFSDK_Aftermath_EnableGpuCrashDumps(
GFSDK_Aftermath_Version_API,
- GFSDK_Aftermath_GpuCrashDumpWatchedApiFlags_DX | GFSDK_Aftermath_GpuCrashDumpWatchedApiFlags_Vulkan,
+ GFSDK_Aftermath_GpuCrashDumpWatchedApiFlags_DX |
+ GFSDK_Aftermath_GpuCrashDumpWatchedApiFlags_Vulkan,
aftermathFeatureFlags,
_crashCallback,
_debugInfoCallback,
@@ -431,14 +485,14 @@ Slang::Result AftermathCrashExample::initialize()
this);
// Set to a specific render API as needed. Valid values are...
- //
+ //
// * gfx::DeviceType::Default
// * gfx::DeviceType::Vulkan
// * gfx::DeviceType::DirectX12
// * gfx::DeviceType::DirectX11
const gfx::DeviceType deviceType = gfx::DeviceType::Default;
-
+
initializeBase("aftermath-crash-example", 1024, 768, deviceType);
auto device = getDevice();
@@ -449,14 +503,12 @@ Slang::Result AftermathCrashExample::initialize()
// First, we create an input layout:
//
InputElementDesc inputElements[] = {
- { "POSITION", 0, Format::R32G32B32_FLOAT, offsetof(Vertex, position) },
- { "COLOR", 0, Format::R32G32B32_FLOAT, offsetof(Vertex, color) },
+ {"POSITION", 0, Format::R32G32B32_FLOAT, offsetof(Vertex, position)},
+ {"COLOR", 0, Format::R32G32B32_FLOAT, offsetof(Vertex, color)},
};
- auto inputLayout = gDevice->createInputLayout(
- sizeof(Vertex),
- &inputElements[0],
- 2);
- if (!inputLayout) return SLANG_FAIL;
+ auto inputLayout = gDevice->createInputLayout(sizeof(Vertex), &inputElements[0], 2);
+ if (!inputLayout)
+ return SLANG_FAIL;
// Next we allocate a vertex buffer for our pre-initialized
// vertex data.
@@ -466,7 +518,8 @@ Slang::Result AftermathCrashExample::initialize()
vertexBufferDesc.sizeInBytes = kVertexCount * sizeof(Vertex);
vertexBufferDesc.defaultState = ResourceState::VertexBuffer;
m_vertexBuffer = device->createBufferResource(vertexBufferDesc, &kVertexData[0]);
- if (!m_vertexBuffer) return SLANG_FAIL;
+ if (!m_vertexBuffer)
+ return SLANG_FAIL;
// Now we will use our `loadShaderProgram` function to load
// the code from `shaders.slang` into the graphics API.
@@ -490,10 +543,12 @@ Slang::Result AftermathCrashExample::initialize()
return SLANG_OK;
}
-void AftermathCrashExample::renderFrame(int frameBufferIndex)
+void AftermathCrashExample::renderFrame(int frameBufferIndex)
{
- ComPtr<ICommandBuffer> commandBuffer = getTransientHeaps()[frameBufferIndex]->createCommandBuffer();
- auto renderEncoder = commandBuffer->encodeRenderCommands(gRenderPass, getFrameBuffers()[frameBufferIndex]);
+ ComPtr<ICommandBuffer> commandBuffer =
+ getTransientHeaps()[frameBufferIndex]->createCommandBuffer();
+ auto renderEncoder =
+ commandBuffer->encodeRenderCommands(gRenderPass, getFrameBuffers()[frameBufferIndex]);
gfx::Viewport viewport = {};
viewport.maxZ = 1.0f;
@@ -508,9 +563,10 @@ void AftermathCrashExample::renderFrame(int frameBufferIndex)
ShaderCursor rootCursor(rootObject);
rootCursor["Uniforms"]["modelViewProjection"].setData(
- deviceInfo.identityProjectionMatrix, sizeof(float) * 16);
+ deviceInfo.identityProjectionMatrix,
+ sizeof(float) * 16);
- // We are going to extra efforts to create a shader that we know will time
+ // We are going to extra efforts to create a shader that we know will time
// out because we *want* a GPU "crash", such we can capture via nsight aftermath.
// The failCount is just a number that is large enought to make things take too long.
int32_t failCount = 0x3fffffff;