diff options
| author | Ellie Hermaszewska <ellieh@nvidia.com> | 2024-10-29 14:49:26 +0800 |
|---|---|---|
| committer | GitHub <noreply@github.com> | 2024-10-29 14:49:26 +0800 |
| commit | f65d756bff8d4c5cbc15bd0322a2ae8e6b896a21 (patch) | |
| tree | ea1d61342cd29368e19135000ec2948813096205 /examples/gpu-printing | |
| parent | a729c15e9dce9f5116a38afc66329ab2ca4cea54 (diff) | |
format
* format
* Minor test fixes
* enable checking cpp format in ci
Diffstat (limited to 'examples/gpu-printing')
| -rw-r--r-- | examples/gpu-printing/gpu-printing.cpp | 159 | ||||
| -rw-r--r-- | examples/gpu-printing/gpu-printing.h | 50 | ||||
| -rw-r--r-- | examples/gpu-printing/main.cpp | 209 |
3 files changed, 217 insertions, 201 deletions
diff --git a/examples/gpu-printing/gpu-printing.cpp b/examples/gpu-printing/gpu-printing.cpp index aeca7aa9a..f71578554 100644 --- a/examples/gpu-printing/gpu-printing.cpp +++ b/examples/gpu-printing/gpu-printing.cpp @@ -2,7 +2,6 @@ #include "gpu-printing.h" #include <assert.h> - #include <string.h> // This file implements the CPU side of a simple GPU printing @@ -39,7 +38,7 @@ void GPUPrinting::loadStrings(slang::ProgramLayout* slangReflection) // that appear in the linked program. // SlangUInt hashedStringCount = slangReflection->getHashedStringCount(); - for( SlangUInt ii = 0; ii < hashedStringCount; ++ii ) + for (SlangUInt ii = 0; ii < hashedStringCount; ++ii) { // For each string we can fetch its bytes from the Slang // reflection data. @@ -59,7 +58,8 @@ void GPUPrinting::loadStrings(slang::ProgramLayout* slangReflection) // The `GPUPrinting` implementation will store the mapping // from hash codes back to strings in a simple STL `map`. // - m_hashedStrings.insert(std::make_pair(hash, std::string(stringData, stringData + stringSize))); + m_hashedStrings.insert( + std::make_pair(hash, std::string(stringData, stringData + stringSize))); } } @@ -73,12 +73,12 @@ void GPUPrinting::processGPUPrintCommands(const void* data, size_t dataSize) // a granularity of 32-bits words, so we start by computing // how many words, total, will fit in the buffer. // - uint32_t dataWordCount = uint32_t(dataSize/ sizeof(uint32_t)); + uint32_t dataWordCount = uint32_t(dataSize / sizeof(uint32_t)); // // If the buffer doesn't even have enough space for the leading counter, // then there is nothing to print. // - if( dataWordCount < 1 ) + if (dataWordCount < 1) { fprintf(stderr, "error: expected at least 4 bytes in GPU printing buffer\n"); return; @@ -87,7 +87,7 @@ void GPUPrinting::processGPUPrintCommands(const void* data, size_t dataSize) // Otherwise, we set ourselves up to start reading data from the buffer // at a granularity of 32-bit words. // - const uint32_t* dataCursor = (const uint32_t*) data; + const uint32_t* dataCursor = (const uint32_t*)data; // The first word of a printing buffer gives us the total number of // words that were appended by GPU printing operations. @@ -106,20 +106,25 @@ void GPUPrinting::processGPUPrintCommands(const void* data, size_t dataSize) // larger buffer. // size_t totalBytesWritten = sizeof(uint32_t) * (wordsAppended + 1); - if( totalBytesWritten > dataSize ) + if (totalBytesWritten > dataSize) { - fprintf(stderr, "warning: GPU code attempted to write %llu bytes to the printing buffer, but only %llu bytes were available\n", (unsigned long long)totalBytesWritten, (unsigned long long)dataSize); + fprintf( + stderr, + "warning: GPU code attempted to write %llu bytes to the printing buffer, but only %llu " + "bytes were available\n", + (unsigned long long)totalBytesWritten, + (unsigned long long)dataSize); // If the buffer is full, then we only want to read through // to the end of what is available. // - dataEnd = ((const uint32_t*) data) + dataWordCount; + dataEnd = ((const uint32_t*)data) + dataWordCount; } // We will now proceed to read off "commands" from the buffer, // and execute those commands to print things to `stdout`. // - while( dataCursor < dataEnd ) + while (dataCursor < dataEnd) { // The first word of each command is encoded to hold both // an "opcode" for the command, and the number of "payload" @@ -135,7 +140,7 @@ void GPUPrinting::processGPUPrintCommands(const void* data, size_t dataSize) // avoid crashes from a command trying to fetch data past // the end of the buffer. // - if( payloadWordCount > size_t(dataCursor - dataEnd) ) + if (payloadWordCount > size_t(dataCursor - dataEnd)) { break; } @@ -149,7 +154,7 @@ void GPUPrinting::processGPUPrintCommands(const void* data, size_t dataSize) dataCursor += payloadWordCount; // What to do with a command depends a lot on which "op" was selected. - switch( op ) + switch (op) { default: // If we encounter an op that we don't understand, there is a change @@ -176,21 +181,21 @@ void GPUPrinting::processGPUPrintCommands(const void* data, size_t dataSize) // We will use a macro to avoid duplication the code shared // between these cases. // - #define CASE(OP, FORMAT, TYPE) \ - case GPUPrintingOp::OP: \ - { \ - TYPE value; \ - assert(payloadWordCount >= (sizeof(value) / sizeof(uint32_t))); \ - memcpy(&value, payloadWords, sizeof(value)); \ - printf(FORMAT, value); \ - } \ - break - - CASE(Int32, "%d", int); - CASE(UInt32, "%u", unsigned int); - CASE(Float32, "%f", float); - - #undef CASE +#define CASE(OP, FORMAT, TYPE) \ + case GPUPrintingOp::OP: \ + { \ + TYPE value; \ + assert(payloadWordCount >= (sizeof(value) / sizeof(uint32_t))); \ + memcpy(&value, payloadWords, sizeof(value)); \ + printf(FORMAT, value); \ + } \ + break + + CASE(Int32, "%d", int); + CASE(UInt32, "%u", unsigned int); + CASE(Float32, "%f", float); + +#undef CASE case GPUPrintingOp::String: { @@ -214,7 +219,7 @@ void GPUPrinting::processGPUPrintCommands(const void* data, size_t dataSize) // to appear in the GPU code. // auto iter = m_hashedStrings.find(hash); - if(iter == m_hashedStrings.end()) + if (iter == m_hashedStrings.end()) { // If we didn't have a string to match that hash code in // our map, we can continue trying to print, but it is @@ -230,7 +235,7 @@ void GPUPrinting::processGPUPrintCommands(const void* data, size_t dataSize) // // TODO: This code isn't robust against strings with // embeded null bytes. - //s + // s printf("%s", iter->second.c_str()); } break; @@ -248,7 +253,7 @@ void GPUPrinting::processGPUPrintCommands(const void* data, size_t dataSize) StringHash formatHash = *payloadWords++; auto iter = m_hashedStrings.find(formatHash); - if(iter == m_hashedStrings.end()) + if (iter == m_hashedStrings.end()) { // If we didn't have a string to match that hash code in // our map, we can continue trying to print, but it is @@ -270,14 +275,14 @@ void GPUPrinting::processGPUPrintCommands(const void* data, size_t dataSize) // const char* cursor = format.c_str(); const char* end = cursor + format.length(); - while( cursor != end ) + while (cursor != end) { int c = *cursor++; // If we see a byte other than `%`, then we can just // output it directly and keep scanning the format string. // - if( c != '%' ) + if (c != '%') { putchar(c); continue; @@ -289,7 +294,7 @@ void GPUPrinting::processGPUPrintCommands(const void* data, size_t dataSize) // If we are somehow at the end of the format // string, then the format was bad. // - if( cursor == end ) + if (cursor == end) { fprintf(stderr, "error: unexpected '%%' at and of format string\n"); break; @@ -299,7 +304,7 @@ void GPUPrinting::processGPUPrintCommands(const void* data, size_t dataSize) // the `%` character, then it is an escaped // `%` so we should just emit it as-is and move along. // - if( *cursor == '%' ) + if (*cursor == '%') { putchar(*cursor++); continue; @@ -317,52 +322,56 @@ void GPUPrinting::processGPUPrintCommands(const void* data, size_t dataSize) // read a single-byte specifier. // int specifier = *cursor++; - switch( specifier ) + switch (specifier) { default: - fprintf(stderr, "error: unexpected format specifier '%c' (0x%X)\n", specifier, specifier); + fprintf( + stderr, + "error: unexpected format specifier '%c' (0x%X)\n", + specifier, + specifier); break; - // When processing each format speecifier, we will - // read words from the payload, as necessary - // to yield a value of the expected type. - // - // To reduce the amount of boilerplate, we will - // use a macro to capture the shared code for - // common cases. - // - #define CASE(CHAR, FORMAT, TYPE) \ - case CHAR: \ - { \ - assert(payloadWords != payloadWordsEnd); \ - TYPE value; \ - memcpy(&value, payloadWords, sizeof(value)); \ - payloadWords += sizeof(value) / sizeof(uint32_t); \ - printf(FORMAT, value); \ - } \ - break + // When processing each format speecifier, we will + // read words from the payload, as necessary + // to yield a value of the expected type. + // + // To reduce the amount of boilerplate, we will + // use a macro to capture the shared code for + // common cases. + // +#define CASE(CHAR, FORMAT, TYPE) \ + case CHAR: \ + { \ + assert(payloadWords != payloadWordsEnd); \ + TYPE value; \ + memcpy(&value, payloadWords, sizeof(value)); \ + payloadWords += sizeof(value) / sizeof(uint32_t); \ + printf(FORMAT, value); \ + } \ + break case 'i': // `%i` is just an alias for `%d` - CASE('d', "%d", int); - CASE('u', "%u", unsigned int); - CASE('x', "%x", unsigned int); - CASE('X', "%X", unsigned int); - - // Note: all of our printing support for floating-point - // values will use the `float` type instead of `double`. - // This isn't compatible with C rules, but makes more sense - // for GPU code. - // - CASE('f', "%f", float); - CASE('F', "%F", float); - CASE('e', "%e", float); - CASE('E', "%E", float); - CASE('g', "%g", float); - CASE('G', "%G", float); - CASE('c', "%c", int); - - #undef CASE + CASE('d', "%d", int); + CASE('u', "%u", unsigned int); + CASE('x', "%x", unsigned int); + CASE('X', "%X", unsigned int); + + // Note: all of our printing support for floating-point + // values will use the `float` type instead of `double`. + // This isn't compatible with C rules, but makes more sense + // for GPU code. + // + CASE('f', "%f", float); + CASE('F', "%F", float); + CASE('e', "%e", float); + CASE('E', "%E", float); + CASE('g', "%g", float); + CASE('G', "%G", float); + CASE('c', "%c", int); + +#undef CASE case 's': { @@ -373,7 +382,7 @@ void GPUPrinting::processGPUPrintCommands(const void* data, size_t dataSize) assert(payloadWords != payloadWordsEnd); StringHash hash = *payloadWords++; auto iter = m_hashedStrings.find(hash); - if(iter == m_hashedStrings.end()) + if (iter == m_hashedStrings.end()) { fprintf(stderr, "error: string with unknown hash 0x%x\n", hash); continue; @@ -387,6 +396,4 @@ void GPUPrinting::processGPUPrintCommands(const void* data, size_t dataSize) break; } } - - } diff --git a/examples/gpu-printing/gpu-printing.h b/examples/gpu-printing/gpu-printing.h index 64eaf770a..84c036548 100644 --- a/examples/gpu-printing/gpu-printing.h +++ b/examples/gpu-printing/gpu-printing.h @@ -19,39 +19,39 @@ #include <map> #include <string> - /// Stores state used for executing print commands generated by GPU shaders +/// Stores state used for executing print commands generated by GPU shaders struct GPUPrinting { public: - /// Load any string literals used by a Slang program. - /// - /// The `slangReflection` should be the layout and reflection - /// object for a Slang shader program that might need to produce - /// printed output. This function will load any strings - /// referenced by the program into its database for mapping - /// string hashes back to the original strings. - /// + /// Load any string literals used by a Slang program. + /// + /// The `slangReflection` should be the layout and reflection + /// object for a Slang shader program that might need to produce + /// printed output. This function will load any strings + /// referenced by the program into its database for mapping + /// string hashes back to the original strings. + /// void loadStrings(slang::ProgramLayout* slangReflection); - /// Process a buffer of GPU printing commands and write output to `stdout`. - /// - /// This function attempts to read print commands from the buffer - /// pointed to by `data` and execute them to produce output. - /// - /// The buffer pointed at by `data` (of size `dataSize`) should be allocated - /// in host-visible memory. - /// - /// Before executing GPU work, the first four bytes pointed to by `data` - /// should have been cleared to zero. - /// - /// If GPU work has attempted to write more data than the buffer - /// can fit, a warning will be printed to `stderr`, and printing commands - /// that could not fit completely in the buffer will be skipped. - /// + /// Process a buffer of GPU printing commands and write output to `stdout`. + /// + /// This function attempts to read print commands from the buffer + /// pointed to by `data` and execute them to produce output. + /// + /// The buffer pointed at by `data` (of size `dataSize`) should be allocated + /// in host-visible memory. + /// + /// Before executing GPU work, the first four bytes pointed to by `data` + /// should have been cleared to zero. + /// + /// If GPU work has attempted to write more data than the buffer + /// can fit, a warning will be printed to `stderr`, and printing commands + /// that could not fit completely in the buffer will be skipped. + /// void processGPUPrintCommands(const void* data, size_t dataSize); private: typedef int StringHash; - std::map<StringHash, std::string> m_hashedStrings; + std::map<StringHash, std::string> m_hashedStrings; }; diff --git a/examples/gpu-printing/main.cpp b/examples/gpu-printing/main.cpp index fd15661dd..fa9f919dc 100644 --- a/examples/gpu-printing/main.cpp +++ b/examples/gpu-printing/main.cpp @@ -1,17 +1,16 @@ // main.cpp -#include <string> - +#include "slang-com-ptr.h" #include "slang.h" -#include "slang-com-ptr.h" +#include <string> using Slang::ComPtr; +#include "examples/example-base/example-base.h" +#include "gfx-util/shader-cursor.h" #include "gpu-printing.h" #include "slang-gfx.h" -#include "gfx-util/shader-cursor.h" -#include "tools/platform/window.h" #include "source/core/slang-basic.h" -#include "examples/example-base/example-base.h" +#include "tools/platform/window.h" using namespace gfx; @@ -23,7 +22,9 @@ ComPtr<slang::ISession> createSlangSession(gfx::IDevice* device) return slangSession; } -ComPtr<slang::IModule> compileShaderModuleFromFile(slang::ISession* slangSession, char const* filePath) +ComPtr<slang::IModule> compileShaderModuleFromFile( + slang::ISession* slangSession, + char const* filePath) { ComPtr<slang::IModule> slangModule; ComPtr<slang::IBlob> diagnosticBlob; @@ -34,113 +35,121 @@ ComPtr<slang::IModule> compileShaderModuleFromFile(slang::ISession* slangSession return slangModule; } -struct ExampleProgram: public TestBase +struct ExampleProgram : public TestBase { -int gWindowWidth = 640; -int gWindowHeight = 480; + int gWindowWidth = 640; + int gWindowHeight = 480; -ComPtr<gfx::IDevice> gDevice; + ComPtr<gfx::IDevice> gDevice; -ComPtr<slang::ISession> gSlangSession; -ComPtr<slang::IModule> gSlangModule; -ComPtr<gfx::IShaderProgram> gProgram; + ComPtr<slang::ISession> gSlangSession; + ComPtr<slang::IModule> gSlangModule; + ComPtr<gfx::IShaderProgram> gProgram; -ComPtr<gfx::IPipelineState> gPipelineState; + ComPtr<gfx::IPipelineState> gPipelineState; -Slang::Dictionary<int, std::string> gHashedStrings; + Slang::Dictionary<int, std::string> gHashedStrings; -GPUPrinting gGPUPrinting; + GPUPrinting gGPUPrinting; -ComPtr<gfx::IShaderProgram> loadComputeProgram(slang::IModule* slangModule, char const* entryPointName) -{ - ComPtr<slang::IEntryPoint> entryPoint; - slangModule->findEntryPointByName(entryPointName, entryPoint.writeRef()); - - ComPtr<slang::IComponentType> linkedProgram; - entryPoint->link(linkedProgram.writeRef()); - - if (isTestMode()) + ComPtr<gfx::IShaderProgram> loadComputeProgram( + slang::IModule* slangModule, + char const* entryPointName) { - printEntrypointHashes(1, 1, linkedProgram); - } + ComPtr<slang::IEntryPoint> entryPoint; + slangModule->findEntryPointByName(entryPointName, entryPoint.writeRef()); - gGPUPrinting.loadStrings(linkedProgram->getLayout()); + ComPtr<slang::IComponentType> linkedProgram; + entryPoint->link(linkedProgram.writeRef()); - gfx::IShaderProgram::Desc programDesc = {}; - programDesc.slangGlobalScope = linkedProgram; + if (isTestMode()) + { + printEntrypointHashes(1, 1, linkedProgram); + } - auto shaderProgram = gDevice->createProgram(programDesc); + gGPUPrinting.loadStrings(linkedProgram->getLayout()); - return shaderProgram; -} + gfx::IShaderProgram::Desc programDesc = {}; + programDesc.slangGlobalScope = linkedProgram; -Result execute(int argc, char* argv[]) -{ - parseOption(argc, argv); - IDevice::Desc deviceDesc; - Result res = gfxCreateDevice(&deviceDesc, gDevice.writeRef()); - if(SLANG_FAILED(res)) return res; - - Slang::String path = resourceBase.resolveResource("kernels.slang"); - - gSlangSession = createSlangSession(gDevice); - gSlangModule = compileShaderModuleFromFile(gSlangSession, path.getBuffer()); - if(!gSlangModule) - return SLANG_FAIL; - - gProgram = loadComputeProgram(gSlangModule, "computeMain"); - if(!gProgram) - return SLANG_FAIL; - - ComputePipelineStateDesc desc; - desc.program = gProgram; - auto pipelineState = gDevice->createComputePipelineState(desc); - if(!pipelineState) return SLANG_FAIL; - - gPipelineState = pipelineState; - - size_t printBufferSize = 4 * 1024; // use a small-ish (4KB) buffer for print output - - IBufferResource::Desc printBufferDesc = {}; - printBufferDesc.type = IResource::Type::Buffer; - printBufferDesc.sizeInBytes = printBufferSize; - printBufferDesc.elementSize = sizeof(uint32_t); - printBufferDesc.defaultState = ResourceState::UnorderedAccess; - printBufferDesc.allowedStates = ResourceStateSet( - ResourceState::CopySource, ResourceState::CopyDestination, ResourceState::UnorderedAccess); - printBufferDesc.memoryType = MemoryType::DeviceLocal; - auto printBuffer = gDevice->createBufferResource(printBufferDesc); - - IResourceView::Desc printBufferViewDesc = {}; - printBufferViewDesc.type = IResourceView::Type::UnorderedAccess; - printBufferViewDesc.format = Format::Unknown; - auto printBufferView = gDevice->createBufferView(printBuffer, nullptr, printBufferViewDesc); - - ITransientResourceHeap::Desc transientResourceHeapDesc = {}; - transientResourceHeapDesc.constantBufferSize = 256; - auto transientHeap = gDevice->createTransientResourceHeap(transientResourceHeapDesc); - - ICommandQueue::Desc queueDesc = {ICommandQueue::QueueType::Graphics}; - auto queue = gDevice->createCommandQueue(queueDesc); - auto commandBuffer = transientHeap->createCommandBuffer(); - auto encoder = commandBuffer->encodeComputeCommands(); - auto rootShaderObject = encoder->bindPipeline(gPipelineState); - auto cursor = ShaderCursor(rootShaderObject); - cursor["gPrintBuffer"].setResource(printBufferView); - encoder->dispatchCompute(1, 1, 1); - encoder->bufferBarrier(printBuffer, ResourceState::UnorderedAccess, ResourceState::CopySource); - encoder->endEncoding(); - commandBuffer->close(); - queue->executeCommandBuffer(commandBuffer); - - ComPtr<ISlangBlob> blob; - gDevice->readBufferResource(printBuffer, 0, printBufferSize, blob.writeRef()); - - gGPUPrinting.processGPUPrintCommands(blob->getBufferPointer(), printBufferSize); - - return SLANG_OK; -} + auto shaderProgram = gDevice->createProgram(programDesc); + + return shaderProgram; + } + Result execute(int argc, char* argv[]) + { + parseOption(argc, argv); + IDevice::Desc deviceDesc; + Result res = gfxCreateDevice(&deviceDesc, gDevice.writeRef()); + if (SLANG_FAILED(res)) + return res; + + Slang::String path = resourceBase.resolveResource("kernels.slang"); + + gSlangSession = createSlangSession(gDevice); + gSlangModule = compileShaderModuleFromFile(gSlangSession, path.getBuffer()); + if (!gSlangModule) + return SLANG_FAIL; + + gProgram = loadComputeProgram(gSlangModule, "computeMain"); + if (!gProgram) + return SLANG_FAIL; + + ComputePipelineStateDesc desc; + desc.program = gProgram; + auto pipelineState = gDevice->createComputePipelineState(desc); + if (!pipelineState) + return SLANG_FAIL; + + gPipelineState = pipelineState; + + size_t printBufferSize = 4 * 1024; // use a small-ish (4KB) buffer for print output + + IBufferResource::Desc printBufferDesc = {}; + printBufferDesc.type = IResource::Type::Buffer; + printBufferDesc.sizeInBytes = printBufferSize; + printBufferDesc.elementSize = sizeof(uint32_t); + printBufferDesc.defaultState = ResourceState::UnorderedAccess; + printBufferDesc.allowedStates = ResourceStateSet( + ResourceState::CopySource, + ResourceState::CopyDestination, + ResourceState::UnorderedAccess); + printBufferDesc.memoryType = MemoryType::DeviceLocal; + auto printBuffer = gDevice->createBufferResource(printBufferDesc); + + IResourceView::Desc printBufferViewDesc = {}; + printBufferViewDesc.type = IResourceView::Type::UnorderedAccess; + printBufferViewDesc.format = Format::Unknown; + auto printBufferView = gDevice->createBufferView(printBuffer, nullptr, printBufferViewDesc); + + ITransientResourceHeap::Desc transientResourceHeapDesc = {}; + transientResourceHeapDesc.constantBufferSize = 256; + auto transientHeap = gDevice->createTransientResourceHeap(transientResourceHeapDesc); + + ICommandQueue::Desc queueDesc = {ICommandQueue::QueueType::Graphics}; + auto queue = gDevice->createCommandQueue(queueDesc); + auto commandBuffer = transientHeap->createCommandBuffer(); + auto encoder = commandBuffer->encodeComputeCommands(); + auto rootShaderObject = encoder->bindPipeline(gPipelineState); + auto cursor = ShaderCursor(rootShaderObject); + cursor["gPrintBuffer"].setResource(printBufferView); + encoder->dispatchCompute(1, 1, 1); + encoder->bufferBarrier( + printBuffer, + ResourceState::UnorderedAccess, + ResourceState::CopySource); + encoder->endEncoding(); + commandBuffer->close(); + queue->executeCommandBuffer(commandBuffer); + + ComPtr<ISlangBlob> blob; + gDevice->readBufferResource(printBuffer, 0, printBufferSize, blob.writeRef()); + + gGPUPrinting.processGPUPrintCommands(blob->getBufferPointer(), printBufferSize); + + return SLANG_OK; + } }; int main(int argc, char* argv[]) |
