summaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
-rw-r--r--examples/nv-aftermath-example/README.md46
-rw-r--r--examples/nv-aftermath-example/main.cpp543
-rw-r--r--examples/nv-aftermath-example/shaders.slang81
-rw-r--r--premake5.lua82
-rw-r--r--slang.h12
-rw-r--r--source/compiler-core/slang-source-loc.cpp100
-rw-r--r--source/compiler-core/slang-source-loc.h20
-rwxr-xr-xsource/slang/slang-compiler.h30
-rw-r--r--source/slang/slang-emit-source-writer.cpp2
-rw-r--r--source/slang/slang-ir-obfuscate-loc.cpp4
-rw-r--r--source/slang/slang.cpp102
-rw-r--r--tools/gfx/d3d/d3d-swapchain.h10
-rw-r--r--tools/gfx/d3d/d3d-util.cpp60
-rw-r--r--tools/gfx/d3d/d3d-util.h4
-rw-r--r--tools/gfx/d3d11/d3d11-device.cpp38
-rw-r--r--tools/gfx/d3d12/d3d12-device.cpp50
-rw-r--r--tools/gfx/d3d12/d3d12-device.h2
-rw-r--r--tools/gfx/vulkan/vk-device.cpp34
18 files changed, 1186 insertions, 34 deletions
diff --git a/examples/nv-aftermath-example/README.md b/examples/nv-aftermath-example/README.md
new file mode 100644
index 000000000..133a4359f
--- /dev/null
+++ b/examples/nv-aftermath-example/README.md
@@ -0,0 +1,46 @@
+Nsight Aftermath Crash Example
+==============================
+
+* Demonstrates use of aftermath API to capture a dump with a GPU crash
+* Uses the [obfuscation feature](https://github.com/shader-slang/slang/blob/master/docs/user-guide/a1-03-obfuscation.md)
+* Uses an `emit` source map
+* Demonstrates use of file system compile products
+* Forces a crash via time out, executing a shader that is purposefully slow
+* Can be used to capture D3D and Vulkan (change the device type in the sample)
+* When enabled GFX is built to use Aftermath it's debug layer
+ * This disables D3D debug layer, as not possible to have both enabled
+* NOTE! Will only capture Aftermath DebugInfo with a *debug* build
+ * Gfx only enables debugging info (and therefore aftermath) on *debug* builds
+
+This example is *not* enabled by default. Enabling requires requires...
+
+* Passing "--enable-aftermath=true" to the command line of `premake`.
+* Having a copy of the [Nsight aftermath SDK](https://developer.nvidia.com/nsight-aftermath) in `external/nv-aftermath` directory.
+
+On windows the following would be reasonable..
+
+```
+premake vs2019 --deps=true --enable-aftermath=true
+```
+
+Typically D3D12 debug run produces the following files...
+
+* fragment-0.dxil - Fragment DXIL
+* fragment-0.map - The emit source map, maps locations in the the fragment kernel to the obfuscated source file
+* vertex-0.dxil - Vertex DXIL
+* vertex-0.map - The emit source map, maps locations in the vertex kernel to the obfuscated source file
+* XXXX-obfuscated.map - The obfuscated source map. Will be referenced by the other source maps. Maps obfuscated locations to the original source.
+* aftermath-dump-X.bin - The Aftermath crash capture/s
+* aftermath-debug-info-X.bin - The Aftermath debug info/s
+
+Having emit source maps, can be useful as discussed in [the documentation](https://github.com/shader-slang/slang/blob/master/docs/user-guide/a1-03-obfuscation.md#emit-source-maps), but isn't a requirement. If emit source maps are disabled the source maps `fragment-0.map`/`vertex-0.map` will *not* be produced. In this scenario the mapping to the obfuscated source file is embedded into the kernel/s directly.
+
+A Vulkan run will emit "spv" files, D3D12 will emit "dxil" files and D3D11 will emit "dxbc" files.
+
+The example source describes how to switch between emit source files, and different devices.
+
+## Links
+
+* [nsight aftermath](https://developer.nvidia.com/nsight-aftermath)
+* [obfuscation](https://github.com/shader-slang/slang/blob/master/docs/user-guide/a1-03-obfuscation.md)
+* [source map](https://github.com/source-map/source-map-spec)
diff --git a/examples/nv-aftermath-example/main.cpp b/examples/nv-aftermath-example/main.cpp
new file mode 100644
index 000000000..24d59ae62
--- /dev/null
+++ b/examples/nv-aftermath-example/main.cpp
@@ -0,0 +1,543 @@
+// 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"
+
+using namespace gfx;
+using namespace Slang;
+
+// This example is based on the "triangle" sample.
+//
+// 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
+// debugging via aftermath even with obfuscation.
+//
+// * [obfuscation](https://github.com/shader-slang/slang/blob/master/docs/user-guide/a1-03-obfuscation.md)
+// * [source map](https://github.com/source-map/source-map-spec)
+
+struct Vertex
+{
+ float position[3];
+ float color[3];
+};
+
+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 } },
+};
+
+struct AftermathCrashExample : public WindowedAppBase
+{
+ void diagnoseIfNeeded(slang::IBlob* diagnosticsBlob);
+
+ 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.
+ gfx::IDevice* getDevice() { return gDevice; }
+ gfx::ICommandQueue* getQueue() { return gQueue; }
+ gfx::IFramebufferLayout* getFrameBufferLayout() { return gFramebufferLayout; }
+ 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; }
+
+ ComPtr<gfx::IPipelineState> m_pipelineState;
+ ComPtr<gfx::IBufferResource> m_vertexBuffer;
+
+ /// A counter such that we can make aftermath dump file names unique
+ std::atomic<int> m_uniqueId = 0;
+};
+
+void AftermathCrashExample::diagnoseIfNeeded(slang::IBlob* diagnosticsBlob)
+{
+ if (diagnosticsBlob != nullptr)
+ {
+ printf("%s", (const char*)diagnosticsBlob->getBufferPointer());
+ }
+}
+
+void AftermathCrashExample::onAftermathCrash(const void* data, const uint32_t dataSizeInBytes)
+{
+ // NOTE! This method can be called from *any* thread.
+ const auto id = m_uniqueId++;
+
+ // Dump out as a file
+ Slang::StringBuilder filename;
+ filename << "aftermath-dump-" << id << ".bin";
+
+ File::writeAllBytes(filename, data, dataSizeInBytes);
+
+ //SLANG_BREAKPOINT(0);
+}
+
+void AftermathCrashExample::onAftermathDebugInfo(const void* gpuCrashDump, const uint32_t gpuCrashDumpSize)
+{
+ const auto id = m_uniqueId++;
+
+ // Dump out as a file
+ Slang::StringBuilder filename;
+ filename << "aftermath-debug-info-" << id << ".bin";
+
+ File::writeAllBytes(filename, gpuCrashDump, gpuCrashDumpSize);
+}
+
+void AftermathCrashExample::onAftermathCrashDescription(PFN_GFSDK_Aftermath_AddGpuCrashDumpDescription description)
+{
+ // Ignore for now
+}
+
+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
+};
+
+struct CompileProduct
+{
+ 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".
+
+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)
+{
+ {
+ SlangPathType type;
+ SLANG_RETURN_ON_FAIL(fileSystem->getPathType(rootPath, &type));
+ outEntries.add(FileSystemEntry{ type, rootPath });
+ }
+
+ // A context used to hold state, when using enumeratePathContents
+ struct Context
+ {
+ List<FileSystemEntry>& entries; // The entries to be accumulated to
+ String path; // The path being enumerated
+ };
+
+ for (Index i = outEntries.getCount() - 1; i < outEntries.getCount(); ++i)
+ {
+ const auto& entry = outEntries[i];
+
+ // If it's a directory we want to traverse it's contents
+ if (entry.type == SLANG_PATH_TYPE_DIRECTORY)
+ {
+ Context context{outEntries, entry.path };
+
+ 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);
+ }
+ }
+
+ return SLANG_OK;
+}
+
+/* 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.
+
+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));
+
+ for (const auto& fileSystemEntry : fileSystemEntries)
+ {
+ if (fileSystemEntry.type != SLANG_PATH_TYPE_FILE)
+ {
+ continue;
+ }
+
+ 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.
+ //
+ // 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")));
+
+ StringBuilder buf;
+ // If it's not a uniquename make it unique via the prefix
+ if (!isUniqueName)
+ {
+ // Uniquify with the prefix
+ buf << prefix << "-";
+ }
+
+ 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)
+ {
+ ComPtr<ISlangBlob> blob;
+ SLANG_RETURN_ON_FAIL(fileSystem->loadFile(fileSystemEntry.path.getBuffer(), blob.writeRef()));
+
+ // Add to the results
+ ioProducts.add(CompileProduct{ outFileName, blob });
+ }
+ }
+ }
+
+ return SLANG_OK;
+}
+
+gfx::Result AftermathCrashExample::loadShaderProgram(
+ gfx::IDevice* device,
+ gfx::IShaderProgram** outProgram)
+{
+ ComPtr<slang::ISession> slangSession;
+ slangSession = device->getSlangSession();
+
+ // 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
+ // actual compilation
+ {
+ ComPtr<slang::ICompileRequest> request;
+
+ 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" };
+
+ request->processCommandLineArguments(args, SLANG_COUNT_OF(args));
+
+ // Enable debug info
+ request->setDebugInfoLevel(SLANG_DEBUG_INFO_LEVEL_MAXIMAL);
+ }
+
+ ComPtr<slang::IBlob> diagnosticsBlob;
+ slang::IModule* module = slangSession->loadModule("shaders", diagnosticsBlob.writeRef());
+ diagnoseIfNeeded(diagnosticsBlob);
+ if (!module)
+ return SLANG_FAIL;
+
+ // Find the entry points
+ ComPtr<slang::IEntryPoint> vertexEntryPoint;
+ SLANG_RETURN_ON_FAIL(module->findEntryPointByName("vertexMain", vertexEntryPoint.writeRef()));
+ //
+ ComPtr<slang::IEntryPoint> fragmentEntryPoint;
+ 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`.
+ //
+ // A single Slang module could contain many different entry points (e.g.,
+ // four vertex entry points, three fragment entry points, and two compute
+ // shaders), and before we try to generate output code for our target API
+ // we need to identify which entry points we plan to use together.
+ //
+ // Modules and entry points are both examples of *component types* in the
+ // Slang API. The API also provides a way to build a *composite* out of
+ // other pieces, and that is what we are going to do with our module
+ // and entry points.
+ //
+ Slang::List<slang::IComponentType*> componentTypes;
+ componentTypes.add(module);
+
+ // Later on when we go to extract compiled kernel code for our vertex
+ // and fragment shaders, we will need to make use of their order within
+ // the composition, so we will record the relative ordering of the entry
+ // points here as we add them.
+ int entryPointCount = 0;
+ int vertexEntryPointIndex = entryPointCount++;
+ componentTypes.add(vertexEntryPoint);
+
+ int fragmentEntryPointIndex = entryPointCount++;
+ componentTypes.add(fragmentEntryPoint);
+
+ // Actually creating the composite component type is a single operation
+ // on the Slang session, but the operation could potentially fail if
+ // something about the composite was invalid (e.g., you are trying to
+ // combine multiple copies of the same module), so we need to deal
+ // with the possibility of diagnostic output.
+ //
+ ComPtr<slang::IComponentType> linkedProgram;
+ SlangResult result = slangSession->createCompositeComponentType(
+ componentTypes.getBuffer(),
+ componentTypes.getCount(),
+ linkedProgram.writeRef(),
+ diagnosticsBlob.writeRef());
+ diagnoseIfNeeded(diagnosticsBlob);
+ SLANG_RETURN_ON_FAIL(result);
+
+ const Index targetIndex = 0;
+
+ // Trigger compilation by requesting the code.
+ // Normally gfx would compile as needed.
+ {
+ 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()));
+ }
+
+ {
+ // 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".
+
+ ComPtr<ISlangMutableFileSystem> vertexFileSystem;
+ SLANG_RETURN_ON_FAIL(linkedProgram->getResultAsFileSystem(vertexEntryPointIndex, targetIndex, vertexFileSystem.writeRef()));
+
+ ComPtr<ISlangMutableFileSystem> fragmentFileSystem;
+ 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.
+ SLANG_RETURN_ON_FAIL(_addCompileProducts(vertexFileSystem, "vertex", compileProducts));
+ SLANG_RETURN_ON_FAIL(_addCompileProducts(fragmentFileSystem, "fragment", compileProducts));
+
+ // 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()));
+ }
+ }
+
+ // Once we've described the particular composition of entry points
+ // that we want to compile, we defer to the graphics API layer
+ // to extract compiled kernel code and load it into the API-specific
+ // program representation.
+ //
+ gfx::IShaderProgram::Desc programDesc = {};
+ programDesc.slangGlobalScope = linkedProgram;
+ SLANG_RETURN_ON_FAIL(device->createProgram(programDesc, outProgram));
+
+ return SLANG_OK;
+}
+
+static void GFSDK_AFTERMATH_CALL _crashCallback(const void* gpuCrashDump, const uint32_t gpuCrashDumpSize, void* userData)
+{
+ reinterpret_cast<AftermathCrashExample*>(userData)->onAftermathCrash(gpuCrashDump, gpuCrashDumpSize);
+}
+
+static void GFSDK_AFTERMATH_CALL _debugInfoCallback(const void* gpuCrashDump, const uint32_t gpuCrashDumpSize, void* userData)
+{
+ reinterpret_cast<AftermathCrashExample*>(userData)->onAftermathDebugInfo(gpuCrashDump, gpuCrashDumpSize);
+}
+
+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)
+{
+ 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;
+
+ // 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,
+ aftermathFeatureFlags,
+ _crashCallback,
+ _debugInfoCallback,
+ _crashDescriptionCallback,
+ _markerCallback,
+ 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();
+
+ // We will create objects needed to configur the "input assembler"
+ // (IA) stage of the D3D pipeline.
+ //
+ // First, we create an input layout:
+ //
+ InputElementDesc inputElements[] = {
+ { "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;
+
+ // Next we allocate a vertex buffer for our pre-initialized
+ // vertex data.
+ //
+ IBufferResource::Desc vertexBufferDesc;
+ vertexBufferDesc.type = IResource::Type::Buffer;
+ vertexBufferDesc.sizeInBytes = kVertexCount * sizeof(Vertex);
+ vertexBufferDesc.defaultState = ResourceState::VertexBuffer;
+ m_vertexBuffer = device->createBufferResource(vertexBufferDesc, &kVertexData[0]);
+ if (!m_vertexBuffer) return SLANG_FAIL;
+
+ // Now we will use our `loadShaderProgram` function to load
+ // the code from `shaders.slang` into the graphics API.
+ //
+ ComPtr<IShaderProgram> shaderProgram;
+ SLANG_RETURN_ON_FAIL(loadShaderProgram(device, shaderProgram.writeRef()));
+
+ // Following the D3D12/Vulkan style of API, we need a pipeline state object
+ // (PSO) to encapsulate the configuration of the overall graphics pipeline.
+ //
+ GraphicsPipelineStateDesc desc;
+ desc.inputLayout = inputLayout;
+ desc.program = shaderProgram;
+ desc.framebufferLayout = getFrameBufferLayout();
+ auto pipelineState = device->createGraphicsPipelineState(desc);
+ if (!pipelineState)
+ return SLANG_FAIL;
+
+ m_pipelineState = pipelineState;
+
+ return SLANG_OK;
+}
+
+void AftermathCrashExample::renderFrame(int frameBufferIndex)
+{
+ ComPtr<ICommandBuffer> commandBuffer = getTransientHeaps()[frameBufferIndex]->createCommandBuffer();
+ auto renderEncoder = commandBuffer->encodeRenderCommands(gRenderPass, getFrameBuffers()[frameBufferIndex]);
+
+ gfx::Viewport viewport = {};
+ viewport.maxZ = 1.0f;
+ viewport.extentX = (float)windowWidth;
+ viewport.extentY = (float)windowHeight;
+ renderEncoder->setViewportAndScissor(viewport);
+
+ auto rootObject = renderEncoder->bindPipeline(m_pipelineState);
+
+ auto deviceInfo = getDevice()->getDeviceInfo();
+
+ ShaderCursor rootCursor(rootObject);
+
+ rootCursor["Uniforms"]["modelViewProjection"].setData(
+ deviceInfo.identityProjectionMatrix, sizeof(float) * 16);
+
+ // 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;
+ rootCursor["Uniforms"]["failCount"].setData(&failCount, sizeof(failCount));
+
+ // We also need to set up a few pieces of fixed-function pipeline
+ // state that are not bound by the pipeline state above.
+ //
+ renderEncoder->setVertexBuffer(0, m_vertexBuffer);
+ renderEncoder->setPrimitiveTopology(PrimitiveTopology::TriangleList);
+
+ // Finally, we are ready to issue a draw call for a single triangle.
+ //
+ renderEncoder->draw(3);
+ renderEncoder->endEncoding();
+ commandBuffer->close();
+ getQueue()->executeCommandBuffer(commandBuffer);
+
+ // With that, we are done drawing for one frame, and ready for the next.
+ //
+ getSwapChain()->present();
+
+ // If the id changes means we have a capture and so can quit.
+ // On D3D11, the first present *doesn't* appear to crash.
+ if (m_uniqueId != 0)
+ {
+ platform::Application::quit();
+ }
+}
+
+// This macro instantiates an appropriate main function to
+// run the application defined above.
+PLATFORM_UI_MAIN(innerMain<AftermathCrashExample>)
diff --git a/examples/nv-aftermath-example/shaders.slang b/examples/nv-aftermath-example/shaders.slang
new file mode 100644
index 000000000..61588dd49
--- /dev/null
+++ b/examples/nv-aftermath-example/shaders.slang
@@ -0,0 +1,81 @@
+// This shader is purposefully designed to be so slow it will cause a GPU timeout/crash.
+
+// Uniform data to be passed from application -> shader.
+cbuffer Uniforms
+{
+ float4x4 modelViewProjection;
+
+ // We want to make things fail so we can get an aftermath capture,
+ // so lets have a count that makes things really slow.
+ int failCount;
+}
+
+// Per-vertex attributes to be assembled from bound vertex buffers.
+struct AssembledVertex
+{
+ float3 position : POSITION;
+ float3 color : COLOR;
+};
+
+// Output of the vertex shader, and input to the fragment shader.
+struct CoarseVertex
+{
+ float3 color;
+};
+
+// Output of the fragment shader
+struct Fragment
+{
+ float4 color;
+};
+
+// Vertex Shader
+
+struct VertexStageOutput
+{
+ CoarseVertex coarseVertex : CoarseVertex;
+ float4 sv_position : SV_Position;
+};
+
+[shader("vertex")]
+VertexStageOutput vertexMain(
+ AssembledVertex assembledVertex)
+{
+ VertexStageOutput output;
+
+ float3 position = assembledVertex.position;
+ float3 color = assembledVertex.color;
+
+ output.coarseVertex.color = color;
+ output.sv_position = mul(modelViewProjection, float4(position, 1.0));
+
+ return output;
+}
+
+// Fragment Shader
+
+[shader("fragment")]
+float4 fragmentMain(
+ CoarseVertex coarseVertex : CoarseVertex) : SV_Target
+{
+ float3 color = coarseVertex.color;
+
+ float factor = 0.0f;
+
+ // Waste lots of cycles
+ for (int i = 0; i < failCount; ++i)
+ {
+ factor += 1.0e-20 * sin(float(i & 0xffff));
+ factor += 1.0e-21 * cos(float(i & 0xfff) + 1.0);
+ factor += 1.0e-8f * tan(float(i & 0xfffff));
+ }
+
+ factor = abs(factor);
+
+ while (factor < 0.25)
+ {
+ factor += factor;
+ }
+
+ return float4(color, 1.0) * factor;
+}
diff --git a/premake5.lua b/premake5.lua
index edcd56b4e..c7ed254cb 100644
--- a/premake5.lua
+++ b/premake5.lua
@@ -220,6 +220,14 @@ newoption {
allowed = { { "true", "True"}, { "false", "False" } }
}
+newoption {
+ trigger = "enable-aftermath",
+ description = "(Optional) Enable aftermath in GFX, and add aftermath crash example to project",
+ value = "bool",
+ default = "false",
+ allowed = { { "true", "True"}, { "false", "False" } }
+}
+
buildLocation = _OPTIONS["build-location"]
executeBinary = (_OPTIONS["execute-binary"] == "true")
buildGlslang = (_OPTIONS["build-glslang"] == "true")
@@ -236,6 +244,7 @@ deployGLSLang = (_OPTIONS["deploy-slang-glslang"] == "true")
fullDebugValidation = (_OPTIONS["full-debug-validation"] == "true")
enableAsan = (_OPTIONS["enable-asan"] == "true")
dxOnVk = (_OPTIONS["dx-on-vk"] == "true")
+enableAftermath = (_OPTIONS["enable-aftermath"] == "true")
-- If stdlib embedding is enabled, disable stdlib source embedding by default
disableStdlibSource = enableEmbedStdLib
@@ -245,6 +254,17 @@ if enableEmbedStdLib and _OPTIONS["disable-stdlib-source"] ~= nil then
disableStdlibSource = (_OPTIONS["disable-stdlib-source"] == "true")
end
+if enableAftermath then
+ aftermathPath = "external/nv-aftermath"
+
+ if not os.isfile(path.join(aftermathPath, "nsight-aftermath-usage-guidelines.txt")) then
+ print("external/nv-aftermath directory must hold aftermath SDK")
+ os.exit(0)
+ end
+
+ printf("Enabled aftermath")
+end
+
-- Determine the target info
targetInfo = slangUtil.getTargetInfo()
@@ -809,6 +829,40 @@ example "cpu-com-example"
example "cpu-hello-world"
kind "ConsoleApp"
+if enableAftermath then
+ example "nv-aftermath-example"
+ filter {}
+
+ local aftermathIncludePath = path.join(aftermathPath, "include")
+ local aftermathLibPath = path.join(aftermathPath, "lib")
+
+ -- Add the aftermath includes
+
+ includedirs { aftermathIncludePath }
+
+ -- Add the libs directory.
+ -- Additionally we need to copy dlls that are needed for aftermath usage such that they
+ -- are available from the executable.
+
+ filter { "platforms:x86" }
+ local libPath = path.join(aftermathLibPath, "x86")
+ libdirs { libPath }
+ links { "GFSDK_Aftermath_Lib.x86" }
+
+ postbuildcommands {
+ '{COPY} "$(SolutionDir)"' .. libPath .. '/*.* "%{cfg.targetdir}"'
+ }
+
+ filter { "platforms:x64" }
+ local libPath = path.join(aftermathLibPath, "x64")
+ libdirs { libPath }
+ links { "GFSDK_Aftermath_Lib.x64" }
+
+ postbuildcommands {
+ '{COPY} "$(SolutionDir)"' .. libPath .. '/*.* "%{cfg.targetdir}"'
+ }
+end
+
-- Most of the other projects have more interesting configuration going
-- on, so let's walk through them in order of increasing complexity.
--
@@ -1024,6 +1078,34 @@ tool "gfx"
'{COPY} "' .. path.getabsolute("tools/gfx/slang.slang") .. '" "%{cfg.targetdir}"',
}
end
+
+ -- If aftermath is enabled we need a define to turn on debugging features withing GFX
+
+ if enableAftermath then
+ defines { "GFX_NV_AFTERMATH" }
+
+ local aftermathIncludePath = path.join(aftermathPath, "include")
+ local aftermathLibPath = path.join(aftermathPath, "lib")
+
+ -- Add the aftermath includes
+ includedirs { aftermathIncludePath }
+
+ -- Add the libs
+ --
+ -- We don't copy the dlls as that is something the application should do.
+
+ filter { "platforms:x86" }
+ local libPath = path.join(aftermathLibPath, "x86")
+ libdirs { libPath }
+ links { "GFSDK_Aftermath_Lib.x86" }
+
+ filter { "platforms:x64" }
+ local libPath = path.join(aftermathLibPath, "x64")
+ libdirs { libPath }
+ links { "GFSDK_Aftermath_Lib.x64" }
+
+ end
+
-- To special case that we may be building using cygwin on windows. If 'true windows' we build for dx12/vk and run the script
-- If not we assume it's a cygwin/mingw type situation and remove files that aren't appropriate
if targetInfo.isWindows then
diff --git a/slang.h b/slang.h
index bac197673..01606500b 100644
--- a/slang.h
+++ b/slang.h
@@ -4513,6 +4513,18 @@ namespace slang
IBlob** outCode,
IBlob** outDiagnostics = nullptr) = 0;
+ /** Get the compilation result as a file system.
+
+ Has the same requirements as getEntryPointCode.
+
+ The result is not written to the actual OS file system, but is made avaiable as an
+ in memory representation.
+ */
+ virtual SLANG_NO_THROW SlangResult SLANG_MCALL getResultAsFileSystem(
+ SlangInt entryPointIndex,
+ SlangInt targetIndex,
+ ISlangMutableFileSystem** outFileSystem) = 0;
+
/** Compute a hash for the entry point at `entryPointIndex` for the chosen `targetIndex`.
This computes a hash based on all the dependencies for this component type as well as the
diff --git a/source/compiler-core/slang-source-loc.cpp b/source/compiler-core/slang-source-loc.cpp
index 8710cf91f..f9014e0d8 100644
--- a/source/compiler-core/slang-source-loc.cpp
+++ b/source/compiler-core/slang-source-loc.cpp
@@ -190,12 +190,41 @@ void SourceView::addDefaultLineDirective(SourceLoc directiveLoc)
m_entries.add(entry);
}
-SlangResult _findLocWithSourceMap(SourceManager* lookupSourceManager, SourceView* sourceView, SourceLoc loc, HandleSourceLoc& outLoc)
+// Nominal-like types take into account line directives, and potentially source maps
+static bool _isNominalLike(SourceLocType type)
+{
+ return type == SourceLocType::Nominal || type == SourceLocType::Emit;
+}
+
+static bool _canFollowSourceMap(SourceFile* sourceFile, SourceLocType type)
+{
+ // If we don't have a source map we have nothing to follow
+ if (!sourceFile->getSourceMap())
+ {
+ return false;
+ }
+
+ // If it's obfuscated we can't follow if we are emitting
+ if (sourceFile->getSourceMapKind() == SourceMapKind::Obfuscated &&
+ type == SourceLocType::Emit)
+ {
+ return false;
+ }
+
+ return _isNominalLike(type);
+}
+
+static SlangResult _findLocWithSourceMap(SourceManager* lookupSourceManager, SourceView* sourceView, SourceLoc loc, SourceLocType type, HandleSourceLoc& outLoc)
{
auto sourceFile = sourceView->getSourceFile();
+ if (!_canFollowSourceMap(sourceFile, type))
+ {
+ return SLANG_E_NOT_FOUND;
+ }
+
// Hold a list of sourceFiles visited so we can't end up in a loop of lookups
- List<SourceFile*> sourceFiles;
+ ShortList<SourceFile*, 8> sourceFiles;
sourceFiles.add(sourceFile);
Index entryIndex = -1;
@@ -240,8 +269,10 @@ SlangResult _findLocWithSourceMap(SourceManager* lookupSourceManager, SourceView
sourceFiles.add(foundSourceFile);
// If it has a source map, we try and look up the current location in it's source map
- if (auto foundSourceMap = foundSourceFile->getSourceMap())
+ if (_canFollowSourceMap(foundSourceFile, type))
{
+ auto foundSourceMap = foundSourceFile->getSourceMap();
+
const auto foundEntryIndex = foundSourceMap->get().findEntry(entry.sourceLine, entry.sourceColumn);
// If we found the entry repeat the lookup
@@ -273,18 +304,30 @@ SlangResult _findLocWithSourceMap(SourceManager* lookupSourceManager, SourceView
return SLANG_OK;
}
-HandleSourceLoc SourceView::getHandleLoc(SourceLoc loc, SourceLocType type)
+
+SlangResult SourceView::_findSourceMapLoc(SourceLoc loc, SourceLocType type, HandleSourceLoc& outLoc)
{
- // If it's nominal
- if (type == SourceLocType::Nominal && m_sourceFile->getSourceMap())
+ // We only do source map lookups with nominal
+ if (!_isNominalLike(type))
{
- // TODO(JS):
- // Ideally we'd do the lookup on the "current" source manager rather than the source manager on this
- // view, which may be a parent to the current one.
- auto lookupSourceManager = m_sourceFile->getSourceManager();
+ return SLANG_E_NOT_FOUND;
+ }
- HandleSourceLoc handleLoc;
- if (SLANG_SUCCEEDED(_findLocWithSourceMap(lookupSourceManager, this, loc, handleLoc)))
+ // TODO(JS):
+ // Ideally we'd do the lookup on the "current" source manager rather than the source manager on this
+ // view, which may be a parent to the current one.
+ auto lookupSourceManager = m_sourceFile->getSourceManager();
+
+ HandleSourceLoc handleLoc;
+ SLANG_RETURN_ON_FAIL(_findLocWithSourceMap(lookupSourceManager, this, loc, type, outLoc));
+
+ return SLANG_OK;
+}
+
+HandleSourceLoc SourceView::getHandleLoc(SourceLoc loc, SourceLocType type)
+{
+ { HandleSourceLoc handleLoc;
+ if (SLANG_SUCCEEDED(_findSourceMapLoc(loc, type, handleLoc)))
{
return handleLoc;
}
@@ -307,22 +350,21 @@ HandleSourceLoc SourceView::getHandleLoc(SourceLoc loc, SourceLocType type)
HandleSourceLoc handleLoc;
handleLoc.column = columnIndex + 1;
handleLoc.line = lineIndex + 1;
-
- // Make up a default entry
- StringSlicePool::Handle pathHandle = StringSlicePool::Handle(0);
-
- // Only bother looking up the entry information if we want a 'Normal' lookup
- const int entryIndex = (type == SourceLocType::Nominal) ? findEntryIndex(loc) : -1;
- if (entryIndex >= 0)
+
+ // Only bother looking up the entry information if we want a 'Norminal'-like lookup
+ if (_isNominalLike(type))
{
- const Entry& entry = m_entries[entryIndex];
- // Adjust the line
- handleLoc.line += entry.m_lineAdjust;
- // Get the pathHandle..
- pathHandle = entry.m_pathHandle;
+ const int entryIndex = findEntryIndex(loc);
+ if (entryIndex >= 0)
+ {
+ const Entry& entry = m_entries[entryIndex];
+ // Adjust the line
+ handleLoc.line += entry.m_lineAdjust;
+ // Get the pathHandle..
+ handleLoc.pathHandle = entry.m_pathHandle;
+ }
}
- handleLoc.pathHandle = pathHandle;
return handleLoc;
}
@@ -371,6 +413,14 @@ PathInfo SourceView::getPathInfo(SourceLoc loc, SourceLocType type)
return getViewPathInfo();
}
+ {
+ HandleSourceLoc handleLoc;
+ if (SLANG_SUCCEEDED(_findSourceMapLoc(loc, type, handleLoc)))
+ {
+ return _getPathInfoFromHandle(handleLoc.pathHandle);
+ }
+ }
+
const int entryIndex = findEntryIndex(loc);
return _getPathInfoFromHandle((entryIndex >= 0) ? m_entries[entryIndex].m_pathHandle : StringSlicePool::Handle(0));
}
diff --git a/source/compiler-core/slang-source-loc.h b/source/compiler-core/slang-source-loc.h
index fafff7de6..5c78c4293 100644
--- a/source/compiler-core/slang-source-loc.h
+++ b/source/compiler-core/slang-source-loc.h
@@ -164,6 +164,16 @@ struct SourceRange
SourceLoc end;
};
+/// Source maps associated with files are could be of different uses. We use the SourceMapKind
+/// to indicate the usage.
+///
+/// If the source map is obfuscated reasonable/desirable to ignore them on emit (if we didn't we leak information,
+/// and we don't emit into the locations in the obfuscated intermediate "file").
+enum class SourceMapKind
+{
+ Normal, ///< A regular source map
+ Obfuscated, ///< Obfuscated source map
+};
// Pre-declare
struct SourceManager;
@@ -256,8 +266,11 @@ public:
/// Get the source map associated with this file. If it's set when doing
/// lookup for source locations, the source map will be used
IBoxValue<SourceMap>* getSourceMap() const { return m_sourceMap; }
+ /// Get the source map kind
+ SourceMapKind getSourceMapKind() const { return m_sourceMapKind; }
+
/// Set a source map
- void setSourceMap(IBoxValue<SourceMap>* sourceMap) { m_sourceMap = sourceMap; }
+ void setSourceMap(IBoxValue<SourceMap>* sourceMap, SourceMapKind sourceMapKind) { m_sourceMap = sourceMap; m_sourceMapKind = sourceMapKind; }
/// Ctor
SourceFile(SourceManager* sourceManager, const PathInfo& pathInfo, size_t contentSize);
@@ -281,12 +294,15 @@ public:
// If set then the locations in this file are really from locations from elsewhere,
// where the SourceMap specifies that mapping
ComPtr<IBoxValue<SourceMap>> m_sourceMap;
+ // What kind of source map it is (if there is one)
+ SourceMapKind m_sourceMapKind = SourceMapKind::Normal;
};
enum class SourceLocType
{
Nominal, ///< The normal interpretation which takes into account #line directives and source maps
Actual, ///< Ignores #line directives/source maps - and is the location as seen in the actual file
+ Emit, ///< Behaves the same as `Nominal` but ignores source maps. Used for Emit source locations.
};
// A source location in a format a human might like to see
@@ -395,6 +411,8 @@ class SourceView
/// Get the pathInfo from a string handle. If it's 0, it will return the _getPathInfo
PathInfo _getPathInfoFromHandle(StringSlicePool::Handle pathHandle) const;
+ SlangResult _findSourceMapLoc(SourceLoc loc, SourceLocType type, HandleSourceLoc& outLoc);
+
String m_viewPath; ///< Path to this view. If empty the path is the path to the SourceView
SourceLoc m_initiatingSourceLoc; ///< An optional source loc that defines where this view was initiated from. SourceLoc(0) if not defined.
diff --git a/source/slang/slang-compiler.h b/source/slang/slang-compiler.h
index ca16bfb94..7d73599ba 100755
--- a/source/slang/slang-compiler.h
+++ b/source/slang/slang-compiler.h
@@ -297,6 +297,12 @@ namespace Slang
SlangInt targetIndex,
slang::IBlob** outCode,
slang::IBlob** outDiagnostics) SLANG_OVERRIDE;
+
+ SLANG_NO_THROW SlangResult SLANG_MCALL getResultAsFileSystem(
+ SlangInt entryPointIndex,
+ SlangInt targetIndex,
+ ISlangMutableFileSystem** outFileSystem) SLANG_OVERRIDE;
+
SLANG_NO_THROW SlangResult SLANG_MCALL specialize(
slang::SpecializationArg const* specializationArgs,
SlangInt specializationArgCount,
@@ -851,6 +857,14 @@ namespace Slang
return Super::getEntryPointCode(entryPointIndex, targetIndex, outCode, outDiagnostics);
}
+ SLANG_NO_THROW SlangResult SLANG_MCALL getResultAsFileSystem(
+ SlangInt entryPointIndex,
+ SlangInt targetIndex,
+ ISlangMutableFileSystem** outFileSystem) SLANG_OVERRIDE
+ {
+ return Super::getResultAsFileSystem(entryPointIndex, targetIndex, outFileSystem);
+ }
+
SLANG_NO_THROW SlangResult SLANG_MCALL specialize(
slang::SpecializationArg const* specializationArgs,
SlangInt specializationArgCount,
@@ -1067,6 +1081,14 @@ namespace Slang
return Super::getEntryPointCode(entryPointIndex, targetIndex, outCode, outDiagnostics);
}
+ SLANG_NO_THROW SlangResult SLANG_MCALL getResultAsFileSystem(
+ SlangInt entryPointIndex,
+ SlangInt targetIndex,
+ ISlangMutableFileSystem** outFileSystem) SLANG_OVERRIDE
+ {
+ return Super::getResultAsFileSystem(entryPointIndex, targetIndex, outFileSystem);
+ }
+
SLANG_NO_THROW SlangResult SLANG_MCALL specialize(
slang::SpecializationArg const* specializationArgs,
SlangInt specializationArgCount,
@@ -1221,6 +1243,14 @@ namespace Slang
return Super::getEntryPointCode(entryPointIndex, targetIndex, outCode, outDiagnostics);
}
+ SLANG_NO_THROW SlangResult SLANG_MCALL getResultAsFileSystem(
+ SlangInt entryPointIndex,
+ SlangInt targetIndex,
+ ISlangMutableFileSystem** outFileSystem) SLANG_OVERRIDE
+ {
+ return Super::getResultAsFileSystem(entryPointIndex, targetIndex, outFileSystem);
+ }
+
SLANG_NO_THROW SlangResult SLANG_MCALL specialize(
slang::SpecializationArg const* specializationArgs,
SlangInt specializationArgCount,
diff --git a/source/slang/slang-emit-source-writer.cpp b/source/slang/slang-emit-source-writer.cpp
index 72696c94a..4ff547119 100644
--- a/source/slang/slang-emit-source-writer.cpp
+++ b/source/slang/slang-emit-source-writer.cpp
@@ -311,7 +311,7 @@ void SourceWriter::advanceToSourceLocation(const SourceLoc& sourceLocation)
}
// Workout the humane source location.
- const HumaneSourceLoc humaneSourceLoc = getSourceManager()->getHumaneLoc(sourceLocation);
+ const HumaneSourceLoc humaneSourceLoc = getSourceManager()->getHumaneLoc(sourceLocation, SourceLocType::Emit);
// If the location is valid, mark need to update, and the new location
if (humaneSourceLoc.line > 0)
diff --git a/source/slang/slang-ir-obfuscate-loc.cpp b/source/slang/slang-ir-obfuscate-loc.cpp
index ecc16d2b2..860633106 100644
--- a/source/slang/slang-ir-obfuscate-loc.cpp
+++ b/source/slang/slang-ir-obfuscate-loc.cpp
@@ -362,8 +362,8 @@ SlangResult obfuscateModuleLocs(IRModule* module, SourceManager* sourceManager)
sourceMap->addEntry(entries[i]);
}
- // Associate the sourceMap with the obfuscated file
- obfuscatedFile->setSourceMap(boxedSourceMap);
+ // Associate the sourceMap with the obfuscated file.
+ obfuscatedFile->setSourceMap(boxedSourceMap, SourceMapKind::Obfuscated);
// Set the obfuscated map onto the module
module->setObfuscatedSourceMap(boxedSourceMap);
diff --git a/source/slang/slang.cpp b/source/slang/slang.cpp
index 760a119d1..e08bb2a62 100644
--- a/source/slang/slang.cpp
+++ b/source/slang/slang.cpp
@@ -3464,6 +3464,106 @@ SLANG_NO_THROW slang::ProgramLayout* SLANG_MCALL ComponentType::getLayout(
return asExternal(programLayout);
}
+static ICastable* _findDiagnosticRepresentation(IArtifact* artifact)
+{
+ if (auto rep = findAssociatedRepresentation<IArtifactDiagnostics>(artifact))
+ {
+ return rep;
+ }
+
+ for (auto associated : artifact->getAssociated())
+ {
+ if (isDerivedFrom(associated->getDesc().payload, ArtifactPayload::Diagnostics))
+ {
+ return associated;
+ }
+ }
+ return nullptr;
+}
+
+static IArtifact* _findObfuscatedSourceMap(IArtifact* artifact)
+{
+ // If we find any obfuscated source maps, we are done
+ for (auto associated : artifact->getAssociated())
+ {
+ const auto desc = associated->getDesc();
+
+ if (isDerivedFrom(desc.payload, ArtifactPayload::SourceMap) &&
+ isDerivedFrom(desc.style, ArtifactStyle::Obfuscated))
+ {
+ return associated;
+ }
+ }
+ return nullptr;
+}
+
+SLANG_NO_THROW SlangResult SLANG_MCALL ComponentType::getResultAsFileSystem(
+ SlangInt entryPointIndex,
+ Int targetIndex,
+ ISlangMutableFileSystem** outFileSystem)
+{
+ ComPtr<ISlangBlob> diagnostics;
+ ComPtr<ISlangBlob> code;
+
+ SLANG_RETURN_ON_FAIL(getEntryPointCode(entryPointIndex, targetIndex, diagnostics.writeRef(), code.writeRef()));
+
+ auto linkage = getLinkage();
+
+ auto target = linkage->targets[targetIndex];
+
+ auto targetProgram = getTargetProgram(target);
+
+ IArtifact* artifact = targetProgram->getExistingEntryPointResult(entryPointIndex);
+
+ // Add diagnostics id needs be...
+ if (diagnostics && !_findDiagnosticRepresentation(artifact))
+ {
+ // Add as an associated
+
+ auto diagnosticsArtifact = Artifact::create(ArtifactDesc::make(Artifact::Kind::HumanText, ArtifactPayload::Diagnostics));
+ diagnosticsArtifact->addRepresentationUnknown(diagnostics);
+
+ artifact->addAssociated(diagnosticsArtifact);
+
+ SLANG_ASSERT(diagnosticsArtifact == _findDiagnosticRepresentation(artifact));
+ }
+
+ // Add obfuscated source maps
+ if (!_findObfuscatedSourceMap(artifact))
+ {
+ List<IRModule*> irModules;
+ enumerateIRModules([&](IRModule* irModule) -> void { irModules.add(irModule); });
+
+ for (auto irModule : irModules)
+ {
+ if (auto obfuscatedSourceMap = irModule->getObfuscatedSourceMap())
+ {
+ auto artifactDesc = ArtifactDesc::make(ArtifactKind::Json, ArtifactPayload::SourceMap, ArtifactStyle::Obfuscated);
+
+ // Create the source map artifact
+ auto sourceMapArtifact = Artifact::create(artifactDesc, obfuscatedSourceMap->get().m_file.getUnownedSlice());
+
+ sourceMapArtifact->addRepresentation(obfuscatedSourceMap);
+
+ // associate with the artifact
+ artifact->addAssociated(sourceMapArtifact);
+ }
+ }
+ }
+
+ // Turn into a file system and return
+ ComPtr<ISlangMutableFileSystem> fileSystem(new MemoryFileSystem);
+
+ // Filter the containerArtifact into things that can be written
+ ComPtr<IArtifact> writeArtifact;
+ SLANG_RETURN_ON_FAIL(ArtifactContainerUtil::filter(artifact, writeArtifact));
+ SLANG_RETURN_ON_FAIL(ArtifactContainerUtil::writeContainer(writeArtifact, "", fileSystem));
+
+ *outFileSystem = fileSystem.detach();
+
+ return SLANG_OK;
+}
+
SLANG_NO_THROW SlangResult SLANG_MCALL ComponentType::getEntryPointCode(
SlangInt entryPointIndex,
Int targetIndex,
@@ -4983,7 +5083,7 @@ SlangResult _addLibraryReference(EndToEndCompileRequest* req, IArtifact* artifac
if (name.getLength())
{
auto sourceFile = sourceManager->findSourceFileByPathRecursively(name);
- sourceFile->setSourceMap(sourceMap);
+ sourceFile->setSourceMap(sourceMap, SourceMapKind::Obfuscated);
}
}
}
diff --git a/tools/gfx/d3d/d3d-swapchain.h b/tools/gfx/d3d/d3d-swapchain.h
index 0d4b3fafb..c9e0de82a 100644
--- a/tools/gfx/d3d/d3d-swapchain.h
+++ b/tools/gfx/d3d/d3d-swapchain.h
@@ -99,7 +99,15 @@ public:
}
virtual SLANG_NO_THROW Result SLANG_MCALL present() override
{
- if (SLANG_FAILED(m_swapChain->Present(m_desc.enableVSync ? 1 : 0, 0)))
+ const auto res = m_swapChain->Present(m_desc.enableVSync ? 1 : 0, 0);
+
+ // We may want to wait for crash dump completion for some kinds of debugging scenarios
+ if (res == DXGI_ERROR_DEVICE_REMOVED || res == DXGI_ERROR_DEVICE_RESET)
+ {
+ D3DUtil::waitForCrashDumpCompletion(res);
+ }
+
+ if (SLANG_FAILED(res))
{
return SLANG_FAIL;
}
diff --git a/tools/gfx/d3d/d3d-util.cpp b/tools/gfx/d3d/d3d-util.cpp
index e1ffc0efc..34d615744 100644
--- a/tools/gfx/d3d/d3d-util.cpp
+++ b/tools/gfx/d3d/d3d-util.cpp
@@ -14,6 +14,14 @@
#include "core/slang-basic.h"
#include "core/slang-platform.h"
+#ifdef GFX_NV_AFTERMATH
+# include "GFSDK_Aftermath.h"
+# include "GFSDK_Aftermath_Defines.h"
+# include "GFSDK_Aftermath_GpuCrashDump.h"
+
+# include "core/slang-process.h"
+#endif
+
namespace gfx {
using namespace Slang;
@@ -870,6 +878,58 @@ Result SLANG_MCALL reportD3DLiveObjects()
return D3DUtil::reportLiveObjects();
}
+
+/* static */SlangResult D3DUtil::waitForCrashDumpCompletion(HRESULT res)
+{
+ // If it's not a device remove/reset then theres nothing to wait for
+ if (!(res == DXGI_ERROR_DEVICE_REMOVED || res == DXGI_ERROR_DEVICE_RESET))
+ {
+ return SLANG_OK;
+ }
+
+#if GFX_NV_AFTERMATH
+ {
+ GFSDK_Aftermath_CrashDump_Status status = GFSDK_Aftermath_CrashDump_Status_Unknown;
+ if (GFSDK_Aftermath_GetCrashDumpStatus(&status) != GFSDK_Aftermath_Result_Success)
+ {
+ return SLANG_FAIL;
+ }
+
+ const auto startTick = Process::getClockTick();
+ const auto frequency = Process::getClockFrequency();
+
+ float timeOutInSecs = 1.0f;
+
+ uint64_t timeOutTicks = uint64_t(frequency * timeOutInSecs) + 1;
+
+ // Loop while Aftermath crash dump data collection has not finished or
+ // the application is still processing the crash dump data.
+ while (status != GFSDK_Aftermath_CrashDump_Status_CollectingDataFailed &&
+ status != GFSDK_Aftermath_CrashDump_Status_Finished &&
+ Process::getClockTick() - startTick < timeOutTicks)
+ {
+ // Sleep a couple of milliseconds and poll the status again.
+ Process::sleepCurrentThread(50);
+ if (GFSDK_Aftermath_GetCrashDumpStatus(&status) != GFSDK_Aftermath_Result_Success)
+ {
+ return SLANG_FAIL;
+ }
+ }
+
+ if (status == GFSDK_Aftermath_CrashDump_Status_Finished)
+ {
+ return SLANG_OK;
+ }
+ else
+ {
+ return SLANG_E_TIME_OUT;
+ }
+ }
+#endif
+
+ return SLANG_OK;
+}
+
/* static */SlangResult D3DUtil::findAdapters(DeviceCheckFlags flags, const AdapterLUID* adapterLUID, IDXGIFactory* dxgiFactory, List<ComPtr<IDXGIAdapter>>& outDxgiAdapters)
{
outDxgiAdapters.clear();
diff --git a/tools/gfx/d3d/d3d-util.h b/tools/gfx/d3d/d3d-util.h
index a35928f47..ce40ec722 100644
--- a/tools/gfx/d3d/d3d-util.h
+++ b/tools/gfx/d3d/d3d-util.h
@@ -121,6 +121,10 @@ class D3DUtil
static D3D12_RESOURCE_STATES getResourceState(ResourceState state);
static SlangResult reportLiveObjects();
+
+ /// Call after a DXGI_ERROR_DEVICE_REMOVED/DXGI_ERROR_DEVICE_RESET on present, to wait for
+ /// dumping to complete. Will return SLANG_OK if wait happened successfully
+ static SlangResult waitForCrashDumpCompletion(HRESULT res);
};
#if SLANG_GFX_HAS_DXR_SUPPORT
diff --git a/tools/gfx/d3d11/d3d11-device.cpp b/tools/gfx/d3d11/d3d11-device.cpp
index c10a608dc..96a5043fb 100644
--- a/tools/gfx/d3d11/d3d11-device.cpp
+++ b/tools/gfx/d3d11/d3d11-device.cpp
@@ -16,6 +16,12 @@
#include "d3d11-helper-functions.h"
+#ifdef GFX_NV_AFTERMATH
+# include "GFSDK_Aftermath.h"
+# include "GFSDK_Aftermath_Defines.h"
+# include "GFSDK_Aftermath_GpuCrashDump.h"
+#endif
+
namespace gfx
{
@@ -23,6 +29,7 @@ using namespace Slang;
namespace d3d11
{
+
SlangResult DeviceImpl::initialize(const Desc& desc)
{
SLANG_RETURN_ON_FAIL(slangContext.initialize(
@@ -148,11 +155,42 @@ SlangResult DeviceImpl::initialize(const Desc& desc)
m_device.writeRef(),
&featureLevel,
m_immediateContext.writeRef());
+
+#ifdef GFX_NV_AFTERMATH
+ if (SLANG_SUCCEEDED(res))
+ {
+ if (deviceCheckFlags & DeviceCheckFlag::UseDebug)
+ {
+ // Initialize Nsight Aftermath for this device.
+ // This combination of flags is not necessarily appropriate for real world usage
+ const uint32_t aftermathFlags =
+ GFSDK_Aftermath_FeatureFlags_EnableMarkers | // Enable event marker tracking.
+ GFSDK_Aftermath_FeatureFlags_CallStackCapturing | // Enable automatic call stack event markers.
+ GFSDK_Aftermath_FeatureFlags_EnableResourceTracking | // Enable tracking of resources.
+ GFSDK_Aftermath_FeatureFlags_GenerateShaderDebugInfo | // Generate debug information for shaders.
+ GFSDK_Aftermath_FeatureFlags_EnableShaderErrorReporting; // Enable additional runtime shader error reporting.
+
+ auto initResult = GFSDK_Aftermath_DX11_Initialize(
+ GFSDK_Aftermath_Version_API,
+ aftermathFlags,
+ m_device);
+
+ if (initResult != GFSDK_Aftermath_Result_Success)
+ {
+ SLANG_ASSERT_FAILURE("Unable to initialize aftermath");
+ // Unable to initialize aftermath
+ return SLANG_FAIL;
+ }
+ }
+ }
+#endif
+
// Check if successfully constructed - if so we are done.
if (SLANG_SUCCEEDED(res))
{
break;
}
+
}
// If res is failure, means all styles have have failed, and so initialization fails.
if (SLANG_FAILED(res))
diff --git a/tools/gfx/d3d12/d3d12-device.cpp b/tools/gfx/d3d12/d3d12-device.cpp
index 863326a94..68078b445 100644
--- a/tools/gfx/d3d12/d3d12-device.cpp
+++ b/tools/gfx/d3d12/d3d12-device.cpp
@@ -28,6 +28,12 @@
# include "../nvapi/nvapi-include.h"
#endif
+#ifdef GFX_NV_AFTERMATH
+# include "GFSDK_Aftermath.h"
+# include "GFSDK_Aftermath_Defines.h"
+# include "GFSDK_Aftermath_GpuCrashDump.h"
+#endif
+
namespace gfx
{
namespace d3d12
@@ -37,6 +43,13 @@ using namespace Slang;
static const uint32_t D3D_FEATURE_LEVEL_12_2 = 0xc200;
+
+#if GFX_NV_AFTERMATH
+/* static */const bool DeviceImpl::g_isAftermathEnabled = true;
+#else
+/* static */const bool DeviceImpl::g_isAftermathEnabled = false;
+#endif
+
struct ShaderModelInfo
{
D3D_SHADER_MODEL shaderModel;
@@ -286,7 +299,7 @@ Result DeviceImpl::_createDevice(
D3D_FEATURE_LEVEL featureLevel,
D3D12DeviceInfo& outDeviceInfo)
{
- if (m_dxDebug && (deviceCheckFlags & DeviceCheckFlag::UseDebug))
+ if (m_dxDebug && (deviceCheckFlags & DeviceCheckFlag::UseDebug) && !g_isAftermathEnabled)
{
m_dxDebug->EnableDebugLayer();
}
@@ -319,7 +332,7 @@ Result DeviceImpl::_createDevice(
return SLANG_FAIL;
}
- if (m_dxDebug && (deviceCheckFlags & DeviceCheckFlag::UseDebug))
+ if (m_dxDebug && (deviceCheckFlags & DeviceCheckFlag::UseDebug) && !g_isAftermathEnabled)
{
ComPtr<ID3D12InfoQueue> infoQueue;
if (SLANG_SUCCEEDED(device->QueryInterface(infoQueue.writeRef())))
@@ -373,6 +386,35 @@ Result DeviceImpl::_createDevice(
}
}
+
+#ifdef GFX_NV_AFTERMATH
+ {
+ if ((deviceCheckFlags & DeviceCheckFlag::UseDebug) && g_isAftermathEnabled)
+ {
+ // Initialize Nsight Aftermath for this device.
+ // This combination of flags is not necessarily appropraite for real world usage
+ const uint32_t aftermathFlags =
+ GFSDK_Aftermath_FeatureFlags_EnableMarkers | // Enable event marker tracking.
+ GFSDK_Aftermath_FeatureFlags_CallStackCapturing | // Enable automatic call stack event markers.
+ GFSDK_Aftermath_FeatureFlags_EnableResourceTracking | // Enable tracking of resources.
+ GFSDK_Aftermath_FeatureFlags_GenerateShaderDebugInfo | // Generate debug information for shaders.
+ GFSDK_Aftermath_FeatureFlags_EnableShaderErrorReporting; // Enable additional runtime shader error reporting.
+
+ auto initResult = GFSDK_Aftermath_DX12_Initialize(
+ GFSDK_Aftermath_Version_API,
+ aftermathFlags,
+ device);
+
+ if ( initResult != GFSDK_Aftermath_Result_Success)
+ {
+ SLANG_ASSERT_FAILURE("Unable to initialize aftermath");
+ // Unable to initialize
+ return SLANG_FAIL;
+ }
+ }
+ }
+#endif
+
// Get the descs
{
adapter->GetDesc(&outDeviceInfo.m_desc);
@@ -474,7 +516,9 @@ Result DeviceImpl::initialize(const Desc& desc)
}
#endif
- if (ENABLE_DEBUG_LAYER || isGfxDebugLayerEnabled())
+
+ // If Aftermath is enabled, we can't enable the D3D12 debug layer as well
+ if (ENABLE_DEBUG_LAYER || isGfxDebugLayerEnabled() && !g_isAftermathEnabled)
{
m_D3D12GetDebugInterface =
(PFN_D3D12_GET_DEBUG_INTERFACE)loadProc(d3dModule, "D3D12GetDebugInterface");
diff --git a/tools/gfx/d3d12/d3d12-device.h b/tools/gfx/d3d12/d3d12-device.h
index 975ba419b..6bbdde9d0 100644
--- a/tools/gfx/d3d12/d3d12-device.h
+++ b/tools/gfx/d3d12/d3d12-device.h
@@ -56,6 +56,8 @@ public:
ComPtr<ID3D12Debug> m_dxDebug;
+ static const bool g_isAftermathEnabled;
+
D3D12DeviceInfo m_deviceInfo;
ID3D12Device* m_device = nullptr;
ID3D12Device5* m_device5 = nullptr;
diff --git a/tools/gfx/vulkan/vk-device.cpp b/tools/gfx/vulkan/vk-device.cpp
index 654353d63..8f7a88886 100644
--- a/tools/gfx/vulkan/vk-device.cpp
+++ b/tools/gfx/vulkan/vk-device.cpp
@@ -18,6 +18,12 @@
#include "vk-helper-functions.h"
+#ifdef GFX_NV_AFTERMATH
+# include "GFSDK_Aftermath.h"
+# include "GFSDK_Aftermath_Defines.h"
+# include "GFSDK_Aftermath_GpuCrashDump.h"
+#endif
+
namespace gfx
{
@@ -658,6 +664,34 @@ Result DeviceImpl::initVulkanInstanceAndDevice(
m_queueFamilyIndex = m_api.findQueue(VK_QUEUE_GRAPHICS_BIT | VK_QUEUE_COMPUTE_BIT);
assert(m_queueFamilyIndex >= 0);
+#if defined(GFX_NV_AFTERMATH)
+ VkDeviceDiagnosticsConfigCreateInfoNV aftermathInfo = {};
+
+ {
+ // Enable NV_device_diagnostic_checkpoints extension to be able to
+ // use Aftermath event markers.
+ deviceExtensions.add(VK_NV_DEVICE_DIAGNOSTIC_CHECKPOINTS_EXTENSION_NAME);
+
+ // Enable NV_device_diagnostics_config extension to configure Aftermath
+ // features.
+ deviceExtensions.add(VK_NV_DEVICE_DIAGNOSTICS_CONFIG_EXTENSION_NAME);
+
+ // Set up device creation info for Aftermath feature flag configuration.
+ VkDeviceDiagnosticsConfigFlagsNV aftermathFlags =
+ VK_DEVICE_DIAGNOSTICS_CONFIG_ENABLE_AUTOMATIC_CHECKPOINTS_BIT_NV | // Enable automatic call stack checkpoints.
+ VK_DEVICE_DIAGNOSTICS_CONFIG_ENABLE_RESOURCE_TRACKING_BIT_NV | // Enable tracking of resources.
+ VK_DEVICE_DIAGNOSTICS_CONFIG_ENABLE_SHADER_DEBUG_INFO_BIT_NV; // Generate debug information for shaders.
+ // Not available on the version of Vulkan currently building with.
+ //VK_DEVICE_DIAGNOSTICS_CONFIG_ENABLE_SHADER_ERROR_REPORTING_BIT_NV; // Enable additional runtime shader error reporting.
+
+ aftermathInfo.sType = VK_STRUCTURE_TYPE_DEVICE_DIAGNOSTICS_CONFIG_CREATE_INFO_NV;
+ aftermathInfo.flags = aftermathFlags;
+
+ aftermathInfo.pNext = deviceCreateInfo.pNext;
+ deviceCreateInfo.pNext = &aftermathInfo;
+ }
+#endif
+
if (handles[2].handleValue == 0)
{
float queuePriority = 0.0f;