diff options
| author | Dietrich Geisler <dag368@cornell.edu> | 2020-07-27 12:14:17 -0400 |
|---|---|---|
| committer | GitHub <noreply@github.com> | 2020-07-27 09:14:17 -0700 |
| commit | 348058f96e81d11da2698acf8343a99a199f1f24 (patch) | |
| tree | b795625654f7696b50e98127a9b745bdb8f68fc3 /examples/heterogeneous-hello-world/main.cpp | |
| parent | 87940a649e3b4f757905015de95225d57ec2f27c (diff) | |
Baseline Heterogeneous Example (#1460)
* Baseline Heterogeneous Example
This PR introduces a baseline heterogeneous example, including both a
Slang file and an associated C++ helper file. This refactoring
primarily moves the Slang file "into the driver's seat" while
maintaining that the C++ side still does most of the actual work.
* Fix to prelude path
Diffstat (limited to 'examples/heterogeneous-hello-world/main.cpp')
| -rw-r--r-- | examples/heterogeneous-hello-world/main.cpp | 253 |
1 files changed, 169 insertions, 84 deletions
diff --git a/examples/heterogeneous-hello-world/main.cpp b/examples/heterogeneous-hello-world/main.cpp index 8d5540a32..a590f8c4b 100644 --- a/examples/heterogeneous-hello-world/main.cpp +++ b/examples/heterogeneous-hello-world/main.cpp @@ -35,18 +35,36 @@ #include "gfx/render.h" #include "gfx/d3d11/render-d3d11.h" #include "gfx/window.h" +#include "../../prelude/slang-cpp-types.h"; using namespace gfx; -// We will start with a function that will invoke the Slang compiler -// to generate target-specific code from a shader file, and then -// use that to initialize an API shader program. +// We create global ref pointers to avoid dereferencing values // -// Note that `Renderer` and `ShaderProgram` here are types from -// the graphics API abstraction layer, and *not* part of the -// Slang API. This function is representative of code that a user -// might write to integrate Slang into their renderer/engine. +RefPtr<gfx::ShaderProgram> gShaderProgram; +RefPtr<gfx::ApplicationContext> gAppContext; +RefPtr<gfx::Renderer> gRenderer; + +RefPtr<gfx::BufferResource> gStructuredBuffer; + +RefPtr<gfx::PipelineLayout> gPipelineLayout; +RefPtr<gfx::PipelineState> gPipelineState; +RefPtr<gfx::DescriptorSetLayout> gDescriptorSetLayout; +RefPtr<gfx::DescriptorSet> gDescriptorSet; + +// Boilerplate types to help the slan-generated file // -RefPtr<gfx::ShaderProgram> loadShaderProgram(gfx::Renderer* renderer) +struct gfx_Window_0; +struct gfx_Renderer_0; +struct gfx_BufferResource_0; +struct gfx_ShaderProgram_0; +struct gfx_DescriptorSetLayout_0; +struct gfx_PipelineLayout_0; +struct gfx_DescriptorSet_0; +struct gfx_PipelineState_0; + +bool executeComputation_0(); + +gfx::ShaderProgram* loadShaderProgram(gfx::Renderer* renderer) { // First, we need to create a "session" for interacting with the Slang // compiler. This scopes all of our application's interactions @@ -159,54 +177,37 @@ RefPtr<gfx::ShaderProgram> loadShaderProgram(gfx::Renderer* renderer) programDesc.kernels = &kernelDescs[0]; programDesc.kernelCount = 2; - auto shaderProgram = renderer->createProgram(programDesc); + gShaderProgram = renderer->createProgram(programDesc); // Once we've used the output blobs from the Slang compiler to initialize // the API-specific shader program, we can release their memory. // computeShaderBlob->release(); - return shaderProgram; + return gShaderProgram; } // Now that we've covered the function that actually loads and // compiles our Slang shade code, we can go through the rest // of the application code without as much commentary. // -Result computeMain() +gfx::Window* createWindow(int windowWidth, int windowHeight) { - // We will hard-code the size of our rendering window and initial array. - // - int gWindowWidth = 1024; - int gWindowHeight = 768; - float initialArray[4] = { 3.0f, -20.0f, -6.0f, 8.0f }; - - // We will define global variables for the various platform and - // graphics API objects that our application needs: - // - // As a reminder, *none* of these are Slang API objects. All - // of them come from the utility library we are using to simplify - // building an example program. - // - gfx::ApplicationContext* gAppContext; - gfx::Window* gWindow; - RefPtr<gfx::Renderer> gRenderer; - - RefPtr<gfx::BufferResource> gUnorderedAccess; - RefPtr<gfx::BufferResource> gReadBuffer; - - RefPtr<gfx::PipelineLayout> gPipelineLayout; - RefPtr<gfx::PipelineState> gPipelineState; - RefPtr<gfx::DescriptorSet> gDescriptorSet; - // Create a window for our application to render into. // WindowDesc windowDesc; windowDesc.title = "Hello, World!"; - windowDesc.width = gWindowWidth; - windowDesc.height = gWindowHeight; - gWindow = createWindow(windowDesc); + windowDesc.width = windowWidth; + windowDesc.height = windowHeight; + return createWindow(windowDesc); + //return globalWindow; +} +gfx::Renderer* createRenderer( + int windowWidth, + int windowHeight, + gfx::Window* window) +{ // Initialize the rendering layer. // // Note: for now we are hard-coding logic to use the @@ -216,14 +217,18 @@ Result computeMain() // gRenderer = createD3D11Renderer(); Renderer::Desc rendererDesc; - rendererDesc.width = gWindowWidth; - rendererDesc.height = gWindowHeight; + rendererDesc.width = windowWidth; + rendererDesc.height = windowHeight; { - Result res = gRenderer->initialize(rendererDesc, getPlatformWindowHandle(gWindow)); - if(SLANG_FAILED(res)) return res; + Result res = gRenderer->initialize(rendererDesc, getPlatformWindowHandle(window)); + if (SLANG_FAILED(res)) return nullptr; } + return gRenderer; +} - // Create a structured buffer for passing the compute data +gfx::BufferResource* createStructuredBuffer(gfx::Renderer* renderer, float* initialArray) +{ + // Create a structured buffer for storing the data for computation // int structuredBufferSize = 4 * sizeof(float); @@ -233,23 +238,21 @@ Result computeMain() structuredBufferDesc.elementSize = 4; structuredBufferDesc.cpuAccessFlags = Resource::AccessFlag::Read; - gUnorderedAccess = gRenderer->createBufferResource( + gStructuredBuffer = renderer->createBufferResource( Resource::Usage::UnorderedAccess, structuredBufferDesc, initialArray); - if(!gUnorderedAccess) return SLANG_FAIL; - - // Now we will use our `loadShaderProgram` function to load - // the code from `shader.slang` into the graphics API. - // - RefPtr<ShaderProgram> shaderProgram = loadShaderProgram(gRenderer); - if(!shaderProgram) return SLANG_FAIL; + return gStructuredBuffer; +} +gfx::DescriptorSetLayout* buildDescriptorSetLayout(gfx::Renderer* renderer) +{ // Our example graphics API usess a "modern" D3D12/Vulkan style // of resource binding, so now we will dive into describing and // allocating "descriptor sets." // // First, we need to construct a descriptor set *layout*. + // DescriptorSetLayout::SlotRangeDesc slotRanges[] = { DescriptorSetLayout::SlotRangeDesc(DescriptorSlotType::StorageBuffer), @@ -257,78 +260,96 @@ Result computeMain() DescriptorSetLayout::Desc descriptorSetLayoutDesc; descriptorSetLayoutDesc.slotRangeCount = 1; descriptorSetLayoutDesc.slotRanges = &slotRanges[0]; - auto descriptorSetLayout = gRenderer->createDescriptorSetLayout(descriptorSetLayoutDesc); - if(!descriptorSetLayout) return SLANG_FAIL; + gDescriptorSetLayout = renderer->createDescriptorSetLayout(descriptorSetLayoutDesc); + return gDescriptorSetLayout; +} +gfx::PipelineLayout* buildPipeline(gfx::Renderer* renderer, gfx::DescriptorSetLayout* descriptorSetLayout) +{ // Next we will allocate a pipeline layout, which specifies // that we will render with only a single descriptor set bound. // PipelineLayout::DescriptorSetDesc descriptorSets[] = { - PipelineLayout::DescriptorSetDesc( descriptorSetLayout ), + PipelineLayout::DescriptorSetDesc(descriptorSetLayout), }; PipelineLayout::Desc pipelineLayoutDesc; pipelineLayoutDesc.renderTargetCount = 1; pipelineLayoutDesc.descriptorSetCount = 1; pipelineLayoutDesc.descriptorSets = &descriptorSets[0]; - auto pipelineLayout = gRenderer->createPipelineLayout(pipelineLayoutDesc); - if(!pipelineLayout) return SLANG_FAIL; + gPipelineLayout = renderer->createPipelineLayout(pipelineLayoutDesc); - gPipelineLayout = pipelineLayout; + return gPipelineLayout; +} +gfx::DescriptorSet* buildDescriptorSet( + gfx::Renderer* renderer, + gfx::DescriptorSetLayout* descriptorSetLayout, + gfx::BufferResource* structuredBuffer) +{ // Once we have the descriptor set layout, we can allocate // and fill in a descriptor set to hold our parameters. // - auto descriptorSet = gRenderer->createDescriptorSet(descriptorSetLayout); - if(!descriptorSet) return SLANG_FAIL; + gDescriptorSet = renderer->createDescriptorSet(descriptorSetLayout); + if(!gDescriptorSet) return nullptr; // Once we have the bufferResource created, we can fill in // a descriptor set for creating a structured buffer // ResourceView::Desc resourceViewDesc; resourceViewDesc.type = ResourceView::Type::UnorderedAccess; - auto resourceView = gRenderer->createBufferView(gUnorderedAccess, resourceViewDesc); - descriptorSet->setResource(0, 0, resourceView); + auto resourceView = renderer->createBufferView(structuredBuffer, resourceViewDesc); + gDescriptorSet->setResource(0, 0, resourceView); - gDescriptorSet = descriptorSet; + return gDescriptorSet; +} +gfx::PipelineState* buildPipelineState( + gfx::ShaderProgram* shaderProgram, + gfx::Renderer* renderer, + gfx::PipelineLayout* pipelineLayout) +{ // Following the D3D12/Vulkan style of API, we need a pipeline state object // (PSO) to encapsulate the configuration of the overall graphics pipeline. // ComputePipelineStateDesc desc; - desc.pipelineLayout = gPipelineLayout; + desc.pipelineLayout = pipelineLayout; desc.program = shaderProgram; - auto pipelineState = gRenderer->createComputePipelineState(desc); - if(!pipelineState) return SLANG_FAIL; - - gPipelineState = pipelineState; - - // Once we've initialized all the graphics API objects, - // it is time to show our application window and start rendering. - // - //showWindow(gWindow); - - // Now we configure our graphics pipeline state by setting the - // PSO, binding our descriptor set (which references the - // constant buffer that we wrote to above), and setting - // some additional bits of state, before drawing our triangle. - // + gPipelineState = renderer->createComputePipelineState(desc); + return gPipelineState; +} +void printInitialValues(float* initialArray, int length) +{ // Print out the values before the computation printf("Before:\n"); - for (float v : initialArray) + for (int i = 0; i < length; i++) { - printf("%f, ", v); + printf("%f, ", initialArray[i]); } printf("\n"); +} + +void dispatchComputation( + gfx::Renderer* gRenderer, + gfx::PipelineState* gPipelineState, + gfx::PipelineLayout* gPipelineLayout, + gfx::DescriptorSet* gDescriptorSet) +{ gRenderer->setPipelineState(PipelineType::Compute, gPipelineState); gRenderer->setDescriptorSet(PipelineType::Compute, gPipelineLayout, 0, gDescriptorSet); gRenderer->dispatchCompute(4, 1, 1); +} - if(float* outputData = (float*) gRenderer->map(gUnorderedAccess, MapFlavor::HostRead)) +void print_output( + gfx::Renderer* renderer, + gfx::BufferResource* structuredBuffer, + int length) +{ + if (float* outputData = (float*)renderer->map(structuredBuffer, MapFlavor::HostRead)) { // Print out the values the the kernel produced printf("After: \n"); @@ -338,10 +359,74 @@ Result computeMain() } printf("\n"); - gRenderer->unmap(gUnorderedAccess); + renderer->unmap(structuredBuffer); } +} + +// Boilerplate functions to help the slang-generated file and types +gfx_Window_0* createWindow_0(int32_t _0, int32_t _1) +{ + return (gfx_Window_0*)createWindow(_0, _1); +} + +gfx_Renderer_0* createRenderer_0(int32_t _0, int32_t _1, gfx_Window_0* _2) +{ + return (gfx_Renderer_0*)createRenderer(_0, _1, (gfx::Window*)_2); +} + +gfx_BufferResource_0* createStructuredBuffer_0(gfx_Renderer_0* _0, FixedArray<float, 4> _1) +{ + return (gfx_BufferResource_0*)createStructuredBuffer((gfx::Renderer*)_0, (float*)&_1); +} + +gfx_ShaderProgram_0* loadShaderProgram_0(gfx_Renderer_0* _0) +{ + return (gfx_ShaderProgram_0*)loadShaderProgram((gfx::Renderer*)_0); +} + +gfx_DescriptorSetLayout_0* buildDescriptorSetLayout_0(gfx_Renderer_0* _0) +{ + return (gfx_DescriptorSetLayout_0*)buildDescriptorSetLayout((gfx::Renderer*)_0); +} - return SLANG_OK; +gfx_PipelineLayout_0* buildPipeline_0(gfx_Renderer_0* _0, gfx_DescriptorSetLayout_0* _1) +{ + return (gfx_PipelineLayout_0*)buildPipeline((gfx::Renderer*)_0, (gfx::DescriptorSetLayout*)_1); +} + +gfx_DescriptorSet_0* buildDescriptorSet_0(gfx_Renderer_0* _0, gfx_DescriptorSetLayout_0* _1, gfx_BufferResource_0* _2) +{ + return (gfx_DescriptorSet_0*)buildDescriptorSet( + (gfx::Renderer*)_0, + (gfx::DescriptorSetLayout*)_1, + (gfx::BufferResource*)_2); +} + +gfx_PipelineState_0* buildPipelineState_0(gfx_ShaderProgram_0* _0, gfx_Renderer_0* _1, gfx_PipelineLayout_0* _2) +{ + return (gfx_PipelineState_0*)buildPipelineState( + (gfx::ShaderProgram*)_0, + (gfx::Renderer*)_1, + (gfx::PipelineLayout*)_2); +} + +void printInitialValues_0(FixedArray<float, 4> _0, int32_t _1) +{ + printInitialValues((float*)&_0, _1); +} + +void dispatchComputation_0(gfx_Renderer_0* _0, gfx_PipelineState_0* _1, gfx_PipelineLayout_0* _2, gfx_DescriptorSet_0* _3) +{ + dispatchComputation( + (gfx::Renderer*)_0, + (gfx::PipelineState*)_1, + (gfx::PipelineLayout*)_2, + (gfx::DescriptorSet*)_3); +} + +void print_output_0(gfx_Renderer_0* _0, gfx_BufferResource_0* _1, int32_t _2) +{ + print_output((gfx::Renderer*)_0, (gfx::BufferResource*)_1, _2); } // This "inner" main function is used by the platform abstraction @@ -354,7 +439,7 @@ void innerMain(ApplicationContext* context) // `struct` type, and then walk through the lifecyle // of the application. - if (SLANG_FAILED(computeMain())) + if (!(executeComputation_0())) { return exitApplication(context, 1); } |
