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 | |
| parent | a729c15e9dce9f5116a38afc66329ab2ca4cea54 (diff) | |
format
* format
* Minor test fixes
* enable checking cpp format in ci
Diffstat (limited to 'examples')
| -rw-r--r-- | examples/autodiff-texture/main.cpp | 272 | ||||
| -rw-r--r-- | examples/cpu-com-example/main.cpp | 54 | ||||
| -rw-r--r-- | examples/cpu-hello-world/main.cpp | 66 | ||||
| -rw-r--r-- | examples/example-base/example-base.cpp | 87 | ||||
| -rw-r--r-- | examples/example-base/example-base.h | 42 | ||||
| -rw-r--r-- | examples/example-base/test-base.cpp | 15 | ||||
| -rw-r--r-- | examples/example-base/test-base.h | 14 | ||||
| -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 | ||||
| -rw-r--r-- | examples/hello-world/main.cpp | 46 | ||||
| -rw-r--r-- | examples/hello-world/vulkan-api.cpp | 30 | ||||
| -rw-r--r-- | examples/hello-world/vulkan-api.h | 12 | ||||
| -rw-r--r-- | examples/model-viewer/main.cpp | 605 | ||||
| -rw-r--r-- | examples/nv-aftermath-example/main.cpp | 264 | ||||
| -rw-r--r-- | examples/platform-test/main.cpp | 188 | ||||
| -rw-r--r-- | examples/ray-tracing-pipeline/main.cpp | 1076 | ||||
| -rw-r--r-- | examples/ray-tracing/main.cpp | 1006 | ||||
| -rw-r--r-- | examples/shader-object/main.cpp | 31 | ||||
| -rw-r--r-- | examples/shader-toy/main.cpp | 635 | ||||
| -rw-r--r-- | examples/triangle/main.cpp | 629 |
21 files changed, 2911 insertions, 2579 deletions
diff --git a/examples/autodiff-texture/main.cpp b/examples/autodiff-texture/main.cpp index 8bffadd84..d09b7dbac 100644 --- a/examples/autodiff-texture/main.cpp +++ b/examples/autodiff-texture/main.cpp @@ -2,10 +2,10 @@ #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/vector-math.h" #include "tools/platform/window.h" -#include "slang.h" using namespace gfx; using namespace Slang; @@ -41,14 +41,18 @@ struct AutoDiffTexture : public WindowedAppBase } gfx::Result loadRenderProgram( - gfx::IDevice* device, const char* fileName, const char* fragmentShader, gfx::IShaderProgram** outProgram) + gfx::IDevice* device, + const char* fileName, + const char* fragmentShader, + gfx::IShaderProgram** outProgram) { ComPtr<slang::ISession> slangSession; slangSession = device->getSlangSession(); ComPtr<slang::IBlob> diagnosticsBlob; Slang::String path = resourceBase.resolveResource(fileName); - slang::IModule* module = slangSession->loadModule(path.getBuffer(), diagnosticsBlob.writeRef()); + slang::IModule* module = + slangSession->loadModule(path.getBuffer(), diagnosticsBlob.writeRef()); diagnoseIfNeeded(diagnosticsBlob); if (!module) return SLANG_FAIL; @@ -91,14 +95,17 @@ struct AutoDiffTexture : public WindowedAppBase } gfx::Result loadComputeProgram( - gfx::IDevice* device, const char* fileName, gfx::IShaderProgram** outProgram) + gfx::IDevice* device, + const char* fileName, + gfx::IShaderProgram** outProgram) { ComPtr<slang::ISession> slangSession; slangSession = device->getSlangSession(); ComPtr<slang::IBlob> diagnosticsBlob; Slang::String path = resourceBase.resolveResource(fileName); - slang::IModule* module = slangSession->loadModule(path.getBuffer(), diagnosticsBlob.writeRef()); + slang::IModule* module = + slangSession->loadModule(path.getBuffer(), diagnosticsBlob.writeRef()); diagnoseIfNeeded(diagnosticsBlob); if (!module) return SLANG_FAIL; @@ -174,7 +181,11 @@ struct AutoDiffTexture : public WindowedAppBase ClearValue kClearValue; bool resetLearntTexture = false; - ComPtr<gfx::ITextureResource> createRenderTargetTexture(gfx::Format format, int w, int h, int levels) + ComPtr<gfx::ITextureResource> createRenderTargetTexture( + gfx::Format format, + int w, + int h, + int levels) { gfx::ITextureResource::Desc textureDesc = {}; textureDesc.allowedStates.add(ResourceState::ShaderResource); @@ -239,7 +250,8 @@ struct AutoDiffTexture : public WindowedAppBase return gDevice->createTextureView(tex, rtvDesc); } ComPtr<gfx::IPipelineState> createRenderPipelineState( - IInputLayout* inputLayout, IShaderProgram* program) + IInputLayout* inputLayout, + IShaderProgram* program) { GraphicsPipelineStateDesc desc; desc.inputLayout = inputLayout; @@ -269,7 +281,7 @@ struct AutoDiffTexture : public WindowedAppBase desc.subresourceRange.layerCount = 1; desc.subresourceRange.mipLevel = level; desc.subresourceRange.baseArrayLayer = 0; - return gDevice->createTextureView(texture,desc); + return gDevice->createTextureView(texture, desc); } Slang::Result initialize() { @@ -320,20 +332,29 @@ struct AutoDiffTexture : public WindowedAppBase { ComPtr<IShaderProgram> shaderProgram; - SLANG_RETURN_ON_FAIL( - loadRenderProgram(gDevice, "train.slang", "fragmentMain", shaderProgram.writeRef())); + SLANG_RETURN_ON_FAIL(loadRenderProgram( + gDevice, + "train.slang", + "fragmentMain", + shaderProgram.writeRef())); gRefPipelineState = createRenderPipelineState(inputLayout, shaderProgram); } { ComPtr<IShaderProgram> shaderProgram; - SLANG_RETURN_ON_FAIL( - loadRenderProgram(gDevice, "train.slang", "diffFragmentMain", shaderProgram.writeRef())); + SLANG_RETURN_ON_FAIL(loadRenderProgram( + gDevice, + "train.slang", + "diffFragmentMain", + shaderProgram.writeRef())); gIterPipelineState = createRenderPipelineState(inputLayout, shaderProgram); } { ComPtr<IShaderProgram> shaderProgram; - SLANG_RETURN_ON_FAIL( - loadRenderProgram(gDevice, "draw-quad.slang", "fragmentMain", shaderProgram.writeRef())); + SLANG_RETURN_ON_FAIL(loadRenderProgram( + gDevice, + "draw-quad.slang", + "fragmentMain", + shaderProgram.writeRef())); gDrawQuadPipelineState = createRenderPipelineState(inputLayout, shaderProgram); } { @@ -344,17 +365,20 @@ struct AutoDiffTexture : public WindowedAppBase } { ComPtr<IShaderProgram> shaderProgram; - SLANG_RETURN_ON_FAIL(loadComputeProgram(gDevice, "convert.slang", shaderProgram.writeRef())); + SLANG_RETURN_ON_FAIL( + loadComputeProgram(gDevice, "convert.slang", shaderProgram.writeRef())); gConvertPipelineState = createComputePipelineState(shaderProgram); } { ComPtr<IShaderProgram> shaderProgram; - SLANG_RETURN_ON_FAIL(loadComputeProgram(gDevice, "buildmip.slang", shaderProgram.writeRef())); + SLANG_RETURN_ON_FAIL( + loadComputeProgram(gDevice, "buildmip.slang", shaderProgram.writeRef())); gBuildMipPipelineState = createComputePipelineState(shaderProgram); } { ComPtr<IShaderProgram> shaderProgram; - SLANG_RETURN_ON_FAIL(loadComputeProgram(gDevice, "learnmip.slang", shaderProgram.writeRef())); + SLANG_RETURN_ON_FAIL( + loadComputeProgram(gDevice, "learnmip.slang", shaderProgram.writeRef())); gLearnMipPipelineState = createComputePipelineState(shaderProgram); } @@ -394,7 +418,7 @@ struct AutoDiffTexture : public WindowedAppBase gDiffTextureUAVs.add(createUAV(gDiffTexture, i)); gfx::ISamplerState::Desc samplerDesc = {}; - //samplerDesc.maxLOD = 0.0f; + // samplerDesc.maxLOD = 0.0f; gSampler = gDevice->createSamplerState(samplerDesc); gDepthTexture = createDepthTexture(); @@ -404,7 +428,8 @@ struct AutoDiffTexture : public WindowedAppBase gRefImageRTV = createRTV(gRefImage, Format::R8G8B8A8_UNORM); gRefImageSRV = createSRV(gRefImage); - gIterImage = createRenderTargetTexture(Format::R8G8B8A8_UNORM, windowWidth, windowHeight, 1); + gIterImage = + createRenderTargetTexture(Format::R8G8B8A8_UNORM, windowWidth, windowHeight, 1); gIterImageRTV = createRTV(gIterImage, Format::R8G8B8A8_UNORM); gIterImageSRV = createSRV(gIterImage); @@ -414,17 +439,38 @@ struct AutoDiffTexture : public WindowedAppBase { ComPtr<ICommandBuffer> commandBuffer = gTransientHeaps[0]->createCommandBuffer(); auto encoder = commandBuffer->encodeResourceCommands(); - encoder->textureBarrier(gLearningTexture, ResourceState::RenderTarget, ResourceState::UnorderedAccess); - encoder->textureBarrier(gDiffTexture, ResourceState::RenderTarget, ResourceState::UnorderedAccess); - encoder->textureBarrier(gRefImage, ResourceState::RenderTarget, ResourceState::ShaderResource); - encoder->textureBarrier(gIterImage, ResourceState::RenderTarget, ResourceState::ShaderResource); + encoder->textureBarrier( + gLearningTexture, + ResourceState::RenderTarget, + ResourceState::UnorderedAccess); + encoder->textureBarrier( + gDiffTexture, + ResourceState::RenderTarget, + ResourceState::UnorderedAccess); + encoder->textureBarrier( + gRefImage, + ResourceState::RenderTarget, + ResourceState::ShaderResource); + encoder->textureBarrier( + gIterImage, + ResourceState::RenderTarget, + ResourceState::ShaderResource); for (int i = 0; i < gLearningTextureUAVs.getCount(); i++) { ClearValue clearValue = {}; - encoder->clearResourceView(gLearningTextureUAVs[i], &clearValue, ClearResourceViewFlags::None); - encoder->clearResourceView(gDiffTextureUAVs[i], &clearValue, ClearResourceViewFlags::None); + encoder->clearResourceView( + gLearningTextureUAVs[i], + &clearValue, + ClearResourceViewFlags::None); + encoder->clearResourceView( + gDiffTextureUAVs[i], + &clearValue, + ClearResourceViewFlags::None); } - encoder->textureBarrier(gLearningTexture, ResourceState::UnorderedAccess, ResourceState::ShaderResource); + encoder->textureBarrier( + gLearningTexture, + ResourceState::UnorderedAccess, + ResourceState::ShaderResource); encoder->endEncoding(); commandBuffer->close(); @@ -453,7 +499,10 @@ struct AutoDiffTexture : public WindowedAppBase float rotX = (rand() / (float)RAND_MAX) * 0.3f; float rotY = (rand() / (float)RAND_MAX) * 0.2f; glm::mat4x4 matProj = glm::perspectiveRH_ZO( - glm::radians(60.0f), (float)windowWidth / (float)windowHeight, 0.1f, 1000.0f); + glm::radians(60.0f), + (float)windowWidth / (float)windowHeight, + 0.1f, + 1000.0f); auto identity = glm::mat4(1.0f); auto translate = glm::translate( identity, @@ -468,9 +517,11 @@ struct AutoDiffTexture : public WindowedAppBase return transformMatrix; } - template <typename SetupPipelineFunc> + template<typename SetupPipelineFunc> void renderImage( - int transientHeapIndex, IFramebuffer* fb, const SetupPipelineFunc& setupPipeline) + int transientHeapIndex, + IFramebuffer* fb, + const SetupPipelineFunc& setupPipeline) { ComPtr<ICommandBuffer> commandBuffer = gTransientHeaps[transientHeapIndex]->createCommandBuffer(); @@ -496,9 +547,13 @@ struct AutoDiffTexture : public WindowedAppBase void renderReferenceImage(int transientHeapIndex, glm::mat4x4 transformMatrix) { { - ComPtr<ICommandBuffer> commandBuffer = gTransientHeaps[transientHeapIndex]->createCommandBuffer(); + ComPtr<ICommandBuffer> commandBuffer = + gTransientHeaps[transientHeapIndex]->createCommandBuffer(); auto encoder = commandBuffer->encodeResourceCommands(); - encoder->textureBarrier(gRefImage, ResourceState::ShaderResource, ResourceState::RenderTarget); + encoder->textureBarrier( + gRefImage, + ResourceState::ShaderResource, + ResourceState::RenderTarget); encoder->endEncoding(); commandBuffer->close(); gQueue->executeCommandBuffer(commandBuffer); @@ -512,12 +567,16 @@ struct AutoDiffTexture : public WindowedAppBase auto rootObject = encoder->bindPipeline(gRefPipelineState); ShaderCursor rootCursor(rootObject); rootCursor["Uniforms"]["modelViewProjection"].setData( - &transformMatrix, sizeof(float) * 16); + &transformMatrix, + sizeof(float) * 16); rootCursor["Uniforms"]["bwdTexture"]["texture"].setResource(gTexView); rootCursor["Uniforms"]["sampler"].setSampler(gSampler); - rootCursor["Uniforms"]["mipOffset"].setData(mipMapOffset.getBuffer(), sizeof(uint32_t) * mipMapOffset.getCount()); + rootCursor["Uniforms"]["mipOffset"].setData( + mipMapOffset.getBuffer(), + sizeof(uint32_t) * mipMapOffset.getCount()); rootCursor["Uniforms"]["texRef"].setResource(gTexView); - rootCursor["Uniforms"]["bwdTexture"]["accumulateBuffer"].setResource(gAccumulateBufferView); + rootCursor["Uniforms"]["bwdTexture"]["accumulateBuffer"].setResource( + gAccumulateBufferView); }); } @@ -527,25 +586,52 @@ struct AutoDiffTexture : public WindowedAppBase frameCount++; auto transformMatrix = getTransformMatrix(); renderReferenceImage(frameBufferIndex, transformMatrix); - + // Barriers. { ComPtr<ICommandBuffer> commandBuffer = gTransientHeaps[frameBufferIndex]->createCommandBuffer(); auto resEncoder = commandBuffer->encodeResourceCommands(); ClearValue clearValue = {}; - resEncoder->bufferBarrier(gAccumulateBuffer, ResourceState::Undefined, ResourceState::UnorderedAccess); - resEncoder->bufferBarrier(gReconstructBuffer, ResourceState::Undefined, ResourceState::UnorderedAccess); - resEncoder->textureBarrier(gRefImage, ResourceState::Present, ResourceState::ShaderResource); - resEncoder->textureBarrier(gIterImage, ResourceState::ShaderResource, ResourceState::RenderTarget); - resEncoder->clearResourceView(gAccumulateBufferView, &clearValue, ClearResourceViewFlags::None); - resEncoder->clearResourceView(gReconstructBufferView, &clearValue, ClearResourceViewFlags::None); + resEncoder->bufferBarrier( + gAccumulateBuffer, + ResourceState::Undefined, + ResourceState::UnorderedAccess); + resEncoder->bufferBarrier( + gReconstructBuffer, + ResourceState::Undefined, + ResourceState::UnorderedAccess); + resEncoder->textureBarrier( + gRefImage, + ResourceState::Present, + ResourceState::ShaderResource); + resEncoder->textureBarrier( + gIterImage, + ResourceState::ShaderResource, + ResourceState::RenderTarget); + resEncoder->clearResourceView( + gAccumulateBufferView, + &clearValue, + ClearResourceViewFlags::None); + resEncoder->clearResourceView( + gReconstructBufferView, + &clearValue, + ClearResourceViewFlags::None); if (resetLearntTexture) { - resEncoder->textureBarrier(gLearningTexture, ResourceState::ShaderResource, ResourceState::UnorderedAccess); - for (Index i =0; i <gLearningTextureUAVs.getCount(); i++) - resEncoder->clearResourceView(gLearningTextureUAVs[i], &clearValue, ClearResourceViewFlags::None); - resEncoder->textureBarrier(gLearningTexture, ResourceState::UnorderedAccess, ResourceState::ShaderResource); + resEncoder->textureBarrier( + gLearningTexture, + ResourceState::ShaderResource, + ResourceState::UnorderedAccess); + for (Index i = 0; i < gLearningTextureUAVs.getCount(); i++) + resEncoder->clearResourceView( + gLearningTextureUAVs[i], + &clearValue, + ClearResourceViewFlags::None); + resEncoder->textureBarrier( + gLearningTexture, + ResourceState::UnorderedAccess, + ResourceState::ShaderResource); resetLearntTexture = false; } resEncoder->endEncoding(); @@ -561,16 +647,19 @@ struct AutoDiffTexture : public WindowedAppBase { auto rootObject = encoder->bindPipeline(gIterPipelineState); ShaderCursor rootCursor(rootObject); - + rootCursor["Uniforms"]["modelViewProjection"].setData( - &transformMatrix, sizeof(float) * 16); + &transformMatrix, + sizeof(float) * 16); rootCursor["Uniforms"]["bwdTexture"]["texture"].setResource(gLearningTextureSRV); rootCursor["Uniforms"]["sampler"].setSampler(gSampler); - rootCursor["Uniforms"]["mipOffset"].setData(mipMapOffset.getBuffer(), sizeof(uint32_t) * mipMapOffset.getCount()); + rootCursor["Uniforms"]["mipOffset"].setData( + mipMapOffset.getBuffer(), + sizeof(uint32_t) * mipMapOffset.getCount()); rootCursor["Uniforms"]["texRef"].setResource(gRefImageSRV); - rootCursor["Uniforms"]["bwdTexture"]["accumulateBuffer"].setResource(gAccumulateBufferView); + rootCursor["Uniforms"]["bwdTexture"]["accumulateBuffer"].setResource( + gAccumulateBufferView); rootCursor["Uniforms"]["bwdTexture"]["minLOD"].setData(5.0); - }); // Propagete gradients through mip map layers from top (lowest res) to bottom (highest res). @@ -578,12 +667,17 @@ struct AutoDiffTexture : public WindowedAppBase ComPtr<ICommandBuffer> commandBuffer = gTransientHeaps[frameBufferIndex]->createCommandBuffer(); auto encoder = commandBuffer->encodeComputeCommands(); - encoder->textureBarrier(gLearningTexture, ResourceState::ShaderResource, ResourceState::UnorderedAccess); + encoder->textureBarrier( + gLearningTexture, + ResourceState::ShaderResource, + ResourceState::UnorderedAccess); auto rootObject = encoder->bindPipeline(gReconstructPipelineState); for (int i = (int)mipMapOffset.getCount() - 2; i >= 0; i--) { ShaderCursor rootCursor(rootObject); - rootCursor["Uniforms"]["mipOffset"].setData(mipMapOffset.getBuffer(), sizeof(uint32_t) * mipMapOffset.getCount()); + rootCursor["Uniforms"]["mipOffset"].setData( + mipMapOffset.getBuffer(), + sizeof(uint32_t) * mipMapOffset.getCount()); rootCursor["Uniforms"]["dstLayer"].setData(i); rootCursor["Uniforms"]["layerCount"].setData(mipMapOffset.getCount() - 1); rootCursor["Uniforms"]["width"].setData(textureWidth); @@ -591,22 +685,31 @@ struct AutoDiffTexture : public WindowedAppBase rootCursor["Uniforms"]["accumulateBuffer"].setResource(gAccumulateBufferView); rootCursor["Uniforms"]["dstBuffer"].setResource(gReconstructBufferView); encoder->dispatchCompute( - ((textureWidth >> i) + 15) / 16, ((textureHeight >> i) + 15) / 16, 1); - encoder->bufferBarrier(gReconstructBuffer, ResourceState::UnorderedAccess, ResourceState::UnorderedAccess); + ((textureWidth >> i) + 15) / 16, + ((textureHeight >> i) + 15) / 16, + 1); + encoder->bufferBarrier( + gReconstructBuffer, + ResourceState::UnorderedAccess, + ResourceState::UnorderedAccess); } // Convert bottom layer mip from buffer to texture. rootObject = encoder->bindPipeline(gConvertPipelineState); ShaderCursor rootCursor(rootObject); - rootCursor["Uniforms"]["mipOffset"].setData(mipMapOffset.getBuffer(), sizeof(uint32_t) * mipMapOffset.getCount()); + rootCursor["Uniforms"]["mipOffset"].setData( + mipMapOffset.getBuffer(), + sizeof(uint32_t) * mipMapOffset.getCount()); rootCursor["Uniforms"]["dstLayer"].setData(0); rootCursor["Uniforms"]["width"].setData(textureWidth); rootCursor["Uniforms"]["height"].setData(textureHeight); rootCursor["Uniforms"]["srcBuffer"].setResource(gReconstructBufferView); rootCursor["Uniforms"]["dstTexture"].setResource(gDiffTextureUAVs[0]); - encoder->dispatchCompute( - (textureWidth + 15) / 16, (textureHeight + 15) / 16, 1); - encoder->textureBarrier(gDiffTexture, ResourceState::UnorderedAccess, ResourceState::UnorderedAccess); + encoder->dispatchCompute((textureWidth + 15) / 16, (textureHeight + 15) / 16, 1); + encoder->textureBarrier( + gDiffTexture, + ResourceState::UnorderedAccess, + ResourceState::UnorderedAccess); // Build higher level mip map layers. rootObject = encoder->bindPipeline(gBuildMipPipelineState); @@ -615,11 +718,16 @@ struct AutoDiffTexture : public WindowedAppBase ShaderCursor rootCursor(rootObject); rootCursor["Uniforms"]["dstWidth"].setData(textureWidth >> i); rootCursor["Uniforms"]["dstHeight"].setData(textureHeight >> i); - rootCursor["Uniforms"]["srcTexture"].setResource(gDiffTextureUAVs[i-1]); + rootCursor["Uniforms"]["srcTexture"].setResource(gDiffTextureUAVs[i - 1]); rootCursor["Uniforms"]["dstTexture"].setResource(gDiffTextureUAVs[i]); encoder->dispatchCompute( - ((textureWidth >> i) + 15) / 16, ((textureHeight >> i) + 15) / 16, 1); - encoder->textureBarrier(gDiffTexture, ResourceState::UnorderedAccess, ResourceState::UnorderedAccess); + ((textureWidth >> i) + 15) / 16, + ((textureHeight >> i) + 15) / 16, + 1); + encoder->textureBarrier( + gDiffTexture, + ResourceState::UnorderedAccess, + ResourceState::UnorderedAccess); } // Accumulate gradients to learnt texture. @@ -633,10 +741,18 @@ struct AutoDiffTexture : public WindowedAppBase rootCursor["Uniforms"]["srcTexture"].setResource(gDiffTextureUAVs[i]); rootCursor["Uniforms"]["dstTexture"].setResource(gLearningTextureUAVs[i]); encoder->dispatchCompute( - ((textureWidth >> i) + 15) / 16, ((textureHeight >> i) + 15) / 16, 1); + ((textureWidth >> i) + 15) / 16, + ((textureHeight >> i) + 15) / 16, + 1); } - encoder->textureBarrier(gLearningTexture, ResourceState::UnorderedAccess, ResourceState::ShaderResource); - encoder->textureBarrier(gIterImage, ResourceState::Present, ResourceState::ShaderResource); + encoder->textureBarrier( + gLearningTexture, + ResourceState::UnorderedAccess, + ResourceState::ShaderResource); + encoder->textureBarrier( + gIterImage, + ResourceState::Present, + ResourceState::ShaderResource); encoder->endEncoding(); commandBuffer->close(); @@ -647,12 +763,25 @@ struct AutoDiffTexture : public WindowedAppBase { ComPtr<ICommandBuffer> commandBuffer = gTransientHeaps[frameBufferIndex]->createCommandBuffer(); - auto renderEncoder = commandBuffer->encodeRenderCommands(gRenderPass, gFramebuffers[frameBufferIndex]); + auto renderEncoder = + commandBuffer->encodeRenderCommands(gRenderPass, gFramebuffers[frameBufferIndex]); drawTexturedQuad(renderEncoder, 0, 0, textureWidth, textureHeight, gLearningTextureSRV); int refImageWidth = windowWidth - textureWidth - 10; int refImageHeight = refImageWidth * windowHeight / windowWidth; - drawTexturedQuad(renderEncoder, textureWidth + 10, 0, refImageWidth, refImageHeight, gRefImageSRV); - drawTexturedQuad(renderEncoder, textureWidth + 10, refImageHeight + 10, refImageWidth, refImageHeight, gIterImageSRV); + drawTexturedQuad( + renderEncoder, + textureWidth + 10, + 0, + refImageWidth, + refImageHeight, + gRefImageSRV); + drawTexturedQuad( + renderEncoder, + textureWidth + 10, + refImageHeight + 10, + refImageWidth, + refImageHeight, + gIterImageSRV); renderEncoder->endEncoding(); commandBuffer->close(); gQueue->executeCommandBuffer(commandBuffer); @@ -664,7 +793,13 @@ struct AutoDiffTexture : public WindowedAppBase } } - void drawTexturedQuad(IRenderCommandEncoder* renderEncoder, int x, int y, int w, int h, IResourceView* srv) + void drawTexturedQuad( + IRenderCommandEncoder* renderEncoder, + int x, + int y, + int w, + int h, + IResourceView* srv) { gfx::Viewport viewport = {}; viewport.maxZ = 1.0f; @@ -686,7 +821,6 @@ struct AutoDiffTexture : public WindowedAppBase renderEncoder->setPrimitiveTopology(PrimitiveTopology::TriangleStrip); renderEncoder->draw(4); } - }; PLATFORM_UI_MAIN(innerMain<AutoDiffTexture>) diff --git a/examples/cpu-com-example/main.cpp b/examples/cpu-com-example/main.cpp index 62a01d17f..382b3cacd 100644 --- a/examples/cpu-com-example/main.cpp +++ b/examples/cpu-com-example/main.cpp @@ -1,11 +1,10 @@ // main.cpp -#include <stdio.h> - +#include "slang-com-helper.h" +#include "slang-com-ptr.h" #include "slang.h" -#include "slang-com-ptr.h" -#include "slang-com-helper.h" +#include <stdio.h> // This includes a useful small function for setting up the prelude (described more further below). #include "../../source/core/slang-test-tool-util.h" @@ -17,9 +16,9 @@ using namespace Slang; static const ExampleResources resourceBase("cpu-com-example"); -// For the moment we have to explicitly write the Slang COM interface in C++ code. It *MUST* match +// For the moment we have to explicitly write the Slang COM interface in C++ code. It *MUST* match // the interface in the slang source -// As it stands all interfaces need to derive from ISlangUnknown (or IUnknown). +// As it stands all interfaces need to derive from ISlangUnknown (or IUnknown). class IDoThings : public ISlangUnknown { public: @@ -43,19 +42,33 @@ class DoThings : public IDoThings { public: // We don't need queryInterface for this impl, or ref counting - virtual SLANG_NO_THROW SlangResult SLANG_MCALL queryInterface(SlangUUID const& uuid, void** outObject) SLANG_OVERRIDE { return SLANG_E_NOT_IMPLEMENTED; } - virtual SLANG_NO_THROW uint32_t SLANG_MCALL addRef() SLANG_OVERRIDE { return 1; } - virtual SLANG_NO_THROW uint32_t SLANG_MCALL release() SLANG_OVERRIDE { return 1; } + virtual SLANG_NO_THROW SlangResult SLANG_MCALL + queryInterface(SlangUUID const& uuid, void** outObject) SLANG_OVERRIDE + { + return SLANG_E_NOT_IMPLEMENTED; + } + virtual SLANG_NO_THROW uint32_t SLANG_MCALL addRef() SLANG_OVERRIDE { return 1; } + virtual SLANG_NO_THROW uint32_t SLANG_MCALL release() SLANG_OVERRIDE { return 1; } // IDoThings - virtual SLANG_NO_THROW int SLANG_MCALL doThing(int a, int b) SLANG_OVERRIDE { return a + b + 1; } - virtual SLANG_NO_THROW int SLANG_MCALL calcHash(const char* in) SLANG_OVERRIDE { return (int)_calcHash(in); } - virtual SLANG_NO_THROW void SLANG_MCALL printMessage(const char* in) SLANG_OVERRIDE { printf("%s\n", in); } + virtual SLANG_NO_THROW int SLANG_MCALL doThing(int a, int b) SLANG_OVERRIDE + { + return a + b + 1; + } + virtual SLANG_NO_THROW int SLANG_MCALL calcHash(const char* in) SLANG_OVERRIDE + { + return (int)_calcHash(in); + } + virtual SLANG_NO_THROW void SLANG_MCALL printMessage(const char* in) SLANG_OVERRIDE + { + printf("%s\n", in); + } }; static SlangResult _innerMain(int argc, char** argv) { - // NOTE! This example only works if `slang-llvm` or a C++ compiler that Slang supports is available. + // NOTE! This example only works if `slang-llvm` or a C++ compiler that Slang supports is + // available. // Create the session ComPtr<slang::IGlobalSession> slangSession; @@ -72,16 +85,17 @@ static SlangResult _innerMain(int argc, char** argv) SLANG_ALLOW_DEPRECATED_END // We want to compile to 'HOST_CALLABLE' here such that we can execute the Slang code. - // - // Note that it is possible to use HOST_HOST_CALLABLE, but this currently only works with 'regular' C++ compilers - // not with `slang-llvm`. + // + // Note that it is possible to use HOST_HOST_CALLABLE, but this currently only works with + // 'regular' C++ compilers not with `slang-llvm`. const int targetIndex = request->addCodeGenTarget(SLANG_SHADER_HOST_CALLABLE); // Set the target flag to indicate that we want to compile all into a library. request->setTargetFlags(targetIndex, SLANG_TARGET_FLAG_GENERATE_WHOLE_PROGRAM); // Add the translation unit - const int translationUnitIndex = request->addTranslationUnit(SLANG_SOURCE_LANGUAGE_SLANG, nullptr); + const int translationUnitIndex = + request->addTranslationUnit(SLANG_SOURCE_LANGUAGE_SLANG, nullptr); // Set the source file for the translation unit Slang::String path = resourceBase.resolveResource("shader.slang"); @@ -93,13 +107,13 @@ static SlangResult _innerMain(int argc, char** argv) // compiler may have produced "diagnostic" output such as warnings. // We will go ahead and print that output here. // - if(auto diagnostics = request->getDiagnosticOutput()) + if (auto diagnostics = request->getDiagnosticOutput()) { printf("%s", diagnostics); } - // Get the 'shared library' (note that this doesn't necessarily have to be implemented as a shared library - // it's just an interface to executable code). + // Get the 'shared library' (note that this doesn't necessarily have to be implemented as a + // shared library it's just an interface to executable code). ComPtr<ISlangSharedLibrary> sharedLibrary; SLANG_RETURN_ON_FAIL(request->getTargetHostCallable(0, sharedLibrary.writeRef())); diff --git a/examples/cpu-hello-world/main.cpp b/examples/cpu-hello-world/main.cpp index 059efc8d3..60a24fa8c 100644 --- a/examples/cpu-hello-world/main.cpp +++ b/examples/cpu-hello-world/main.cpp @@ -3,7 +3,7 @@ #include <stdio.h> // This file implements an extremely simple example of loading and -// executing a Slang shader program on the CPU. +// executing a Slang shader program on the CPU. // // More information about generation C++ or CPU code can be found in docs/cpu-target.md // @@ -20,7 +20,7 @@ // Allows use of ComPtr - which we can use to scope any 'com-like' pointers easily #include "slang-com-ptr.h" -// Provides macros for handling SlangResult values easily +// Provides macros for handling SlangResult values easily #include "slang-com-helper.h" // This includes a useful small function for setting up the prelude (described more further below). @@ -34,10 +34,10 @@ using namespace Slang; // Slang source is converted into C++ code which is compiled by a backend compiler. // That process uses a 'prelude' which defines types and functions that are needed // for everything else to work. -// +// // We include the prelude here, so we can directly use the types as were used by the // compiled code. It is not necessary to include the prelude, as long as memory is -// laid out in the manner that the generated slang code expects. +// laid out in the manner that the generated slang code expects. #define SLANG_PRELUDE_NAMESPACE CPPPrelude #include "../../prelude/slang-cpp-types.h" @@ -75,11 +75,13 @@ static SlangResult _innerMain(int argc, char** argv) // // Most downstream C++ compilers work on files. In that case slang may generate temporary // files that contain the generated code. Typically the generated files will not be in the - // same directory as the original source so handling includes becomes awkward. The mechanism used here - // is for the prelude code to be an *absolute* path to the 'slang-cpp-prelude.h' - which means - // this will work wherever the generated code is, and allows accessing other files via relative paths. + // same directory as the original source so handling includes becomes awkward. The mechanism + // used here is for the prelude code to be an *absolute* path to the 'slang-cpp-prelude.h' - + // which means this will work wherever the generated code is, and allows accessing other files + // via relative paths. // - // Look at the source to TestToolUtil::setSessionDefaultPreludeFromExePath to see what's involed. + // Look at the source to TestToolUtil::setSessionDefaultPreludeFromExePath to see what's + // involed. TestToolUtil::setSessionDefaultPreludeFromExePath(argv[0], slangSession); slang::SessionDesc sessionDesc = {}; @@ -122,13 +124,16 @@ static SlangResult _innerMain(int argc, char** argv) SLANG_RETURN_ON_FAIL(result); } - // Get the 'shared library' (note that this doesn't necessarily have to be implemented as a shared library - // it's just an interface to executable code). + // Get the 'shared library' (note that this doesn't necessarily have to be implemented as a + // shared library it's just an interface to executable code). ComPtr<ISlangSharedLibrary> sharedLibrary; { ComPtr<slang::IBlob> diagnosticsBlob; SlangResult result = composedProgram->getEntryPointHostCallable( - 0, 0, sharedLibrary.writeRef(), diagnosticsBlob.writeRef()); + 0, + 0, + sharedLibrary.writeRef(), + diagnosticsBlob.writeRef()); diagnoseIfNeeded(diagnosticsBlob); SLANG_RETURN_ON_FAIL(result); if (testBase.isTestMode()) @@ -137,55 +142,58 @@ static SlangResult _innerMain(int argc, char** argv) } } // Once we have the sharedLibrary, we no longer need the request - // unless we want to use reflection, to for example workout how 'UniformState' and 'UniformEntryPointParams' are laid out - // at runtime. We don't do that here - as we hard code the structures. + // unless we want to use reflection, to for example workout how 'UniformState' and + // 'UniformEntryPointParams' are laid out at runtime. We don't do that here - as we hard code + // the structures. // Get the function we are going to execute const char entryPointName[] = "computeMain"; - CPPPrelude::ComputeFunc func = (CPPPrelude::ComputeFunc)sharedLibrary->findFuncByName(entryPointName); + CPPPrelude::ComputeFunc func = + (CPPPrelude::ComputeFunc)sharedLibrary->findFuncByName(entryPointName); if (!func) { return SLANG_FAIL; } // Define the uniform state structure that is *specific* to our shader defined in shader.slang - // That the layout of the structure can be determined through reflection, or can be inferred from - // the original slang source. Look at the documentation in docs/cpu-target.md which describes - // how different resources map. - // The order of the resources is in the order that they are defined in the source. + // That the layout of the structure can be determined through reflection, or can be inferred + // from the original slang source. Look at the documentation in docs/cpu-target.md which + // describes how different resources map. The order of the resources is in the order that they + // are defined in the source. struct UniformState { CPPPrelude::RWStructuredBuffer<float> ioBuffer; }; - // the uniformState will be passed as a pointer to the CPU code + // the uniformState will be passed as a pointer to the CPU code UniformState uniformState; // The contents of the buffer are modified, so we'll copy it - const float startBufferContents[] = { 2.0f, -10.0f, -3.0f, 5.0f }; + const float startBufferContents[] = {2.0f, -10.0f, -3.0f, 5.0f}; float bufferContents[SLANG_COUNT_OF(startBufferContents)]; memcpy(bufferContents, startBufferContents, sizeof(startBufferContents)); // Set up the ioBuffer such that it uses bufferContents. It is important to set the .count - // such that bounds checking can be performed in the kernel. + // such that bounds checking can be performed in the kernel. uniformState.ioBuffer.data = bufferContents; uniformState.ioBuffer.count = SLANG_COUNT_OF(bufferContents); - // In shader.slang, then entry point is attributed with `[numthreads(4, 1, 1)]` meaning each group - // consists of 4 'thread' in x. Our input buffer is 4 wide, and we index the input array via `SV_DispatchThreadID` - // so we only need to run a single group to execute over all of the 4 elements here. - // The group range from { 0, 0, 0 } -> { 1, 1, 1 } means it will execute over the single group { 0, 0, 0 }. + // In shader.slang, then entry point is attributed with `[numthreads(4, 1, 1)]` meaning each + // group consists of 4 'thread' in x. Our input buffer is 4 wide, and we index the input array + // via `SV_DispatchThreadID` so we only need to run a single group to execute over all of the 4 + // elements here. The group range from { 0, 0, 0 } -> { 1, 1, 1 } means it will execute over the + // single group { 0, 0, 0 }. - const CPPPrelude::uint3 startGroupID = { 0, 0, 0}; - const CPPPrelude::uint3 endGroupID = { 1, 1, 1 }; + const CPPPrelude::uint3 startGroupID = {0, 0, 0}; + const CPPPrelude::uint3 endGroupID = {1, 1, 1}; CPPPrelude::ComputeVaryingInput varyingInput; varyingInput.startGroupID = startGroupID; varyingInput.endGroupID = endGroupID; // We don't have any entry point parameters so that's passed as NULL - // We need to cast our definition of the uniform state to the undefined CPPPrelude::UniformState as - // that type is just a name to indicate what kind of thing needs to be passed in. + // We need to cast our definition of the uniform state to the undefined CPPPrelude::UniformState + // as that type is just a name to indicate what kind of thing needs to be passed in. func(&varyingInput, NULL, &uniformState); // bufferContents holds the output diff --git a/examples/example-base/example-base.cpp b/examples/example-base/example-base.cpp index 344611bed..9951d7cf2 100644 --- a/examples/example-base/example-base.cpp +++ b/examples/example-base/example-base.cpp @@ -1,4 +1,5 @@ #include "example-base.h" + #include <chrono> #ifdef _WIN32 @@ -123,7 +124,11 @@ void WindowedAppBase::offlineRender() gTransientHeaps[0]->finish(); } -void WindowedAppBase::createFramebuffers(uint32_t width, uint32_t height, gfx::Format colorFormat, uint32_t frameBufferCount) +void WindowedAppBase::createFramebuffers( + uint32_t width, + uint32_t height, + gfx::Format colorFormat, + uint32_t frameBufferCount) { for (uint32_t i = 0; i < frameBufferCount; i++) { @@ -150,7 +155,8 @@ void WindowedAppBase::createFramebuffers(uint32_t width, uint32_t height, gfx::F colorBufferDesc.size.depth = 1; colorBufferDesc.format = colorFormat; colorBufferDesc.defaultState = ResourceState::RenderTarget; - colorBufferDesc.allowedStates = ResourceStateSet(ResourceState::RenderTarget, ResourceState::CopyDestination); + colorBufferDesc.allowedStates = + ResourceStateSet(ResourceState::RenderTarget, ResourceState::CopyDestination); colorBuffer = gDevice->createTextureResource(colorBufferDesc, nullptr); } else @@ -194,14 +200,21 @@ void WindowedAppBase::createOfflineFramebuffers() void WindowedAppBase::createSwapchainFramebuffers() { gFramebuffers.clear(); - createFramebuffers(gSwapchain->getDesc().width, gSwapchain->getDesc().height, - gSwapchain->getDesc().format, kSwapchainImageCount); + createFramebuffers( + gSwapchain->getDesc().width, + gSwapchain->getDesc().height, + gSwapchain->getDesc().format, + kSwapchainImageCount); } -ComPtr<gfx::IResourceView> WindowedAppBase::createTextureFromFile(String fileName, int& textureWidth, int& textureHeight) +ComPtr<gfx::IResourceView> WindowedAppBase::createTextureFromFile( + String fileName, + int& textureWidth, + int& textureHeight) { int channelsInFile = 0; - auto textureContent = stbi_load(fileName.getBuffer(), &textureWidth, &textureHeight, &channelsInFile, 4); + auto textureContent = + stbi_load(fileName.getBuffer(), &textureWidth, &textureHeight, &channelsInFile, 4); gfx::ITextureResource::Desc textureDesc = {}; textureDesc.allowedStates.add(ResourceState::ShaderResource); textureDesc.format = gfx::Format::R8G8B8A8_UNORM; @@ -222,9 +235,22 @@ ComPtr<gfx::IResourceView> WindowedAppBase::createTextureFromFile(String fileNam subresData[0].strideZ = textureWidth * textureHeight * 4; // Build mipmaps. - struct RGBA { uint8_t v[4]; }; - auto castToRGBA = [](uint32_t v) { RGBA result; memcpy(&result, &v, 4); return result; }; - auto castToUint = [](RGBA v) { uint32_t result; memcpy(&result, &v, 4); return result; }; + struct RGBA + { + uint8_t v[4]; + }; + auto castToRGBA = [](uint32_t v) + { + RGBA result; + memcpy(&result, &v, 4); + return result; + }; + auto castToUint = [](RGBA v) + { + uint32_t result; + memcpy(&result, &v, 4); + return result; + }; int lastMipWidth = textureWidth; int lastMipHeight = textureHeight; @@ -248,7 +274,8 @@ ComPtr<gfx::IResourceView> WindowedAppBase::createTextureFromFile(String fileNam RGBA pix; for (int c = 0; c < 4; c++) { - pix.v[c] = (uint8_t)(((uint32_t)pix1.v[c] + pix2.v[c] + pix3.v[c] + pix4.v[c]) / 4); + pix.v[c] = + (uint8_t)(((uint32_t)pix1.v[c] + pix2.v[c] + pix3.v[c] + pix4.v[c]) / 4); } mipMapData[m][y * w + x] = castToUint(pix); } @@ -286,40 +313,35 @@ void WindowedAppBase::windowSizeChanged() } } -int64_t getCurrentTime() { return std::chrono::high_resolution_clock::now().time_since_epoch().count(); } +int64_t getCurrentTime() +{ + return std::chrono::high_resolution_clock::now().time_since_epoch().count(); +} -int64_t getTimerFrequency() { return std::chrono::high_resolution_clock::period::den; } +int64_t getTimerFrequency() +{ + return std::chrono::high_resolution_clock::period::den; +} class DebugCallback : public IDebugCallback { public: virtual SLANG_NO_THROW void SLANG_MCALL - handleMessage(DebugMessageType type, DebugMessageSource source, const char* message) override + handleMessage(DebugMessageType type, DebugMessageSource source, const char* message) override { const char* typeStr = ""; switch (type) { - case DebugMessageType::Info: - typeStr = "INFO: "; - break; - case DebugMessageType::Warning: - typeStr = "WARNING: "; - break; - case DebugMessageType::Error: - typeStr = "ERROR: "; - break; - default: - break; + case DebugMessageType::Info: typeStr = "INFO: "; break; + case DebugMessageType::Warning: typeStr = "WARNING: "; break; + case DebugMessageType::Error: typeStr = "ERROR: "; break; + default: break; } const char* sourceStr = "[GraphicsLayer]: "; switch (source) { - case DebugMessageSource::Slang: - sourceStr = "[Slang]: "; - break; - case DebugMessageSource::Driver: - sourceStr = "[Driver]: "; - break; + case DebugMessageSource::Slang: sourceStr = "[Slang]: "; break; + case DebugMessageSource::Driver: sourceStr = "[Driver]: "; break; } printf("%s%s%s\n", sourceStr, typeStr, message); #ifdef _WIN32 @@ -338,5 +360,8 @@ void initDebugCallback() } #ifdef _WIN32 -void _Win32OutputDebugString(const char* str) { OutputDebugStringW(Slang::String(str).toWString().begin()); } +void _Win32OutputDebugString(const char* str) +{ + OutputDebugStringW(Slang::String(str).toWString().begin()); +} #endif diff --git a/examples/example-base/example-base.h b/examples/example-base/example-base.h index b97728e17..c10231c14 100644 --- a/examples/example-base/example-base.h +++ b/examples/example-base/example-base.h @@ -1,10 +1,10 @@ #pragma once #include "slang-gfx.h" -#include "tools/platform/window.h" #include "source/core/slang-basic.h" #include "source/core/slang-io.h" #include "test-base.h" +#include "tools/platform/window.h" #ifdef _WIN32 void _Win32OutputDebugString(const char* str); @@ -34,38 +34,52 @@ protected: int height, gfx::DeviceType deviceType = gfx::DeviceType::Default); - void createFramebuffers(uint32_t width, uint32_t height, gfx::Format colorFormat, uint32_t frameBufferCount); + void createFramebuffers( + uint32_t width, + uint32_t height, + gfx::Format colorFormat, + uint32_t frameBufferCount); void createSwapchainFramebuffers(); void createOfflineFramebuffers(); void mainLoop(); - Slang::ComPtr<gfx::IResourceView> createTextureFromFile(Slang::String fileName, int& textureWidth, int& textureHeight); + Slang::ComPtr<gfx::IResourceView> createTextureFromFile( + Slang::String fileName, + int& textureWidth, + int& textureHeight); virtual void windowSizeChanged(); protected: virtual void renderFrame(int framebufferIndex) = 0; + public: platform::Window* getWindow() { return gWindow.Ptr(); } virtual void finalize() { gQueue->waitOnHost(); } void offlineRender(); }; -struct ExampleResources { +struct ExampleResources +{ Slang::String baseDir; - ExampleResources(const Slang::String &dir) : baseDir(dir) {} - - Slang::String resolveResource(const char* fileName) const { - static const Slang::List<Slang::String> directories { + ExampleResources(const Slang::String& dir) + : baseDir(dir) + { + } + + Slang::String resolveResource(const char* fileName) const + { + static const Slang::List<Slang::String> directories{ "examples", "../examples", "../../examples", }; - - for (const Slang::String& dir : directories) { + + for (const Slang::String& dir : directories) + { Slang::StringBuilder pathSb; - pathSb << dir << "/" << baseDir << "/" << fileName; + pathSb << dir << "/" << baseDir << "/" << fileName; if (Slang::File::exists(pathSb.getBuffer())) return pathSb.toString(); } @@ -77,7 +91,8 @@ struct ExampleResources { int64_t getCurrentTime(); int64_t getTimerFrequency(); -template<typename ... TArgs> inline void reportError(const char* format, TArgs... args) +template<typename... TArgs> +inline void reportError(const char* format, TArgs... args) { printf(format, args...); #ifdef _WIN32 @@ -87,7 +102,8 @@ template<typename ... TArgs> inline void reportError(const char* format, TArgs.. #endif } -template <typename... TArgs> inline void log(const char* format, TArgs... args) +template<typename... TArgs> +inline void log(const char* format, TArgs... args) { reportError(format, args...); } diff --git a/examples/example-base/test-base.cpp b/examples/example-base/test-base.cpp index 9d8ec8ce6..5d40f2d80 100644 --- a/examples/example-base/test-base.cpp +++ b/examples/example-base/test-base.cpp @@ -30,23 +30,30 @@ int TestBase::parseOption(int argc, char** argv) return 0; } -void TestBase::printEntrypointHashes(int entryPointCount, int targetCount, ComPtr<slang::IComponentType>& composedProgram) +void TestBase::printEntrypointHashes( + int entryPointCount, + int targetCount, + ComPtr<slang::IComponentType>& composedProgram) { for (int targetIndex = 0; targetIndex < targetCount; targetIndex++) { for (int entryPointIndex = 0; entryPointIndex < entryPointCount; entryPointIndex++) { ComPtr<slang::IBlob> entryPointHashBlob; - composedProgram->getEntryPointHash(entryPointIndex, targetIndex, entryPointHashBlob.writeRef()); + composedProgram->getEntryPointHash( + entryPointIndex, + targetIndex, + entryPointHashBlob.writeRef()); Slang::StringBuilder strBuilder; - strBuilder << "callIdx: " << m_globalCounter << ", entrypoint: "<< entryPointIndex << ", target: " << targetIndex << ", hash: "; + strBuilder << "callIdx: " << m_globalCounter << ", entrypoint: " << entryPointIndex + << ", target: " << targetIndex << ", hash: "; m_globalCounter++; uint8_t* buffer = (uint8_t*)entryPointHashBlob->getBufferPointer(); for (size_t i = 0; i < entryPointHashBlob->getBufferSize(); i++) { - strBuilder<<Slang::StringUtil::makeStringWithFormat("%.2X", buffer[i]); + strBuilder << Slang::StringUtil::makeStringWithFormat("%.2X", buffer[i]); } fprintf(stdout, "%s\n", strBuilder.begin()); } diff --git a/examples/example-base/test-base.h b/examples/example-base/test-base.h index cdd72c580..3f600eae7 100644 --- a/examples/example-base/test-base.h +++ b/examples/example-base/test-base.h @@ -1,22 +1,26 @@ #pragma once -#include "slang.h" #include "slang-com-ptr.h" +#include "slang.h" #include "source/core/slang-string-util.h" using Slang::ComPtr; -class TestBase { +class TestBase +{ public: // Parses command line options. This example only has one option for testing purpose. int parseOption(int argc, char** argv); - void printEntrypointHashes(int entryPointCount, int targetCount, ComPtr<slang::IComponentType>& composedProgram); + void printEntrypointHashes( + int entryPointCount, + int targetCount, + ComPtr<slang::IComponentType>& composedProgram); bool isTestMode() const { return m_isTestMode; } private: - bool m_isTestMode = false; - uint64_t m_globalCounter = 0; + bool m_isTestMode = false; + uint64_t m_globalCounter = 0; }; 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[]) diff --git a/examples/hello-world/main.cpp b/examples/hello-world/main.cpp index 87d440901..e9585bde9 100644 --- a/examples/hello-world/main.cpp +++ b/examples/hello-world/main.cpp @@ -7,13 +7,12 @@ // The goal is to demonstrate how to use the Slang API to cross compile // shader code. // -#include "slang.h" -#include "slang-com-ptr.h" - -#include "vulkan-api.h" #include "examples/example-base/example-base.h" #include "examples/example-base/test-base.h" +#include "slang-com-ptr.h" +#include "slang.h" #include "source/core/slang-string-util.h" +#include "vulkan-api.h" using Slang::ComPtr; @@ -65,7 +64,6 @@ struct HelloWorldExample : public TestBase int run(); ~HelloWorldExample(); - }; int main(int argc, char* argv[]) @@ -102,8 +100,7 @@ int HelloWorldExample::initVulkanInstanceAndDevice() poolCreateInfo.sType = VK_STRUCTURE_TYPE_COMMAND_POOL_CREATE_INFO; poolCreateInfo.flags = VK_COMMAND_POOL_CREATE_RESET_COMMAND_BUFFER_BIT; poolCreateInfo.queueFamilyIndex = vkAPI.queueFamilyIndex; - RETURN_ON_FAIL(vkAPI.vkCreateCommandPool( - vkAPI.device, &poolCreateInfo, nullptr, &commandPool)); + RETURN_ON_FAIL(vkAPI.vkCreateCommandPool(vkAPI.device, &poolCreateInfo, nullptr, &commandPool)); vkAPI.vkGetDeviceQueue(vkAPI.device, vkAPI.queueFamilyIndex, 0, &queue); return 0; @@ -204,7 +201,10 @@ int HelloWorldExample::createComputePipelineFromShader() { ComPtr<slang::IBlob> diagnosticsBlob; SlangResult result = composedProgram->getEntryPointCode( - 0, 0, spirvCode.writeRef(), diagnosticsBlob.writeRef()); + 0, + 0, + spirvCode.writeRef(), + diagnosticsBlob.writeRef()); diagnoseIfNeeded(diagnosticsBlob); RETURN_ON_FAIL(result); @@ -238,13 +238,19 @@ int HelloWorldExample::createComputePipelineFromShader() } descSetLayoutCreateInfo.pBindings = bindings; RETURN_ON_FAIL(vkAPI.vkCreateDescriptorSetLayout( - vkAPI.device, &descSetLayoutCreateInfo, nullptr, &descriptorSetLayout)); + vkAPI.device, + &descSetLayoutCreateInfo, + nullptr, + &descriptorSetLayout)); VkPipelineLayoutCreateInfo pipelineLayoutCreateInfo = { VK_STRUCTURE_TYPE_PIPELINE_LAYOUT_CREATE_INFO}; pipelineLayoutCreateInfo.setLayoutCount = 1; pipelineLayoutCreateInfo.pSetLayouts = &descriptorSetLayout; RETURN_ON_FAIL(vkAPI.vkCreatePipelineLayout( - vkAPI.device, &pipelineLayoutCreateInfo, nullptr, &pipelineLayout)); + vkAPI.device, + &pipelineLayoutCreateInfo, + nullptr, + &pipelineLayout)); // Next we create a shader module from the compiled SPIRV code. VkShaderModuleCreateInfo shaderCreateInfo = {VK_STRUCTURE_TYPE_SHADER_MODULE_CREATE_INFO}; @@ -263,7 +269,12 @@ int HelloWorldExample::createComputePipelineFromShader() pipelineCreateInfo.stage.pName = "main"; pipelineCreateInfo.layout = pipelineLayout; RETURN_ON_FAIL(vkAPI.vkCreateComputePipelines( - vkAPI.device, VK_NULL_HANDLE, 1, &pipelineCreateInfo, nullptr, &pipeline)); + vkAPI.device, + VK_NULL_HANDLE, + 1, + &pipelineCreateInfo, + nullptr, + &pipeline)); // We can destroy shader module now since it will no longer be used. vkAPI.vkDestroyShaderModule(vkAPI.device, vkShaderModule, nullptr); @@ -287,7 +298,8 @@ int HelloWorldExample::createInOutBuffers() vkAPI.vkGetBufferMemoryRequirements(vkAPI.device, inOutBuffers[i], &memoryReqs); int memoryTypeIndex = vkAPI.findMemoryTypeIndex( - memoryReqs.memoryTypeBits, VK_MEMORY_PROPERTY_DEVICE_LOCAL_BIT); + memoryReqs.memoryTypeBits, + VK_MEMORY_PROPERTY_DEVICE_LOCAL_BIT); assert(memoryTypeIndex >= 0); VkMemoryPropertyFlags actualMemoryProperites = @@ -347,7 +359,10 @@ int HelloWorldExample::createInOutBuffers() commandBufferAllocInfo.commandBufferCount = 1; commandBufferAllocInfo.commandPool = commandPool; commandBufferAllocInfo.level = VK_COMMAND_BUFFER_LEVEL_PRIMARY; - RETURN_ON_FAIL(vkAPI.vkAllocateCommandBuffers(vkAPI.device, &commandBufferAllocInfo, &uploadCommandBuffer)); + RETURN_ON_FAIL(vkAPI.vkAllocateCommandBuffers( + vkAPI.device, + &commandBufferAllocInfo, + &uploadCommandBuffer)); VkCommandBufferBeginInfo beginInfo = {VK_STRUCTURE_TYPE_COMMAND_BUFFER_BEGIN_INFO}; vkAPI.vkBeginCommandBuffer(uploadCommandBuffer, &beginInfo); @@ -378,7 +393,10 @@ int HelloWorldExample::dispatchCompute() descriptorPoolCreateInfo.flags = 0; VkDescriptorPool descriptorPool = VK_NULL_HANDLE; RETURN_ON_FAIL(vkAPI.vkCreateDescriptorPool( - vkAPI.device, &descriptorPoolCreateInfo, nullptr, &descriptorPool)); + vkAPI.device, + &descriptorPoolCreateInfo, + nullptr, + &descriptorPool)); // Allocate descriptor set. VkDescriptorSetAllocateInfo descSetAllocInfo = {VK_STRUCTURE_TYPE_DESCRIPTOR_SET_ALLOCATE_INFO}; diff --git a/examples/hello-world/vulkan-api.cpp b/examples/hello-world/vulkan-api.cpp index 3581563d4..95c4a512e 100644 --- a/examples/hello-world/vulkan-api.cpp +++ b/examples/hello-world/vulkan-api.cpp @@ -1,16 +1,17 @@ #include "vulkan-api.h" + #include "slang.h" -#include <stdlib.h> -#include <stdio.h> #include <assert.h> +#include <stdio.h> +#include <stdlib.h> #include <string.h> #include <vector> #if SLANG_WINDOWS_FAMILY -# include <windows.h> +#include <windows.h> #else -# include <dlfcn.h> +#include <dlfcn.h> #endif #if _DEBUG @@ -25,7 +26,8 @@ VKAPI_ATTR VkBool32 VKAPI_CALL debugMessageCallback( int32_t /*msgCode*/, const char* pLayerPrefix, const char* pMsg, - void* /*pUserData*/) + void* /*pUserData*/ +) { printf("[%s]: %s\n", pLayerPrefix, pMsg); return 1; @@ -62,7 +64,7 @@ int initializeVulkanDevice(VulkanAPI& api) uint32_t propertyCount; if (api.vkEnumerateInstanceLayerProperties(&propertyCount, nullptr) != 0) return -1; - std::vector< VkLayerProperties> properties(propertyCount); + std::vector<VkLayerProperties> properties(propertyCount); if (api.vkEnumerateInstanceLayerProperties(&propertyCount, properties.data()) != 0) return -1; for (const auto& p : properties) @@ -119,17 +121,19 @@ int initializeVulkanDevice(VulkanAPI& api) debugCreateInfo.flags = debugFlags; RETURN_ON_FAIL(api.vkCreateDebugReportCallbackEXT( - api.instance, &debugCreateInfo, nullptr, &api.debugReportCallback)); + api.instance, + &debugCreateInfo, + nullptr, + &api.debugReportCallback)); } // Enumerate physical devices. uint32_t numPhysicalDevices = 0; - RETURN_ON_FAIL( - api.vkEnumeratePhysicalDevices(api.instance, &numPhysicalDevices, nullptr)); + RETURN_ON_FAIL(api.vkEnumeratePhysicalDevices(api.instance, &numPhysicalDevices, nullptr)); std::vector<VkPhysicalDevice> physicalDevices; physicalDevices.resize(numPhysicalDevices); - RETURN_ON_FAIL(api.vkEnumeratePhysicalDevices( - api.instance, &numPhysicalDevices, &physicalDevices[0])); + RETURN_ON_FAIL( + api.vkEnumeratePhysicalDevices(api.instance, &numPhysicalDevices, &physicalDevices[0])); // We will use device 0. api.initPhysicalDevice(physicalDevices[0]); @@ -145,7 +149,9 @@ int initializeVulkanDevice(VulkanAPI& api) std::vector<VkQueueFamilyProperties> queueFamilies; queueFamilies.resize(numQueueFamilies); api.vkGetPhysicalDeviceQueueFamilyProperties( - api.physicalDevice, &numQueueFamilies, &queueFamilies[0]); + api.physicalDevice, + &numQueueFamilies, + &queueFamilies[0]); // Find a queue that can service our needs. auto requiredQueueFlags = VK_QUEUE_COMPUTE_BIT; diff --git a/examples/hello-world/vulkan-api.h b/examples/hello-world/vulkan-api.h index 89ca9747d..ab0822569 100644 --- a/examples/hello-world/vulkan-api.h +++ b/examples/hello-world/vulkan-api.h @@ -121,12 +121,12 @@ struct VulkanAPI }; #define RETURN_ON_FAIL(x) \ - { \ - auto _res = x; \ - if (_res != 0) \ - { \ - return -1; \ - } \ + { \ + auto _res = x; \ + if (_res != 0) \ + { \ + return -1; \ + } \ } // Loads Vulkan library and creates a VkDevice. diff --git a/examples/model-viewer/main.cpp b/examples/model-viewer/main.cpp index 7ca5eeb74..7faaf5569 100644 --- a/examples/model-viewer/main.cpp +++ b/examples/model-viewer/main.cpp @@ -16,21 +16,21 @@ // We still need to include the Slang header to use the Slang API // -#include "slang.h" #include "slang-com-helper.h" +#include "slang.h" // We will again make use of a graphics API abstraction // layer that implements the shader-object idiom based on Slang's // `ParameterBlock` and `interface` features to simplify shader specialization // and parameter binding. // +#include "examples/example-base/example-base.h" #include "slang-gfx.h" #include "tools/gfx-util/shader-cursor.h" +#include "tools/platform/gui.h" #include "tools/platform/model.h" #include "tools/platform/vector-math.h" #include "tools/platform/window.h" -#include "tools/platform/gui.h" -#include "examples/example-base/example-base.h" #include <map> #include <sstream> @@ -51,7 +51,7 @@ struct RendererContext slang::TypeReflection* perViewShaderType; slang::TypeReflection* perModelShaderType; - TestBase *pTestBase; + TestBase* pTestBase; Result init(IDevice* inDevice, TestBase* inTestBase) { @@ -60,9 +60,8 @@ struct RendererContext pTestBase = inTestBase; Slang::String path = resourceBase.resolveResource("shaders.slang").getBuffer(); - shaderModule = device->getSlangSession()->loadModule( - path.getBuffer(), - diagnostic.writeRef()); + shaderModule = + device->getSlangSession()->loadModule(path.getBuffer(), diagnostic.writeRef()); diagnoseIfNeeded(diagnostic); if (!shaderModule) return SLANG_FAIL; @@ -165,9 +164,9 @@ struct Material : RefObject // struct SimpleMaterial : Material { - glm::vec3 diffuseColor; - glm::vec3 specularColor; - float specularity = 1.0f; + glm::vec3 diffuseColor; + glm::vec3 specularColor; + float specularity = 1.0f; // Create a shader object that contains the type info and parameter values // that represent an instance of `SimpleMaterial`. @@ -194,20 +193,20 @@ struct SimpleMaterial : Material // struct Mesh : RefObject { - RefPtr<Material> material; - int firstIndex; - int indexCount; + RefPtr<Material> material; + int firstIndex; + int indexCount; }; struct Model : RefObject { typedef platform::ModelLoader::Vertex Vertex; - ComPtr<IBufferResource> vertexBuffer; - ComPtr<IBufferResource> indexBuffer; - PrimitiveTopology primitiveTopology; - int vertexCount; - int indexCount; - std::vector<RefPtr<Mesh>> meshes; + ComPtr<IBufferResource> vertexBuffer; + ComPtr<IBufferResource> indexBuffer; + PrimitiveTopology primitiveTopology; + int vertexCount; + int indexCount; + std::vector<RefPtr<Mesh>> meshes; }; // // Loading a model from disk is done with the help of some utility @@ -216,10 +215,10 @@ struct Model : RefObject // used for its representation. // RefPtr<Model> loadModel( - RendererContext* context, - char const* inputPath, + RendererContext* context, + char const* inputPath, platform::ModelLoader::LoadFlags loadFlags = 0, - float scale = 1.0f) + float scale = 1.0f) { // The model loading interface using a C++ interface of // callback functions to handle creating the application-specific @@ -308,7 +307,7 @@ struct Light : RefObject // The shader object for a light will be stashed here // after it is created. -// ComPtr<IShaderObject> shaderObject; + // ComPtr<IShaderObject> shaderObject; }; // Helper function to retrieve the underlying shader type of `T`. @@ -411,8 +410,7 @@ struct LightEnvLayout : public RefObject { auto program = context->slangReflection; std::stringstream typeNameBuilder; - typeNameBuilder << "LightArray<" << lightType->getName() << "," << maximumCount - << ">"; + typeNameBuilder << "LightArray<" << lightType->getName() << "," << maximumCount << ">"; layout.typeName = typeNameBuilder.str(); } @@ -420,7 +418,8 @@ struct LightEnvLayout : public RefObject mapLightTypeToArrayIndex.insert(std::make_pair(lightType, arrayIndex)); } - template<typename T> void addLightType(RendererContext* context, Int maximumCount) + template<typename T> + void addLightType(RendererContext* context, Int maximumCount) { addLightType(context, getShaderType<T>(context), maximumCount); } @@ -459,8 +458,7 @@ struct LightEnv : public RefObject RefPtr<LightEnvLayout> layout; RendererContext* context; LightEnv(RefPtr<LightEnvLayout> layout, RendererContext* inContext) - : layout(layout) - , context(inContext) + : layout(layout), context(inContext) { for (auto arrayLayout : layout->lightArrayLayouts) { @@ -640,329 +638,324 @@ struct LightEnv : public RefObject // struct ModelViewer : WindowedAppBase { -RendererContext context; + RendererContext context; -// Most of the application state is stored in the list of loaded models, -// as well as the active light source (a single light for now). -// -std::vector<RefPtr<Model>> gModels; -RefPtr<LightEnv> lightEnv; + // Most of the application state is stored in the list of loaded models, + // as well as the active light source (a single light for now). + // + std::vector<RefPtr<Model>> gModels; + RefPtr<LightEnv> lightEnv; -// The pipeline state object we will use to draw models. -ComPtr<IPipelineState> gPipelineState; + // The pipeline state object we will use to draw models. + ComPtr<IPipelineState> gPipelineState; -// During startup the application will load one or more models and -// add them to the `gModels` list. -// -void loadAndAddModel( - char const* inputPath, - platform::ModelLoader::LoadFlags loadFlags = 0, - float scale = 1.0f) -{ - auto model = loadModel(&context, inputPath, loadFlags, scale); - if(!model) return; - gModels.push_back(model); -} + // During startup the application will load one or more models and + // add them to the `gModels` list. + // + void loadAndAddModel( + char const* inputPath, + platform::ModelLoader::LoadFlags loadFlags = 0, + float scale = 1.0f) + { + auto model = loadModel(&context, inputPath, loadFlags, scale); + if (!model) + return; + gModels.push_back(model); + } -// Our "simulation" state consists of just a few values. -// -uint64_t lastTime = 0; + // Our "simulation" state consists of just a few values. + // + uint64_t lastTime = 0; -//glm::vec3 lightDir = normalize(glm::vec3(10, 10, 10)); -//glm::vec3 lightColor = glm::vec3(1, 1, 1); + // glm::vec3 lightDir = normalize(glm::vec3(10, 10, 10)); + // glm::vec3 lightColor = glm::vec3(1, 1, 1); -glm::vec3 cameraPosition = glm::vec3(1.75, 1.25, 5); -glm::quat cameraOrientation = glm::quat(1, glm::vec3(0)); + glm::vec3 cameraPosition = glm::vec3(1.75, 1.25, 5); + glm::quat cameraOrientation = glm::quat(1, glm::vec3(0)); -float translationScale = 0.5f; -float rotationScale = 0.025f; + float translationScale = 0.5f; + float rotationScale = 0.025f; -// In order to control camera movement, we will -// use good old WASD -bool wPressed = false; -bool aPressed = false; -bool sPressed = false; -bool dPressed = false; + // In order to control camera movement, we will + // use good old WASD + bool wPressed = false; + bool aPressed = false; + bool sPressed = false; + bool dPressed = false; -bool isMouseDown = false; -float lastMouseX = 0.0f; -float lastMouseY = 0.0f; + bool isMouseDown = false; + float lastMouseX = 0.0f; + float lastMouseY = 0.0f; -void setKeyState(platform::KeyCode key, bool state) -{ - switch (key) + void setKeyState(platform::KeyCode key, bool state) { - default: - break; - case platform::KeyCode::W: - wPressed = state; - break; - case platform::KeyCode::A: - aPressed = state; - break; - case platform::KeyCode::S: - sPressed = state; - break; - case platform::KeyCode::D: - dPressed = state; - break; + switch (key) + { + default: break; + case platform::KeyCode::W: wPressed = state; break; + case platform::KeyCode::A: aPressed = state; break; + case platform::KeyCode::S: sPressed = state; break; + case platform::KeyCode::D: dPressed = state; break; + } } -} -void onKeyDown(platform::KeyEventArgs args) { setKeyState(args.key, true); } -void onKeyUp(platform::KeyEventArgs args) { setKeyState(args.key, false); } + void onKeyDown(platform::KeyEventArgs args) { setKeyState(args.key, true); } + void onKeyUp(platform::KeyEventArgs args) { setKeyState(args.key, false); } -void onMouseDown(platform::MouseEventArgs args) -{ - isMouseDown = true; - lastMouseX = (float)args.x; - lastMouseY = (float)args.y; -} - -void onMouseMove(platform::MouseEventArgs args) -{ - if (isMouseDown) + void onMouseDown(platform::MouseEventArgs args) { - float deltaX = args.x - lastMouseX; - float deltaY = args.y - lastMouseY; - - cameraOrientation = - glm::rotate(cameraOrientation, -deltaX * rotationScale, glm::vec3(0, 1, 0)); - cameraOrientation = - glm::rotate(cameraOrientation, -deltaY * rotationScale, glm::vec3(1, 0, 0)); - - cameraOrientation = normalize(cameraOrientation); - + isMouseDown = true; lastMouseX = (float)args.x; lastMouseY = (float)args.y; } -} -void onMouseUp(platform::MouseEventArgs args) -{ - isMouseDown = false; -} -// The overall initialization logic is quite similar to -// the earlier example. The biggest difference is that we -// create instances of our application-specific parameter -// block layout and effect types instead of just creating -// raw graphics API objects. -// -Result initialize() -{ - initializeBase("Model Viewer", 1024, 768); - if (!isTestMode()) + void onMouseMove(platform::MouseEventArgs args) { - gWindow->events.mouseMove = [this](const platform::MouseEventArgs& e) { onMouseMove(e); }; - gWindow->events.mouseUp = [this](const platform::MouseEventArgs& e) { onMouseUp(e); }; - gWindow->events.mouseDown = [this](const platform::MouseEventArgs& e) { onMouseDown(e); }; - gWindow->events.keyDown = [this](const platform::KeyEventArgs& e) { onKeyDown(e); }; - gWindow->events.keyUp = [this](const platform::KeyEventArgs& e) { onKeyUp(e); }; - } + if (isMouseDown) + { + float deltaX = args.x - lastMouseX; + float deltaY = args.y - lastMouseY; - // Initialize `RendererContext`, which loads the shader module from file. - SLANG_RETURN_ON_FAIL(context.init(gDevice, this)); + cameraOrientation = + glm::rotate(cameraOrientation, -deltaX * rotationScale, glm::vec3(0, 1, 0)); + cameraOrientation = + glm::rotate(cameraOrientation, -deltaY * rotationScale, glm::vec3(1, 0, 0)); + cameraOrientation = normalize(cameraOrientation); - InputElementDesc inputElements[] = { - {"POSITION", 0, Format::R32G32B32_FLOAT, offsetof(Model::Vertex, position) }, - {"NORMAL", 0, Format::R32G32B32_FLOAT, offsetof(Model::Vertex, normal) }, - {"UV", 0, Format::R32G32_FLOAT, offsetof(Model::Vertex, uv) }, - }; - auto inputLayout = gDevice->createInputLayout( - sizeof(Model::Vertex), - &inputElements[0], - 3); - if(!inputLayout) return SLANG_FAIL; - - // Create the pipeline state object for drawing models. - GraphicsPipelineStateDesc pipelineStateDesc = {}; - pipelineStateDesc.program = context.shaderProgram; - pipelineStateDesc.framebufferLayout = gFramebufferLayout; - pipelineStateDesc.inputLayout = inputLayout; - pipelineStateDesc.primitiveType = PrimitiveType::Triangle; - pipelineStateDesc.depthStencil.depthFunc = ComparisonFunc::LessEqual; - pipelineStateDesc.depthStencil.depthTestEnable = true; - gPipelineState = gDevice->createGraphicsPipelineState(pipelineStateDesc); - - // We will create a lighting environment layout that can hold a few point - // and directional lights, and then initialize a lighting environment - // with just a single point light. + lastMouseX = (float)args.x; + lastMouseY = (float)args.y; + } + } + void onMouseUp(platform::MouseEventArgs args) { isMouseDown = false; } + + // The overall initialization logic is quite similar to + // the earlier example. The biggest difference is that we + // create instances of our application-specific parameter + // block layout and effect types instead of just creating + // raw graphics API objects. // - RefPtr<LightEnvLayout> lightEnvLayout = new LightEnvLayout(); - lightEnvLayout->addLightType<PointLight>(&context, 10); - lightEnvLayout->addLightType<DirectionalLight>(&context, 2); + Result initialize() + { + initializeBase("Model Viewer", 1024, 768); + if (!isTestMode()) + { + gWindow->events.mouseMove = [this](const platform::MouseEventArgs& e) + { onMouseMove(e); }; + gWindow->events.mouseUp = [this](const platform::MouseEventArgs& e) { onMouseUp(e); }; + gWindow->events.mouseDown = [this](const platform::MouseEventArgs& e) + { onMouseDown(e); }; + gWindow->events.keyDown = [this](const platform::KeyEventArgs& e) { onKeyDown(e); }; + gWindow->events.keyUp = [this](const platform::KeyEventArgs& e) { onKeyUp(e); }; + } - lightEnv = new LightEnv(lightEnvLayout, &context); + // Initialize `RendererContext`, which loads the shader module from file. + SLANG_RETURN_ON_FAIL(context.init(gDevice, this)); - RefPtr<PointLight> pointLight = new PointLight(); - pointLight->position = glm::vec3(5, 3, 1); - pointLight->intensity = glm::vec3(10); - lightEnv->add(pointLight); - // Once we have created all our graphcis API and application resources, - // we can start to load models. For now we are keeping things extremely - // simple by using a trivial `.obj` file that can be checked into source - // control. - // - // Support for loading more interesting/complex models will be added - // to this example over time (although model loading is *not* the focus). - // - Slang::String path = resourceBase.resolveResource("cube.obj").getBuffer(); - loadAndAddModel(path.getBuffer()); + InputElementDesc inputElements[] = { + {"POSITION", 0, Format::R32G32B32_FLOAT, offsetof(Model::Vertex, position)}, + {"NORMAL", 0, Format::R32G32B32_FLOAT, offsetof(Model::Vertex, normal)}, + {"UV", 0, Format::R32G32_FLOAT, offsetof(Model::Vertex, uv)}, + }; + auto inputLayout = gDevice->createInputLayout(sizeof(Model::Vertex), &inputElements[0], 3); + if (!inputLayout) + return SLANG_FAIL; - return SLANG_OK; -} + // Create the pipeline state object for drawing models. + GraphicsPipelineStateDesc pipelineStateDesc = {}; + pipelineStateDesc.program = context.shaderProgram; + pipelineStateDesc.framebufferLayout = gFramebufferLayout; + pipelineStateDesc.inputLayout = inputLayout; + pipelineStateDesc.primitiveType = PrimitiveType::Triangle; + pipelineStateDesc.depthStencil.depthFunc = ComparisonFunc::LessEqual; + pipelineStateDesc.depthStencil.depthTestEnable = true; + gPipelineState = gDevice->createGraphicsPipelineState(pipelineStateDesc); + + // We will create a lighting environment layout that can hold a few point + // and directional lights, and then initialize a lighting environment + // with just a single point light. + // + RefPtr<LightEnvLayout> lightEnvLayout = new LightEnvLayout(); + lightEnvLayout->addLightType<PointLight>(&context, 10); + lightEnvLayout->addLightType<DirectionalLight>(&context, 2); -// With the setup work done, we can look at the per-frame rendering -// logic to see how the application will drive the `RenderContext` -// type to perform both shader parameter binding and code specialization. -// -void renderFrame(int frameIndex) override -{ - // In order to see that things are rendering properly we need some - // kind of animation, so we will compute a crude delta-time value here. - // - if(!lastTime) lastTime = getCurrentTime(); - uint64_t currentTime = getCurrentTime(); - float deltaTime = float(double(currentTime - lastTime) / double(getTimerFrequency())); - lastTime = currentTime; + lightEnv = new LightEnv(lightEnvLayout, &context); - // We will use the GLM library to do the matrix math required - // to set up our various transformation matrices. - // - glm::mat4x4 identity = glm::mat4x4(1.0f); + RefPtr<PointLight> pointLight = new PointLight(); + pointLight->position = glm::vec3(5, 3, 1); + pointLight->intensity = glm::vec3(10); + lightEnv->add(pointLight); - platform::Rect clientRect{}; - if (isTestMode()) - { - clientRect.width = 1024; - clientRect.height = 768; - } - else - { - clientRect = getWindow()->getClientRect(); - } - if (clientRect.height == 0) - return; - glm::mat4x4 projection = glm::perspectiveRH_ZO( - glm::radians(60.0f), float(clientRect.width) / float(clientRect.height), - 0.1f, - 1000.0f); - - // We are implementing a *very* basic 6DOF first-person - // camera movement model. - // - glm::mat3x3 cameraOrientationMat(cameraOrientation); - glm::vec3 forward = -cameraOrientationMat[2]; - glm::vec3 right = cameraOrientationMat[0]; - - glm::vec3 movement = glm::vec3(0); - if(wPressed) movement += forward; - if(sPressed) movement -= forward; - if(aPressed) movement -= right; - if(dPressed) movement += right; - - cameraPosition += deltaTime * translationScale * movement; - - glm::mat4x4 view = identity; - view *= glm::mat4x4(inverse(cameraOrientation)); - view = glm::translate(view, -cameraPosition); - - glm::mat4x4 viewProjection = projection * view; - auto deviceInfo = gDevice->getDeviceInfo(); - glm::mat4x4 correctionMatrix; - memcpy(&correctionMatrix, deviceInfo.identityProjectionMatrix, sizeof(float)*16); - viewProjection = correctionMatrix * viewProjection; - // glm uses column-major layout, we need to translate it to row-major. - viewProjection = glm::transpose(viewProjection); - - auto drawCommandBuffer = gTransientHeaps[frameIndex]->createCommandBuffer(); - auto drawCommandEncoder = - drawCommandBuffer->encodeRenderCommands(gRenderPass, gFramebuffers[frameIndex]); - gfx::Viewport viewport = {}; - viewport.maxZ = 1.0f; - viewport.extentX = (float)clientRect.width; - viewport.extentY = (float)clientRect.height; - drawCommandEncoder->setViewportAndScissor(viewport); - drawCommandEncoder->setPrimitiveTopology(PrimitiveTopology::TriangleList); - - // We are only rendering one view, so we can fill in a per-view - // shader object once and use it across all draw calls. - // + // Once we have created all our graphcis API and application resources, + // we can start to load models. For now we are keeping things extremely + // simple by using a trivial `.obj` file that can be checked into source + // control. + // + // Support for loading more interesting/complex models will be added + // to this example over time (although model loading is *not* the focus). + // + Slang::String path = resourceBase.resolveResource("cube.obj").getBuffer(); + loadAndAddModel(path.getBuffer()); - auto viewShaderObject = gDevice->createShaderObject(context.perViewShaderType); - { - ShaderCursor cursor(viewShaderObject); - cursor["viewProjection"].setData(&viewProjection, sizeof(viewProjection)); - cursor["eyePosition"].setData(&cameraPosition, sizeof(cameraPosition)); + return SLANG_OK; } - // The majority of our rendering logic is handled as a loop - // over the models in the scene, and their meshes. + + // With the setup work done, we can look at the per-frame rendering + // logic to see how the application will drive the `RenderContext` + // type to perform both shader parameter binding and code specialization. // - for(auto& model : gModels) + void renderFrame(int frameIndex) override { - drawCommandEncoder->setVertexBuffer(0, model->vertexBuffer); - drawCommandEncoder->setIndexBuffer(model->indexBuffer, Format::R32_UINT); - // For each model we provide a parameter - // block that holds the per-model transformation - // parameters, corresponding to the `PerModel` type - // in the shader code. - glm::mat4x4 modelTransform = identity; - glm::mat4x4 inverseTransposeModelTransform = inverse(transpose(modelTransform)); - auto modelShaderObject = gDevice->createShaderObject(context.perModelShaderType); + // In order to see that things are rendering properly we need some + // kind of animation, so we will compute a crude delta-time value here. + // + if (!lastTime) + lastTime = getCurrentTime(); + uint64_t currentTime = getCurrentTime(); + float deltaTime = float(double(currentTime - lastTime) / double(getTimerFrequency())); + lastTime = currentTime; + + // We will use the GLM library to do the matrix math required + // to set up our various transformation matrices. + // + glm::mat4x4 identity = glm::mat4x4(1.0f); + + platform::Rect clientRect{}; + if (isTestMode()) { - ShaderCursor cursor(modelShaderObject); - cursor["modelTransform"].setData(&modelTransform, sizeof(modelTransform)); - cursor["inverseTransposeModelTransform"].setData( - &inverseTransposeModelTransform, sizeof(inverseTransposeModelTransform)); + clientRect.width = 1024; + clientRect.height = 768; } - - auto lightShaderObject = lightEnv->createShaderObject(); - - // Now we loop over the meshes in the model. + else + { + clientRect = getWindow()->getClientRect(); + } + if (clientRect.height == 0) + return; + glm::mat4x4 projection = glm::perspectiveRH_ZO( + glm::radians(60.0f), + float(clientRect.width) / float(clientRect.height), + 0.1f, + 1000.0f); + + // We are implementing a *very* basic 6DOF first-person + // camera movement model. // - // A more advanced rendering loop would sort things by material - // rather than by model, to avoid overly frequent state changes. - // We are just doing something simple for the purposes of an - // exmple program. + glm::mat3x3 cameraOrientationMat(cameraOrientation); + glm::vec3 forward = -cameraOrientationMat[2]; + glm::vec3 right = cameraOrientationMat[0]; + + glm::vec3 movement = glm::vec3(0); + if (wPressed) + movement += forward; + if (sPressed) + movement -= forward; + if (aPressed) + movement -= right; + if (dPressed) + movement += right; + + cameraPosition += deltaTime * translationScale * movement; + + glm::mat4x4 view = identity; + view *= glm::mat4x4(inverse(cameraOrientation)); + view = glm::translate(view, -cameraPosition); + + glm::mat4x4 viewProjection = projection * view; + auto deviceInfo = gDevice->getDeviceInfo(); + glm::mat4x4 correctionMatrix; + memcpy(&correctionMatrix, deviceInfo.identityProjectionMatrix, sizeof(float) * 16); + viewProjection = correctionMatrix * viewProjection; + // glm uses column-major layout, we need to translate it to row-major. + viewProjection = glm::transpose(viewProjection); + + auto drawCommandBuffer = gTransientHeaps[frameIndex]->createCommandBuffer(); + auto drawCommandEncoder = + drawCommandBuffer->encodeRenderCommands(gRenderPass, gFramebuffers[frameIndex]); + gfx::Viewport viewport = {}; + viewport.maxZ = 1.0f; + viewport.extentX = (float)clientRect.width; + viewport.extentY = (float)clientRect.height; + drawCommandEncoder->setViewportAndScissor(viewport); + drawCommandEncoder->setPrimitiveTopology(PrimitiveTopology::TriangleList); + + // We are only rendering one view, so we can fill in a per-view + // shader object once and use it across all draw calls. // - for(auto& mesh : model->meshes) + + auto viewShaderObject = gDevice->createShaderObject(context.perViewShaderType); { - // Set the pipeline and binding state for drawing each mesh. - auto rootObject = drawCommandEncoder->bindPipeline(gPipelineState); - ShaderCursor rootCursor(rootObject); - rootCursor["gViewParams"].setObject(viewShaderObject); - rootCursor["gModelParams"].setObject(modelShaderObject); - rootCursor["gLightEnv"].setObject(lightShaderObject); - - // Each mesh has a material, and each material has its own - // parameter block that was created at load time, so we - // can just re-use the persistent parameter block for the - // chosen material. + ShaderCursor cursor(viewShaderObject); + cursor["viewProjection"].setData(&viewProjection, sizeof(viewProjection)); + cursor["eyePosition"].setData(&cameraPosition, sizeof(cameraPosition)); + } + // The majority of our rendering logic is handled as a loop + // over the models in the scene, and their meshes. + // + for (auto& model : gModels) + { + drawCommandEncoder->setVertexBuffer(0, model->vertexBuffer); + drawCommandEncoder->setIndexBuffer(model->indexBuffer, Format::R32_UINT); + // For each model we provide a parameter + // block that holds the per-model transformation + // parameters, corresponding to the `PerModel` type + // in the shader code. + glm::mat4x4 modelTransform = identity; + glm::mat4x4 inverseTransposeModelTransform = inverse(transpose(modelTransform)); + auto modelShaderObject = gDevice->createShaderObject(context.perModelShaderType); + { + ShaderCursor cursor(modelShaderObject); + cursor["modelTransform"].setData(&modelTransform, sizeof(modelTransform)); + cursor["inverseTransposeModelTransform"].setData( + &inverseTransposeModelTransform, + sizeof(inverseTransposeModelTransform)); + } + + auto lightShaderObject = lightEnv->createShaderObject(); + + // Now we loop over the meshes in the model. // - // Note that binding the material parameter block here is - // both selecting the values to use for various material - // parameters as well as the *code* to use for material - // evaluation (based on the concrete shader type that - // is implementing the `IMaterial` interface). + // A more advanced rendering loop would sort things by material + // rather than by model, to avoid overly frequent state changes. + // We are just doing something simple for the purposes of an + // exmple program. // - rootCursor["gMaterial"].setObject(mesh->material->shaderObject); + for (auto& mesh : model->meshes) + { + // Set the pipeline and binding state for drawing each mesh. + auto rootObject = drawCommandEncoder->bindPipeline(gPipelineState); + ShaderCursor rootCursor(rootObject); + rootCursor["gViewParams"].setObject(viewShaderObject); + rootCursor["gModelParams"].setObject(modelShaderObject); + rootCursor["gLightEnv"].setObject(lightShaderObject); + + // Each mesh has a material, and each material has its own + // parameter block that was created at load time, so we + // can just re-use the persistent parameter block for the + // chosen material. + // + // Note that binding the material parameter block here is + // both selecting the values to use for various material + // parameters as well as the *code* to use for material + // evaluation (based on the concrete shader type that + // is implementing the `IMaterial` interface). + // + rootCursor["gMaterial"].setObject(mesh->material->shaderObject); - // All the shader parameters and pipeline states have been set up, - // we can now issue a draw call for the mesh. - drawCommandEncoder->drawIndexed(mesh->indexCount, mesh->firstIndex); + // All the shader parameters and pipeline states have been set up, + // we can now issue a draw call for the mesh. + drawCommandEncoder->drawIndexed(mesh->indexCount, mesh->firstIndex); + } } - } - drawCommandEncoder->endEncoding(); - drawCommandBuffer->close(); - gQueue->executeCommandBuffer(drawCommandBuffer); + drawCommandEncoder->endEncoding(); + drawCommandBuffer->close(); + gQueue->executeCommandBuffer(drawCommandBuffer); - if (!isTestMode()) - { - gSwapchain->present(); + if (!isTestMode()) + { + gSwapchain->present(); + } } -} - }; // This macro instantiates an appropriate main function to 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; diff --git a/examples/platform-test/main.cpp b/examples/platform-test/main.cpp index f20a5a716..daa9bbc4b 100644 --- a/examples/platform-test/main.cpp +++ b/examples/platform-test/main.cpp @@ -1,6 +1,6 @@ +#include "examples/example-base/example-base.h" #include "slang.h" #include "tools/platform/window.h" -#include "examples/example-base/example-base.h" using namespace gfx; using namespace Slang; @@ -8,93 +8,105 @@ using namespace Slang; struct PlatformTest : public WindowedAppBase { -void onSizeChanged() -{ - printf("onSizeChanged\n"); -} - -void onFocus() -{ - printf("onFocus\n"); -} - -void onLostFocus() -{ - printf("onLostFocus\n"); -} - -void onKeyDown(platform::KeyEventArgs args) -{ - printf("onKeyDown(key=0x%02x, buttons=0x%02x)\n", (uint32_t)args.key, args.buttons); -} - -void onKeyUp(platform::KeyEventArgs args) -{ - printf("okKeyUp(key=0x%02x, buttons=0x%02x)\n", (uint32_t)args.key, args.buttons); -} - -void onKeyPress(platform::KeyEventArgs args) -{ - printf("onKeyPress(keyChar=0x%02x)\n", args.keyChar); -} - -void onMouseMove(platform::MouseEventArgs args) -{ - printf("onMouseMove(x=%d, y=%d, delta=%d, buttons=0x%02x\n", args.x, args.y, args.delta, args.buttons); -} - -void onMouseDown(platform::MouseEventArgs args) -{ - printf("onMouseDown(x=%d, y=%d, delta=%d, buttons=0x%02x\n", args.x, args.y, args.delta, args.buttons); -} - -void onMouseUp(platform::MouseEventArgs args) -{ - printf("onMouseUp(x=%d, y=%d, delta=%d, buttons=0x%02x\n", args.x, args.y, args.delta, args.buttons); -} - -void onMouseWheel(platform::MouseEventArgs args) -{ - printf("onMouseWheel(x=%d, y=%d, delta=%d, buttons=0x%02x\n", args.x, args.y, args.delta, args.buttons); -} - -Slang::Result initialize() -{ - initializeBase("platform-test", 1024, 768); - - gWindow->events.sizeChanged = [this]() { onSizeChanged(); }; - gWindow->events.focus = [this]() { onFocus(); }; - gWindow->events.lostFocus = [this]() { onLostFocus(); }; - gWindow->events.keyDown = [this](const platform::KeyEventArgs& e) { onKeyDown(e); }; - gWindow->events.keyUp = [this](const platform::KeyEventArgs& e) { onKeyUp(e); }; - gWindow->events.keyPress = [this](const platform::KeyEventArgs& e) { onKeyPress(e); }; - gWindow->events.mouseMove = [this](const platform::MouseEventArgs& e) { onMouseMove(e); }; - gWindow->events.mouseDown = [this](const platform::MouseEventArgs& e) { onMouseDown(e); }; - gWindow->events.mouseUp = [this](const platform::MouseEventArgs& e) { onMouseUp(e); }; - gWindow->events.mouseWheel = [this](const platform::MouseEventArgs& e) { onMouseWheel(e); }; - - return SLANG_OK; -} - -virtual void renderFrame(int frameBufferIndex) override -{ - ComPtr<ICommandBuffer> commandBuffer = gTransientHeaps[frameBufferIndex]->createCommandBuffer(); - - auto renderEncoder = commandBuffer->encodeRenderCommands(gRenderPass, gFramebuffers[frameBufferIndex]); - - gfx::Viewport viewport = {}; - viewport.maxZ = 1.0f; - viewport.extentX = (float)windowWidth; - viewport.extentY = (float)windowHeight; - renderEncoder->setViewportAndScissor(viewport); - - renderEncoder->endEncoding(); - commandBuffer->close(); - gQueue->executeCommandBuffer(commandBuffer); - - gSwapchain->present(); -} - + void onSizeChanged() { printf("onSizeChanged\n"); } + + void onFocus() { printf("onFocus\n"); } + + void onLostFocus() { printf("onLostFocus\n"); } + + void onKeyDown(platform::KeyEventArgs args) + { + printf("onKeyDown(key=0x%02x, buttons=0x%02x)\n", (uint32_t)args.key, args.buttons); + } + + void onKeyUp(platform::KeyEventArgs args) + { + printf("okKeyUp(key=0x%02x, buttons=0x%02x)\n", (uint32_t)args.key, args.buttons); + } + + void onKeyPress(platform::KeyEventArgs args) + { + printf("onKeyPress(keyChar=0x%02x)\n", args.keyChar); + } + + void onMouseMove(platform::MouseEventArgs args) + { + printf( + "onMouseMove(x=%d, y=%d, delta=%d, buttons=0x%02x\n", + args.x, + args.y, + args.delta, + args.buttons); + } + + void onMouseDown(platform::MouseEventArgs args) + { + printf( + "onMouseDown(x=%d, y=%d, delta=%d, buttons=0x%02x\n", + args.x, + args.y, + args.delta, + args.buttons); + } + + void onMouseUp(platform::MouseEventArgs args) + { + printf( + "onMouseUp(x=%d, y=%d, delta=%d, buttons=0x%02x\n", + args.x, + args.y, + args.delta, + args.buttons); + } + + void onMouseWheel(platform::MouseEventArgs args) + { + printf( + "onMouseWheel(x=%d, y=%d, delta=%d, buttons=0x%02x\n", + args.x, + args.y, + args.delta, + args.buttons); + } + + Slang::Result initialize() + { + initializeBase("platform-test", 1024, 768); + + gWindow->events.sizeChanged = [this]() { onSizeChanged(); }; + gWindow->events.focus = [this]() { onFocus(); }; + gWindow->events.lostFocus = [this]() { onLostFocus(); }; + gWindow->events.keyDown = [this](const platform::KeyEventArgs& e) { onKeyDown(e); }; + gWindow->events.keyUp = [this](const platform::KeyEventArgs& e) { onKeyUp(e); }; + gWindow->events.keyPress = [this](const platform::KeyEventArgs& e) { onKeyPress(e); }; + gWindow->events.mouseMove = [this](const platform::MouseEventArgs& e) { onMouseMove(e); }; + gWindow->events.mouseDown = [this](const platform::MouseEventArgs& e) { onMouseDown(e); }; + gWindow->events.mouseUp = [this](const platform::MouseEventArgs& e) { onMouseUp(e); }; + gWindow->events.mouseWheel = [this](const platform::MouseEventArgs& e) { onMouseWheel(e); }; + + return SLANG_OK; + } + + virtual void renderFrame(int frameBufferIndex) override + { + ComPtr<ICommandBuffer> commandBuffer = + gTransientHeaps[frameBufferIndex]->createCommandBuffer(); + + auto renderEncoder = + commandBuffer->encodeRenderCommands(gRenderPass, gFramebuffers[frameBufferIndex]); + + gfx::Viewport viewport = {}; + viewport.maxZ = 1.0f; + viewport.extentX = (float)windowWidth; + viewport.extentY = (float)windowHeight; + renderEncoder->setViewportAndScissor(viewport); + + renderEncoder->endEncoding(); + commandBuffer->close(); + gQueue->executeCommandBuffer(commandBuffer); + + gSwapchain->present(); + } }; // This macro instantiates an appropriate main function to diff --git a/examples/ray-tracing-pipeline/main.cpp b/examples/ray-tracing-pipeline/main.cpp index 562eca4dc..f5ad03e1b 100644 --- a/examples/ray-tracing-pipeline/main.cpp +++ b/examples/ray-tracing-pipeline/main.cpp @@ -3,14 +3,14 @@ // This file implements an example of hardware ray-tracing using // Slang shaders and the `gfx` graphics API. -#include "slang.h" -#include "slang-gfx.h" +#include "examples/example-base/example-base.h" #include "gfx-util/shader-cursor.h" -#include "tools/platform/window.h" -#include "tools/platform/vector-math.h" #include "slang-com-ptr.h" +#include "slang-gfx.h" +#include "slang.h" #include "source/core/slang-basic.h" -#include "examples/example-base/example-base.h" +#include "tools/platform/vector-math.h" +#include "tools/platform/window.h" using namespace gfx; using namespace Slang; @@ -36,8 +36,7 @@ struct Vertex // Define geometry data for our test scene. // The scene contains a floor plane, and a cube placed on top of it at the center. static const int kVertexCount = 24; -static const Vertex kVertexData[kVertexCount] = -{ +static const Vertex kVertexData[kVertexCount] = { // Floor plane {{-100.0f, 0, 100.0f}}, {{100.0f, 0, 100.0f}}, @@ -70,15 +69,9 @@ static const Vertex kVertexData[kVertexCount] = {{1.0f, 0.0, -1.0f}}, }; static const int kIndexCount = 36; -static const int kIndexData[kIndexCount] = -{ - 0, 1, 2, 0, 2, 3, - 4, 5, 6, 4, 6, 7, - 8, 9, 10, 8, 10, 11, - 12, 13, 14, 12, 14, 15, - 16, 17, 18, 16, 18, 19, - 20, 21, 22, 20, 22, 23 -}; +static const int kIndexData[kIndexCount] = {0, 1, 2, 0, 2, 3, 4, 5, 6, 4, 6, 7, + 8, 9, 10, 8, 10, 11, 12, 13, 14, 12, 14, 15, + 16, 17, 18, 16, 18, 19, 20, 21, 22, 20, 22, 23}; struct Primitive { @@ -86,8 +79,7 @@ struct Primitive float color[4]; }; static const int kPrimitiveCount = 12; -static const Primitive kPrimitiveData[kPrimitiveCount] = -{ +static const Primitive kPrimitiveData[kPrimitiveCount] = { {{0.0f, 1.0f, 0.0f, 0.0f}, {0.75f, 0.8f, 0.85f, 1.0f}}, {{0.0f, 1.0f, 0.0f, 0.0f}, {0.75f, 0.8f, 0.85f, 1.0f}}, {{0.0f, 1.0f, 0.0f, 0.0f}, {0.95f, 0.85f, 0.05f, 1.0f}}, @@ -134,559 +126,575 @@ struct RayTracing : public WindowedAppBase { -Uniforms gUniforms = {}; + Uniforms gUniforms = {}; -// Many Slang API functions return detailed diagnostic information -// (error messages, warnings, etc.) as a "blob" of data, or return -// a null blob pointer instead if there were no issues. -// -// For convenience, we define a subroutine that will dump the information -// in a diagnostic blob if one is produced, and skip it otherwise. -// -void diagnoseIfNeeded(slang::IBlob* diagnosticsBlob) -{ - if( diagnosticsBlob != nullptr ) + // Many Slang API functions return detailed diagnostic information + // (error messages, warnings, etc.) as a "blob" of data, or return + // a null blob pointer instead if there were no issues. + // + // For convenience, we define a subroutine that will dump the information + // in a diagnostic blob if one is produced, and skip it otherwise. + // + void diagnoseIfNeeded(slang::IBlob* diagnosticsBlob) { - printf("%s", (const char*) diagnosticsBlob->getBufferPointer()); + if (diagnosticsBlob != nullptr) + { + printf("%s", (const char*)diagnosticsBlob->getBufferPointer()); #ifdef _WIN32 - _Win32OutputDebugString((const char*)diagnosticsBlob->getBufferPointer()); + _Win32OutputDebugString((const char*)diagnosticsBlob->getBufferPointer()); #endif + } } -} -// Load and compile shader code from souce. -gfx::Result loadShaderProgram( - gfx::IDevice* device, bool isRayTracingPipeline, gfx::IShaderProgram** outProgram) -{ - ComPtr<slang::ISession> slangSession; - slangSession = device->getSlangSession(); - - ComPtr<slang::IBlob> diagnosticsBlob; - Slang::String path = resourceBase.resolveResource("shaders.slang"); - slang::IModule* module = slangSession->loadModule(path.getBuffer(), diagnosticsBlob.writeRef()); - diagnoseIfNeeded(diagnosticsBlob); - if(!module) - return SLANG_FAIL; - - Slang::List<slang::IComponentType*> componentTypes; - componentTypes.add(module); - if (isRayTracingPipeline) - { - ComPtr<slang::IEntryPoint> entryPoint; - SLANG_RETURN_ON_FAIL(module->findEntryPointByName("rayGenShader", entryPoint.writeRef())); - componentTypes.add(entryPoint); - SLANG_RETURN_ON_FAIL(module->findEntryPointByName("missShader", entryPoint.writeRef())); - componentTypes.add(entryPoint); - SLANG_RETURN_ON_FAIL( - module->findEntryPointByName("closestHitShader", entryPoint.writeRef())); - componentTypes.add(entryPoint); - SLANG_RETURN_ON_FAIL( - module->findEntryPointByName("shadowRayHitShader", entryPoint.writeRef())); - componentTypes.add(entryPoint); - } - else + // Load and compile shader code from souce. + gfx::Result loadShaderProgram( + gfx::IDevice* device, + bool isRayTracingPipeline, + gfx::IShaderProgram** outProgram) { - ComPtr<slang::IEntryPoint> entryPoint; - SLANG_RETURN_ON_FAIL(module->findEntryPointByName("vertexMain", entryPoint.writeRef())); - componentTypes.add(entryPoint); - SLANG_RETURN_ON_FAIL(module->findEntryPointByName("fragmentMain", entryPoint.writeRef())); - componentTypes.add(entryPoint); - } - - ComPtr<slang::IComponentType> linkedProgram; - SlangResult result = slangSession->createCompositeComponentType( - componentTypes.getBuffer(), - componentTypes.getCount(), - linkedProgram.writeRef(), - diagnosticsBlob.writeRef()); - diagnoseIfNeeded(diagnosticsBlob); - SLANG_RETURN_ON_FAIL(result); + ComPtr<slang::ISession> slangSession; + slangSession = device->getSlangSession(); + + ComPtr<slang::IBlob> diagnosticsBlob; + Slang::String path = resourceBase.resolveResource("shaders.slang"); + slang::IModule* module = + slangSession->loadModule(path.getBuffer(), diagnosticsBlob.writeRef()); + diagnoseIfNeeded(diagnosticsBlob); + if (!module) + return SLANG_FAIL; - if (isTestMode()) - { - printEntrypointHashes(componentTypes.getCount() - 1, 1, linkedProgram); + Slang::List<slang::IComponentType*> componentTypes; + componentTypes.add(module); + if (isRayTracingPipeline) + { + ComPtr<slang::IEntryPoint> entryPoint; + SLANG_RETURN_ON_FAIL( + module->findEntryPointByName("rayGenShader", entryPoint.writeRef())); + componentTypes.add(entryPoint); + SLANG_RETURN_ON_FAIL(module->findEntryPointByName("missShader", entryPoint.writeRef())); + componentTypes.add(entryPoint); + SLANG_RETURN_ON_FAIL( + module->findEntryPointByName("closestHitShader", entryPoint.writeRef())); + componentTypes.add(entryPoint); + SLANG_RETURN_ON_FAIL( + module->findEntryPointByName("shadowRayHitShader", entryPoint.writeRef())); + componentTypes.add(entryPoint); + } + else + { + ComPtr<slang::IEntryPoint> entryPoint; + SLANG_RETURN_ON_FAIL(module->findEntryPointByName("vertexMain", entryPoint.writeRef())); + componentTypes.add(entryPoint); + SLANG_RETURN_ON_FAIL( + module->findEntryPointByName("fragmentMain", entryPoint.writeRef())); + componentTypes.add(entryPoint); + } + + ComPtr<slang::IComponentType> linkedProgram; + SlangResult result = slangSession->createCompositeComponentType( + componentTypes.getBuffer(), + componentTypes.getCount(), + linkedProgram.writeRef(), + diagnosticsBlob.writeRef()); + diagnoseIfNeeded(diagnosticsBlob); + SLANG_RETURN_ON_FAIL(result); + + if (isTestMode()) + { + printEntrypointHashes(componentTypes.getCount() - 1, 1, linkedProgram); + } + + gfx::IShaderProgram::Desc programDesc = {}; + programDesc.slangGlobalScope = linkedProgram; + SLANG_RETURN_ON_FAIL(device->createProgram(programDesc, outProgram)); + + return SLANG_OK; } - gfx::IShaderProgram::Desc programDesc = {}; - programDesc.slangGlobalScope = linkedProgram; - SLANG_RETURN_ON_FAIL(device->createProgram(programDesc, outProgram)); - - return SLANG_OK; -} - -ComPtr<gfx::IPipelineState> gPresentPipelineState; -ComPtr<gfx::IPipelineState> gRenderPipelineState; -ComPtr<gfx::IBufferResource> gFullScreenVertexBuffer; -ComPtr<gfx::IBufferResource> gVertexBuffer; -ComPtr<gfx::IBufferResource> gIndexBuffer; -ComPtr<gfx::IBufferResource> gPrimitiveBuffer; -ComPtr<gfx::IBufferResource> gTransformBuffer; -ComPtr<gfx::IResourceView> gPrimitiveBufferSRV; -ComPtr<gfx::IBufferResource> gInstanceBuffer; -ComPtr<gfx::IBufferResource> gBLASBuffer; -ComPtr<gfx::IAccelerationStructure> gBLAS; -ComPtr<gfx::IBufferResource> gTLASBuffer; -ComPtr<gfx::IAccelerationStructure> gTLAS; -ComPtr<gfx::ITextureResource> gResultTexture; -ComPtr<gfx::IResourceView> gResultTextureUAV; -ComPtr<gfx::IShaderTable> gShaderTable; - -uint64_t lastTime = 0; - -// glm::vec3 lightDir = normalize(glm::vec3(10, 10, 10)); -// glm::vec3 lightColor = glm::vec3(1, 1, 1); - -glm::vec3 cameraPosition = glm::vec3(-2.53f, 2.72f, 4.3f); -float cameraOrientationAngles[2] = {-0.475f, -0.35f}; // Spherical angles (theta, phi). - -float translationScale = 0.5f; -float rotationScale = 0.01f; - -// In order to control camera movement, we will -// use good old WASD -bool wPressed = false; -bool aPressed = false; -bool sPressed = false; -bool dPressed = false; - -bool isMouseDown = false; -float lastMouseX = 0.0f; -float lastMouseY = 0.0f; - -void setKeyState(platform::KeyCode key, bool state) -{ - switch (key) + ComPtr<gfx::IPipelineState> gPresentPipelineState; + ComPtr<gfx::IPipelineState> gRenderPipelineState; + ComPtr<gfx::IBufferResource> gFullScreenVertexBuffer; + ComPtr<gfx::IBufferResource> gVertexBuffer; + ComPtr<gfx::IBufferResource> gIndexBuffer; + ComPtr<gfx::IBufferResource> gPrimitiveBuffer; + ComPtr<gfx::IBufferResource> gTransformBuffer; + ComPtr<gfx::IResourceView> gPrimitiveBufferSRV; + ComPtr<gfx::IBufferResource> gInstanceBuffer; + ComPtr<gfx::IBufferResource> gBLASBuffer; + ComPtr<gfx::IAccelerationStructure> gBLAS; + ComPtr<gfx::IBufferResource> gTLASBuffer; + ComPtr<gfx::IAccelerationStructure> gTLAS; + ComPtr<gfx::ITextureResource> gResultTexture; + ComPtr<gfx::IResourceView> gResultTextureUAV; + ComPtr<gfx::IShaderTable> gShaderTable; + + uint64_t lastTime = 0; + + // glm::vec3 lightDir = normalize(glm::vec3(10, 10, 10)); + // glm::vec3 lightColor = glm::vec3(1, 1, 1); + + glm::vec3 cameraPosition = glm::vec3(-2.53f, 2.72f, 4.3f); + float cameraOrientationAngles[2] = {-0.475f, -0.35f}; // Spherical angles (theta, phi). + + float translationScale = 0.5f; + float rotationScale = 0.01f; + + // In order to control camera movement, we will + // use good old WASD + bool wPressed = false; + bool aPressed = false; + bool sPressed = false; + bool dPressed = false; + + bool isMouseDown = false; + float lastMouseX = 0.0f; + float lastMouseY = 0.0f; + + void setKeyState(platform::KeyCode key, bool state) { - default: - break; - case platform::KeyCode::W: - wPressed = state; - break; - case platform::KeyCode::A: - aPressed = state; - break; - case platform::KeyCode::S: - sPressed = state; - break; - case platform::KeyCode::D: - dPressed = state; - break; + switch (key) + { + default: break; + case platform::KeyCode::W: wPressed = state; break; + case platform::KeyCode::A: aPressed = state; break; + case platform::KeyCode::S: sPressed = state; break; + case platform::KeyCode::D: dPressed = state; break; + } } -} -void onKeyDown(platform::KeyEventArgs args) { setKeyState(args.key, true); } -void onKeyUp(platform::KeyEventArgs args) { setKeyState(args.key, false); } + void onKeyDown(platform::KeyEventArgs args) { setKeyState(args.key, true); } + void onKeyUp(platform::KeyEventArgs args) { setKeyState(args.key, false); } -void onMouseDown(platform::MouseEventArgs args) -{ - isMouseDown = true; - lastMouseX = (float)args.x; - lastMouseY = (float)args.y; -} - -void onMouseMove(platform::MouseEventArgs args) -{ - if (isMouseDown) + void onMouseDown(platform::MouseEventArgs args) { - float deltaX = args.x - lastMouseX; - float deltaY = args.y - lastMouseY; - - cameraOrientationAngles[0] += -deltaX * rotationScale; - cameraOrientationAngles[1] += -deltaY * rotationScale; + isMouseDown = true; lastMouseX = (float)args.x; lastMouseY = (float)args.y; } -} -void onMouseUp(platform::MouseEventArgs args) { isMouseDown = false; } -Slang::Result initialize() -{ - initializeBase("Ray Tracing Pipeline", 1024, 768); - if (!isTestMode()) + void onMouseMove(platform::MouseEventArgs args) { - gWindow->events.mouseMove = [this](const platform::MouseEventArgs& e) { onMouseMove(e); }; - gWindow->events.mouseUp = [this](const platform::MouseEventArgs& e) { onMouseUp(e); }; - gWindow->events.mouseDown = [this](const platform::MouseEventArgs& e) { onMouseDown(e); }; - gWindow->events.keyDown = [this](const platform::KeyEventArgs& e) { onKeyDown(e); }; - gWindow->events.keyUp = [this](const platform::KeyEventArgs& e) { onKeyUp(e); }; + if (isMouseDown) + { + float deltaX = args.x - lastMouseX; + float deltaY = args.y - lastMouseY; + + cameraOrientationAngles[0] += -deltaX * rotationScale; + cameraOrientationAngles[1] += -deltaY * rotationScale; + lastMouseX = (float)args.x; + lastMouseY = (float)args.y; + } } + void onMouseUp(platform::MouseEventArgs args) { isMouseDown = false; } - IBufferResource::Desc vertexBufferDesc; - vertexBufferDesc.type = IResource::Type::Buffer; - vertexBufferDesc.sizeInBytes = kVertexCount * sizeof(Vertex); - vertexBufferDesc.defaultState = ResourceState::ShaderResource; - gVertexBuffer = gDevice->createBufferResource(vertexBufferDesc, &kVertexData[0]); - if(!gVertexBuffer) return SLANG_FAIL; - - IBufferResource::Desc indexBufferDesc; - indexBufferDesc.type = IResource::Type::Buffer; - indexBufferDesc.sizeInBytes = kIndexCount * sizeof(int32_t); - indexBufferDesc.defaultState = ResourceState::ShaderResource; - gIndexBuffer = gDevice->createBufferResource(indexBufferDesc, &kIndexData[0]); - if (!gIndexBuffer) - return SLANG_FAIL; - - IBufferResource::Desc primitiveBufferDesc; - primitiveBufferDesc.type = IResource::Type::Buffer; - primitiveBufferDesc.sizeInBytes = kPrimitiveCount * sizeof(Primitive); - primitiveBufferDesc.elementSize = sizeof(Primitive); - primitiveBufferDesc.defaultState = ResourceState::ShaderResource; - gPrimitiveBuffer = gDevice->createBufferResource(primitiveBufferDesc, &kPrimitiveData[0]); - if (!gPrimitiveBuffer) - return SLANG_FAIL; - - IResourceView::Desc primitiveSRVDesc = {}; - primitiveSRVDesc.format = Format::Unknown; - primitiveSRVDesc.type = IResourceView::Type::ShaderResource; - gPrimitiveBufferSRV = gDevice->createBufferView(gPrimitiveBuffer, nullptr, primitiveSRVDesc); - - IBufferResource::Desc transformBufferDesc; - transformBufferDesc.type = IResource::Type::Buffer; - transformBufferDesc.sizeInBytes = sizeof(float) * 12; - transformBufferDesc.defaultState = ResourceState::ShaderResource; - float transformData[12] = { - 1.0f, 0.0f, 0.0f, 0.0f, 0.0f, 1.0f, 0.0f, 0.0f, 0.0f, 0.0f, 1.0f, 0.0f}; - gTransformBuffer = gDevice->createBufferResource(transformBufferDesc, &transformData); - if (!gTransformBuffer) - return SLANG_FAIL; - // Build bottom level acceleration structure. + Slang::Result initialize() { - IAccelerationStructure::BuildInputs accelerationStructureBuildInputs; - IAccelerationStructure::PrebuildInfo accelerationStructurePrebuildInfo; - accelerationStructureBuildInputs.descCount = 1; - accelerationStructureBuildInputs.kind = IAccelerationStructure::Kind::BottomLevel; - accelerationStructureBuildInputs.flags = - IAccelerationStructure::BuildFlags::AllowCompaction; - IAccelerationStructure::GeometryDesc geomDesc; - geomDesc.flags = IAccelerationStructure::GeometryFlags::Opaque; - geomDesc.type = IAccelerationStructure::GeometryType::Triangles; - geomDesc.content.triangles.indexCount = kIndexCount; - geomDesc.content.triangles.indexData = gIndexBuffer->getDeviceAddress(); - geomDesc.content.triangles.indexFormat = Format::R32_UINT; - geomDesc.content.triangles.vertexCount = kVertexCount; - geomDesc.content.triangles.vertexData = gVertexBuffer->getDeviceAddress(); - geomDesc.content.triangles.vertexFormat = Format::R32G32B32_FLOAT; - geomDesc.content.triangles.vertexStride = sizeof(Vertex); - geomDesc.content.triangles.transform3x4 = gTransformBuffer->getDeviceAddress(); - accelerationStructureBuildInputs.geometryDescs = &geomDesc; - - // Query buffer size for acceleration structure build. - SLANG_RETURN_ON_FAIL(gDevice->getAccelerationStructurePrebuildInfo( - accelerationStructureBuildInputs, &accelerationStructurePrebuildInfo)); - // Allocate buffers for acceleration structure. - IBufferResource::Desc asDraftBufferDesc; - asDraftBufferDesc.type = IResource::Type::Buffer; - asDraftBufferDesc.defaultState = ResourceState::AccelerationStructure; - asDraftBufferDesc.sizeInBytes = (size_t)accelerationStructurePrebuildInfo.resultDataMaxSize; - ComPtr<IBufferResource> draftBuffer = gDevice->createBufferResource(asDraftBufferDesc); - IBufferResource::Desc scratchBufferDesc; - scratchBufferDesc.type = IResource::Type::Buffer; - scratchBufferDesc.defaultState = ResourceState::UnorderedAccess; - scratchBufferDesc.sizeInBytes = (size_t)accelerationStructurePrebuildInfo.scratchDataSize; - ComPtr<IBufferResource> scratchBuffer = gDevice->createBufferResource(scratchBufferDesc); - - // Build acceleration structure. - ComPtr<IQueryPool> compactedSizeQuery; - IQueryPool::Desc queryPoolDesc; - queryPoolDesc.count = 1; - queryPoolDesc.type = QueryType::AccelerationStructureCompactedSize; - SLANG_RETURN_ON_FAIL( - gDevice->createQueryPool(queryPoolDesc, compactedSizeQuery.writeRef())); - - ComPtr<IAccelerationStructure> draftAS; - IAccelerationStructure::CreateDesc draftCreateDesc; - draftCreateDesc.buffer = draftBuffer; - draftCreateDesc.kind = IAccelerationStructure::Kind::BottomLevel; - draftCreateDesc.offset = 0; - draftCreateDesc.size = accelerationStructurePrebuildInfo.resultDataMaxSize; - SLANG_RETURN_ON_FAIL( - gDevice->createAccelerationStructure(draftCreateDesc, draftAS.writeRef())); - - compactedSizeQuery->reset(); - - auto commandBuffer = gTransientHeaps[0]->createCommandBuffer(); - auto encoder = commandBuffer->encodeRayTracingCommands(); - IAccelerationStructure::BuildDesc buildDesc = {}; - buildDesc.dest = draftAS; - buildDesc.inputs = accelerationStructureBuildInputs; - buildDesc.scratchData = scratchBuffer->getDeviceAddress(); - AccelerationStructureQueryDesc compactedSizeQueryDesc = {}; - compactedSizeQueryDesc.queryPool = compactedSizeQuery; - compactedSizeQueryDesc.queryType = QueryType::AccelerationStructureCompactedSize; - encoder->buildAccelerationStructure(buildDesc, 1, &compactedSizeQueryDesc); - encoder->endEncoding(); - commandBuffer->close(); - gQueue->executeCommandBuffer(commandBuffer); - gQueue->waitOnHost(); - - uint64_t compactedSize = 0; - compactedSizeQuery->getResult(0, 1, &compactedSize); - IBufferResource::Desc asBufferDesc; - asBufferDesc.type = IResource::Type::Buffer; - asBufferDesc.defaultState = ResourceState::AccelerationStructure; - asBufferDesc.sizeInBytes = (size_t)compactedSize; - gBLASBuffer = gDevice->createBufferResource(asBufferDesc); - IAccelerationStructure::CreateDesc createDesc; - createDesc.buffer = gBLASBuffer; - createDesc.kind = IAccelerationStructure::Kind::BottomLevel; - createDesc.offset = 0; - createDesc.size = (size_t)compactedSize; - gDevice->createAccelerationStructure(createDesc, gBLAS.writeRef()); - - commandBuffer = gTransientHeaps[0]->createCommandBuffer(); - encoder = commandBuffer->encodeRayTracingCommands(); - encoder->copyAccelerationStructure(gBLAS, draftAS, AccelerationStructureCopyMode::Compact); - encoder->endEncoding(); - commandBuffer->close(); - gQueue->executeCommandBuffer(commandBuffer); - gQueue->waitOnHost(); - } + initializeBase("Ray Tracing Pipeline", 1024, 768); + if (!isTestMode()) + { + gWindow->events.mouseMove = [this](const platform::MouseEventArgs& e) + { onMouseMove(e); }; + gWindow->events.mouseUp = [this](const platform::MouseEventArgs& e) { onMouseUp(e); }; + gWindow->events.mouseDown = [this](const platform::MouseEventArgs& e) + { onMouseDown(e); }; + gWindow->events.keyDown = [this](const platform::KeyEventArgs& e) { onKeyDown(e); }; + gWindow->events.keyUp = [this](const platform::KeyEventArgs& e) { onKeyUp(e); }; + } + + IBufferResource::Desc vertexBufferDesc; + vertexBufferDesc.type = IResource::Type::Buffer; + vertexBufferDesc.sizeInBytes = kVertexCount * sizeof(Vertex); + vertexBufferDesc.defaultState = ResourceState::ShaderResource; + gVertexBuffer = gDevice->createBufferResource(vertexBufferDesc, &kVertexData[0]); + if (!gVertexBuffer) + return SLANG_FAIL; - // Build top level acceleration structure. - { - List<IAccelerationStructure::InstanceDesc> instanceDescs; - instanceDescs.setCount(1); - instanceDescs[0].accelerationStructure = gBLAS->getDeviceAddress(); - instanceDescs[0].flags = - IAccelerationStructure::GeometryInstanceFlags::TriangleFacingCullDisable; - instanceDescs[0].instanceContributionToHitGroupIndex = 0; - instanceDescs[0].instanceID = 0; - instanceDescs[0].instanceMask = 0xFF; - float transformMatrix[] = {1.0f, 0.0f, 0.0f, 0.0f, 0.0f, 1.0f, 0.0f, 0.0f, 0.0f, 0.0f, 1.0f, 0.0f}; - memcpy(&instanceDescs[0].transform[0][0], transformMatrix, sizeof(float) * 12); - - IBufferResource::Desc instanceBufferDesc; - instanceBufferDesc.type = IResource::Type::Buffer; - instanceBufferDesc.sizeInBytes = - instanceDescs.getCount() * sizeof(IAccelerationStructure::InstanceDesc); - instanceBufferDesc.defaultState = ResourceState::ShaderResource; - gInstanceBuffer = gDevice->createBufferResource(instanceBufferDesc, instanceDescs.getBuffer()); - if (!gInstanceBuffer) + IBufferResource::Desc indexBufferDesc; + indexBufferDesc.type = IResource::Type::Buffer; + indexBufferDesc.sizeInBytes = kIndexCount * sizeof(int32_t); + indexBufferDesc.defaultState = ResourceState::ShaderResource; + gIndexBuffer = gDevice->createBufferResource(indexBufferDesc, &kIndexData[0]); + if (!gIndexBuffer) return SLANG_FAIL; - IAccelerationStructure::BuildInputs accelerationStructureBuildInputs = {}; - IAccelerationStructure::PrebuildInfo accelerationStructurePrebuildInfo = {}; - accelerationStructureBuildInputs.descCount = 1; - accelerationStructureBuildInputs.kind = IAccelerationStructure::Kind::TopLevel; - accelerationStructureBuildInputs.instanceDescs = gInstanceBuffer->getDeviceAddress(); - - // Query buffer size for acceleration structure build. - SLANG_RETURN_ON_FAIL(gDevice->getAccelerationStructurePrebuildInfo( - accelerationStructureBuildInputs, &accelerationStructurePrebuildInfo)); - - IBufferResource::Desc asBufferDesc; - asBufferDesc.type = IResource::Type::Buffer; - asBufferDesc.defaultState = ResourceState::AccelerationStructure; - asBufferDesc.sizeInBytes = (size_t)accelerationStructurePrebuildInfo.resultDataMaxSize; - gTLASBuffer = gDevice->createBufferResource(asBufferDesc); - - IBufferResource::Desc scratchBufferDesc; - scratchBufferDesc.type = IResource::Type::Buffer; - scratchBufferDesc.defaultState = ResourceState::UnorderedAccess; - scratchBufferDesc.sizeInBytes = (size_t)accelerationStructurePrebuildInfo.scratchDataSize; - ComPtr<IBufferResource> scratchBuffer = gDevice->createBufferResource(scratchBufferDesc); - - IAccelerationStructure::CreateDesc createDesc; - createDesc.buffer = gTLASBuffer; - createDesc.kind = IAccelerationStructure::Kind::TopLevel; - createDesc.offset = 0; - createDesc.size = (size_t)accelerationStructurePrebuildInfo.resultDataMaxSize; - SLANG_RETURN_ON_FAIL(gDevice->createAccelerationStructure(createDesc, gTLAS.writeRef())); - - auto commandBuffer = gTransientHeaps[0]->createCommandBuffer(); - auto encoder = commandBuffer->encodeRayTracingCommands(); - IAccelerationStructure::BuildDesc buildDesc = {}; - buildDesc.dest = gTLAS; - buildDesc.inputs = accelerationStructureBuildInputs; - buildDesc.scratchData = scratchBuffer->getDeviceAddress(); - encoder->buildAccelerationStructure(buildDesc, 0, nullptr); - encoder->endEncoding(); - commandBuffer->close(); - gQueue->executeCommandBuffer(commandBuffer); - gQueue->waitOnHost(); - } + IBufferResource::Desc primitiveBufferDesc; + primitiveBufferDesc.type = IResource::Type::Buffer; + primitiveBufferDesc.sizeInBytes = kPrimitiveCount * sizeof(Primitive); + primitiveBufferDesc.elementSize = sizeof(Primitive); + primitiveBufferDesc.defaultState = ResourceState::ShaderResource; + gPrimitiveBuffer = gDevice->createBufferResource(primitiveBufferDesc, &kPrimitiveData[0]); + if (!gPrimitiveBuffer) + return SLANG_FAIL; - IBufferResource::Desc fullScreenVertexBufferDesc; - fullScreenVertexBufferDesc.type = IResource::Type::Buffer; - fullScreenVertexBufferDesc.sizeInBytes = - FullScreenTriangle::kVertexCount * sizeof(FullScreenTriangle::Vertex); - fullScreenVertexBufferDesc.defaultState = ResourceState::VertexBuffer; - gFullScreenVertexBuffer = gDevice->createBufferResource( - fullScreenVertexBufferDesc, &FullScreenTriangle::kVertices[0]); - if (!gFullScreenVertexBuffer) - return SLANG_FAIL; - - InputElementDesc inputElements[] = { - {"POSITION", 0, Format::R32G32_FLOAT, offsetof(FullScreenTriangle::Vertex, position)}, - }; - auto inputLayout = gDevice->createInputLayout(sizeof(FullScreenTriangle::Vertex), &inputElements[0], SLANG_COUNT_OF(inputElements)); - if (!inputLayout) - return SLANG_FAIL; - - ComPtr<IShaderProgram> shaderProgram; - SLANG_RETURN_ON_FAIL(loadShaderProgram(gDevice, false, shaderProgram.writeRef())); - GraphicsPipelineStateDesc desc; - desc.inputLayout = inputLayout; - desc.program = shaderProgram; - desc.framebufferLayout = gFramebufferLayout; - gPresentPipelineState = gDevice->createGraphicsPipelineState(desc); - if (!gPresentPipelineState) - return SLANG_FAIL; - - const char* hitgroupNames[] = {"hitgroup0", "hitgroup1"}; - - ComPtr<IShaderProgram> rayTracingProgram; - SLANG_RETURN_ON_FAIL( - loadShaderProgram(gDevice, true, rayTracingProgram.writeRef())); - RayTracingPipelineStateDesc rtpDesc = {}; - rtpDesc.program = rayTracingProgram; - rtpDesc.hitGroupCount = 2; - HitGroupDesc hitGroups[2]; - hitGroups[0].closestHitEntryPoint = "closestHitShader"; - hitGroups[0].hitGroupName = hitgroupNames[0]; - hitGroups[1].closestHitEntryPoint = "shadowRayHitShader"; - hitGroups[1].hitGroupName = hitgroupNames[1]; - rtpDesc.hitGroups = hitGroups; - rtpDesc.maxRayPayloadSize = 64; - rtpDesc.maxRecursion = 2; - SLANG_RETURN_ON_FAIL( - gDevice->createRayTracingPipelineState(rtpDesc, gRenderPipelineState.writeRef())); - if (!gRenderPipelineState) - return SLANG_FAIL; - - IShaderTable::Desc shaderTableDesc = {}; - const char* raygenName = "rayGenShader"; - const char* missName = "missShader"; - shaderTableDesc.program = rayTracingProgram; - shaderTableDesc.hitGroupCount = 2; - shaderTableDesc.hitGroupNames = hitgroupNames; - shaderTableDesc.rayGenShaderCount = 1; - shaderTableDesc.rayGenShaderEntryPointNames = &raygenName; - shaderTableDesc.missShaderCount = 1; - shaderTableDesc.missShaderEntryPointNames = &missName; - SLANG_RETURN_ON_FAIL(gDevice->createShaderTable(shaderTableDesc, gShaderTable.writeRef())); - - createResultTexture(); - return SLANG_OK; -} - -void createResultTexture() -{ - ITextureResource::Desc resultTextureDesc = {}; - resultTextureDesc.type = IResource::Type::Texture2D; - resultTextureDesc.numMipLevels = 1; - resultTextureDesc.size.width = windowWidth; - resultTextureDesc.size.height = windowHeight; - resultTextureDesc.size.depth = 1; - resultTextureDesc.defaultState = ResourceState::UnorderedAccess; - resultTextureDesc.format = Format::R16G16B16A16_FLOAT; - gResultTexture = gDevice->createTextureResource(resultTextureDesc); - IResourceView::Desc resultUAVDesc = {}; - resultUAVDesc.format = resultTextureDesc.format; - resultUAVDesc.type = IResourceView::Type::UnorderedAccess; - gResultTextureUAV = gDevice->createTextureView(gResultTexture, resultUAVDesc); -} - -virtual void windowSizeChanged() override -{ - WindowedAppBase::windowSizeChanged(); - createResultTexture(); -} + IResourceView::Desc primitiveSRVDesc = {}; + primitiveSRVDesc.format = Format::Unknown; + primitiveSRVDesc.type = IResourceView::Type::ShaderResource; + gPrimitiveBufferSRV = + gDevice->createBufferView(gPrimitiveBuffer, nullptr, primitiveSRVDesc); + + IBufferResource::Desc transformBufferDesc; + transformBufferDesc.type = IResource::Type::Buffer; + transformBufferDesc.sizeInBytes = sizeof(float) * 12; + transformBufferDesc.defaultState = ResourceState::ShaderResource; + float transformData[12] = + {1.0f, 0.0f, 0.0f, 0.0f, 0.0f, 1.0f, 0.0f, 0.0f, 0.0f, 0.0f, 1.0f, 0.0f}; + gTransformBuffer = gDevice->createBufferResource(transformBufferDesc, &transformData); + if (!gTransformBuffer) + return SLANG_FAIL; + // Build bottom level acceleration structure. + { + IAccelerationStructure::BuildInputs accelerationStructureBuildInputs; + IAccelerationStructure::PrebuildInfo accelerationStructurePrebuildInfo; + accelerationStructureBuildInputs.descCount = 1; + accelerationStructureBuildInputs.kind = IAccelerationStructure::Kind::BottomLevel; + accelerationStructureBuildInputs.flags = + IAccelerationStructure::BuildFlags::AllowCompaction; + IAccelerationStructure::GeometryDesc geomDesc; + geomDesc.flags = IAccelerationStructure::GeometryFlags::Opaque; + geomDesc.type = IAccelerationStructure::GeometryType::Triangles; + geomDesc.content.triangles.indexCount = kIndexCount; + geomDesc.content.triangles.indexData = gIndexBuffer->getDeviceAddress(); + geomDesc.content.triangles.indexFormat = Format::R32_UINT; + geomDesc.content.triangles.vertexCount = kVertexCount; + geomDesc.content.triangles.vertexData = gVertexBuffer->getDeviceAddress(); + geomDesc.content.triangles.vertexFormat = Format::R32G32B32_FLOAT; + geomDesc.content.triangles.vertexStride = sizeof(Vertex); + geomDesc.content.triangles.transform3x4 = gTransformBuffer->getDeviceAddress(); + accelerationStructureBuildInputs.geometryDescs = &geomDesc; + + // Query buffer size for acceleration structure build. + SLANG_RETURN_ON_FAIL(gDevice->getAccelerationStructurePrebuildInfo( + accelerationStructureBuildInputs, + &accelerationStructurePrebuildInfo)); + // Allocate buffers for acceleration structure. + IBufferResource::Desc asDraftBufferDesc; + asDraftBufferDesc.type = IResource::Type::Buffer; + asDraftBufferDesc.defaultState = ResourceState::AccelerationStructure; + asDraftBufferDesc.sizeInBytes = + (size_t)accelerationStructurePrebuildInfo.resultDataMaxSize; + ComPtr<IBufferResource> draftBuffer = gDevice->createBufferResource(asDraftBufferDesc); + IBufferResource::Desc scratchBufferDesc; + scratchBufferDesc.type = IResource::Type::Buffer; + scratchBufferDesc.defaultState = ResourceState::UnorderedAccess; + scratchBufferDesc.sizeInBytes = + (size_t)accelerationStructurePrebuildInfo.scratchDataSize; + ComPtr<IBufferResource> scratchBuffer = + gDevice->createBufferResource(scratchBufferDesc); + + // Build acceleration structure. + ComPtr<IQueryPool> compactedSizeQuery; + IQueryPool::Desc queryPoolDesc; + queryPoolDesc.count = 1; + queryPoolDesc.type = QueryType::AccelerationStructureCompactedSize; + SLANG_RETURN_ON_FAIL( + gDevice->createQueryPool(queryPoolDesc, compactedSizeQuery.writeRef())); + + ComPtr<IAccelerationStructure> draftAS; + IAccelerationStructure::CreateDesc draftCreateDesc; + draftCreateDesc.buffer = draftBuffer; + draftCreateDesc.kind = IAccelerationStructure::Kind::BottomLevel; + draftCreateDesc.offset = 0; + draftCreateDesc.size = accelerationStructurePrebuildInfo.resultDataMaxSize; + SLANG_RETURN_ON_FAIL( + gDevice->createAccelerationStructure(draftCreateDesc, draftAS.writeRef())); + + compactedSizeQuery->reset(); + + auto commandBuffer = gTransientHeaps[0]->createCommandBuffer(); + auto encoder = commandBuffer->encodeRayTracingCommands(); + IAccelerationStructure::BuildDesc buildDesc = {}; + buildDesc.dest = draftAS; + buildDesc.inputs = accelerationStructureBuildInputs; + buildDesc.scratchData = scratchBuffer->getDeviceAddress(); + AccelerationStructureQueryDesc compactedSizeQueryDesc = {}; + compactedSizeQueryDesc.queryPool = compactedSizeQuery; + compactedSizeQueryDesc.queryType = QueryType::AccelerationStructureCompactedSize; + encoder->buildAccelerationStructure(buildDesc, 1, &compactedSizeQueryDesc); + encoder->endEncoding(); + commandBuffer->close(); + gQueue->executeCommandBuffer(commandBuffer); + gQueue->waitOnHost(); + + uint64_t compactedSize = 0; + compactedSizeQuery->getResult(0, 1, &compactedSize); + IBufferResource::Desc asBufferDesc; + asBufferDesc.type = IResource::Type::Buffer; + asBufferDesc.defaultState = ResourceState::AccelerationStructure; + asBufferDesc.sizeInBytes = (size_t)compactedSize; + gBLASBuffer = gDevice->createBufferResource(asBufferDesc); + IAccelerationStructure::CreateDesc createDesc; + createDesc.buffer = gBLASBuffer; + createDesc.kind = IAccelerationStructure::Kind::BottomLevel; + createDesc.offset = 0; + createDesc.size = (size_t)compactedSize; + gDevice->createAccelerationStructure(createDesc, gBLAS.writeRef()); + + commandBuffer = gTransientHeaps[0]->createCommandBuffer(); + encoder = commandBuffer->encodeRayTracingCommands(); + encoder->copyAccelerationStructure( + gBLAS, + draftAS, + AccelerationStructureCopyMode::Compact); + encoder->endEncoding(); + commandBuffer->close(); + gQueue->executeCommandBuffer(commandBuffer); + gQueue->waitOnHost(); + } + + // Build top level acceleration structure. + { + List<IAccelerationStructure::InstanceDesc> instanceDescs; + instanceDescs.setCount(1); + instanceDescs[0].accelerationStructure = gBLAS->getDeviceAddress(); + instanceDescs[0].flags = + IAccelerationStructure::GeometryInstanceFlags::TriangleFacingCullDisable; + instanceDescs[0].instanceContributionToHitGroupIndex = 0; + instanceDescs[0].instanceID = 0; + instanceDescs[0].instanceMask = 0xFF; + float transformMatrix[] = + {1.0f, 0.0f, 0.0f, 0.0f, 0.0f, 1.0f, 0.0f, 0.0f, 0.0f, 0.0f, 1.0f, 0.0f}; + memcpy(&instanceDescs[0].transform[0][0], transformMatrix, sizeof(float) * 12); + + IBufferResource::Desc instanceBufferDesc; + instanceBufferDesc.type = IResource::Type::Buffer; + instanceBufferDesc.sizeInBytes = + instanceDescs.getCount() * sizeof(IAccelerationStructure::InstanceDesc); + instanceBufferDesc.defaultState = ResourceState::ShaderResource; + gInstanceBuffer = + gDevice->createBufferResource(instanceBufferDesc, instanceDescs.getBuffer()); + if (!gInstanceBuffer) + return SLANG_FAIL; + + IAccelerationStructure::BuildInputs accelerationStructureBuildInputs = {}; + IAccelerationStructure::PrebuildInfo accelerationStructurePrebuildInfo = {}; + accelerationStructureBuildInputs.descCount = 1; + accelerationStructureBuildInputs.kind = IAccelerationStructure::Kind::TopLevel; + accelerationStructureBuildInputs.instanceDescs = gInstanceBuffer->getDeviceAddress(); + + // Query buffer size for acceleration structure build. + SLANG_RETURN_ON_FAIL(gDevice->getAccelerationStructurePrebuildInfo( + accelerationStructureBuildInputs, + &accelerationStructurePrebuildInfo)); + + IBufferResource::Desc asBufferDesc; + asBufferDesc.type = IResource::Type::Buffer; + asBufferDesc.defaultState = ResourceState::AccelerationStructure; + asBufferDesc.sizeInBytes = (size_t)accelerationStructurePrebuildInfo.resultDataMaxSize; + gTLASBuffer = gDevice->createBufferResource(asBufferDesc); + + IBufferResource::Desc scratchBufferDesc; + scratchBufferDesc.type = IResource::Type::Buffer; + scratchBufferDesc.defaultState = ResourceState::UnorderedAccess; + scratchBufferDesc.sizeInBytes = + (size_t)accelerationStructurePrebuildInfo.scratchDataSize; + ComPtr<IBufferResource> scratchBuffer = + gDevice->createBufferResource(scratchBufferDesc); + + IAccelerationStructure::CreateDesc createDesc; + createDesc.buffer = gTLASBuffer; + createDesc.kind = IAccelerationStructure::Kind::TopLevel; + createDesc.offset = 0; + createDesc.size = (size_t)accelerationStructurePrebuildInfo.resultDataMaxSize; + SLANG_RETURN_ON_FAIL( + gDevice->createAccelerationStructure(createDesc, gTLAS.writeRef())); + + auto commandBuffer = gTransientHeaps[0]->createCommandBuffer(); + auto encoder = commandBuffer->encodeRayTracingCommands(); + IAccelerationStructure::BuildDesc buildDesc = {}; + buildDesc.dest = gTLAS; + buildDesc.inputs = accelerationStructureBuildInputs; + buildDesc.scratchData = scratchBuffer->getDeviceAddress(); + encoder->buildAccelerationStructure(buildDesc, 0, nullptr); + encoder->endEncoding(); + commandBuffer->close(); + gQueue->executeCommandBuffer(commandBuffer); + gQueue->waitOnHost(); + } + + IBufferResource::Desc fullScreenVertexBufferDesc; + fullScreenVertexBufferDesc.type = IResource::Type::Buffer; + fullScreenVertexBufferDesc.sizeInBytes = + FullScreenTriangle::kVertexCount * sizeof(FullScreenTriangle::Vertex); + fullScreenVertexBufferDesc.defaultState = ResourceState::VertexBuffer; + gFullScreenVertexBuffer = gDevice->createBufferResource( + fullScreenVertexBufferDesc, + &FullScreenTriangle::kVertices[0]); + if (!gFullScreenVertexBuffer) + return SLANG_FAIL; -glm::vec3 getVectorFromSphericalAngles(float theta, float phi) -{ - auto sinTheta = sin(theta); - auto cosTheta = cos(theta); - auto sinPhi = sin(phi); - auto cosPhi = cos(phi); - return glm::vec3(-sinTheta * cosPhi, sinPhi, -cosTheta * cosPhi); -} -void updateUniforms() -{ - gUniforms.screenWidth = (float)windowWidth; - gUniforms.screenHeight = (float)windowHeight; - if (!lastTime) - lastTime = getCurrentTime(); - uint64_t currentTime = getCurrentTime(); - float deltaTime = float(double(currentTime - lastTime) / double(getTimerFrequency())); - lastTime = currentTime; - - auto camDir = - getVectorFromSphericalAngles(cameraOrientationAngles[0], cameraOrientationAngles[1]); - auto camUp = getVectorFromSphericalAngles( - cameraOrientationAngles[0], cameraOrientationAngles[1] + glm::pi<float>() * 0.5f); - auto camRight = glm::cross(camDir, camUp); - - glm::vec3 movement = glm::vec3(0); - if (wPressed) - movement += camDir; - if (sPressed) - movement -= camDir; - if (aPressed) - movement -= camRight; - if (dPressed) - movement += camRight; - - cameraPosition += deltaTime * translationScale * movement; - - memcpy(gUniforms.cameraDir, &camDir, sizeof(float) * 3); - memcpy(gUniforms.cameraUp, &camUp, sizeof(float) * 3); - memcpy(gUniforms.cameraRight, &camRight, sizeof(float) * 3); - memcpy(gUniforms.cameraPosition, &cameraPosition, sizeof(float) * 3); - auto lightDir = glm::normalize(glm::vec3(1.0f, 3.0f, 2.0f)); - memcpy(gUniforms.lightDir, &lightDir, sizeof(float) * 3); -} - -virtual void renderFrame(int frameBufferIndex) override -{ - updateUniforms(); + InputElementDesc inputElements[] = { + {"POSITION", 0, Format::R32G32_FLOAT, offsetof(FullScreenTriangle::Vertex, position)}, + }; + auto inputLayout = gDevice->createInputLayout( + sizeof(FullScreenTriangle::Vertex), + &inputElements[0], + SLANG_COUNT_OF(inputElements)); + if (!inputLayout) + return SLANG_FAIL; + + ComPtr<IShaderProgram> shaderProgram; + SLANG_RETURN_ON_FAIL(loadShaderProgram(gDevice, false, shaderProgram.writeRef())); + GraphicsPipelineStateDesc desc; + desc.inputLayout = inputLayout; + desc.program = shaderProgram; + desc.framebufferLayout = gFramebufferLayout; + gPresentPipelineState = gDevice->createGraphicsPipelineState(desc); + if (!gPresentPipelineState) + return SLANG_FAIL; + + const char* hitgroupNames[] = {"hitgroup0", "hitgroup1"}; + + ComPtr<IShaderProgram> rayTracingProgram; + SLANG_RETURN_ON_FAIL(loadShaderProgram(gDevice, true, rayTracingProgram.writeRef())); + RayTracingPipelineStateDesc rtpDesc = {}; + rtpDesc.program = rayTracingProgram; + rtpDesc.hitGroupCount = 2; + HitGroupDesc hitGroups[2]; + hitGroups[0].closestHitEntryPoint = "closestHitShader"; + hitGroups[0].hitGroupName = hitgroupNames[0]; + hitGroups[1].closestHitEntryPoint = "shadowRayHitShader"; + hitGroups[1].hitGroupName = hitgroupNames[1]; + rtpDesc.hitGroups = hitGroups; + rtpDesc.maxRayPayloadSize = 64; + rtpDesc.maxRecursion = 2; + SLANG_RETURN_ON_FAIL( + gDevice->createRayTracingPipelineState(rtpDesc, gRenderPipelineState.writeRef())); + if (!gRenderPipelineState) + return SLANG_FAIL; + + IShaderTable::Desc shaderTableDesc = {}; + const char* raygenName = "rayGenShader"; + const char* missName = "missShader"; + shaderTableDesc.program = rayTracingProgram; + shaderTableDesc.hitGroupCount = 2; + shaderTableDesc.hitGroupNames = hitgroupNames; + shaderTableDesc.rayGenShaderCount = 1; + shaderTableDesc.rayGenShaderEntryPointNames = &raygenName; + shaderTableDesc.missShaderCount = 1; + shaderTableDesc.missShaderEntryPointNames = &missName; + SLANG_RETURN_ON_FAIL(gDevice->createShaderTable(shaderTableDesc, gShaderTable.writeRef())); + + createResultTexture(); + return SLANG_OK; + } + + void createResultTexture() { - ComPtr<ICommandBuffer> renderCommandBuffer = - gTransientHeaps[frameBufferIndex]->createCommandBuffer(); - auto renderEncoder = renderCommandBuffer->encodeRayTracingCommands(); - IShaderObject* rootObject = nullptr; - renderEncoder->bindPipeline(gRenderPipelineState, &rootObject); - auto cursor = ShaderCursor(rootObject); - cursor["resultTexture"].setResource(gResultTextureUAV); - cursor["uniforms"].setData(&gUniforms, sizeof(Uniforms)); - cursor["sceneBVH"].setResource(gTLAS); - cursor["primitiveBuffer"].setResource(gPrimitiveBufferSRV); - renderEncoder->dispatchRays(0, gShaderTable, windowWidth, windowHeight, 1); - renderEncoder->endEncoding(); - renderCommandBuffer->close(); - gQueue->executeCommandBuffer(renderCommandBuffer); + ITextureResource::Desc resultTextureDesc = {}; + resultTextureDesc.type = IResource::Type::Texture2D; + resultTextureDesc.numMipLevels = 1; + resultTextureDesc.size.width = windowWidth; + resultTextureDesc.size.height = windowHeight; + resultTextureDesc.size.depth = 1; + resultTextureDesc.defaultState = ResourceState::UnorderedAccess; + resultTextureDesc.format = Format::R16G16B16A16_FLOAT; + gResultTexture = gDevice->createTextureResource(resultTextureDesc); + IResourceView::Desc resultUAVDesc = {}; + resultUAVDesc.format = resultTextureDesc.format; + resultUAVDesc.type = IResourceView::Type::UnorderedAccess; + gResultTextureUAV = gDevice->createTextureView(gResultTexture, resultUAVDesc); } + virtual void windowSizeChanged() override { - ComPtr<ICommandBuffer> presentCommandBuffer = - gTransientHeaps[frameBufferIndex]->createCommandBuffer(); - auto presentEncoder = presentCommandBuffer->encodeRenderCommands( - gRenderPass, gFramebuffers[frameBufferIndex]); - gfx::Viewport viewport = {}; - viewport.maxZ = 1.0f; - viewport.extentX = (float)windowWidth; - viewport.extentY = (float)windowHeight; - presentEncoder->setViewportAndScissor(viewport); - auto rootObject = presentEncoder->bindPipeline(gPresentPipelineState); - auto cursor = ShaderCursor(rootObject->getEntryPoint(1)); - cursor["t"].setResource(gResultTextureUAV); - presentEncoder->setVertexBuffer( - 0, gFullScreenVertexBuffer); - presentEncoder->setPrimitiveTopology(PrimitiveTopology::TriangleList); - presentEncoder->draw(3); - presentEncoder->endEncoding(); - presentCommandBuffer->close(); - gQueue->executeCommandBuffer(presentCommandBuffer); + WindowedAppBase::windowSizeChanged(); + createResultTexture(); } - if (!isTestMode()) + glm::vec3 getVectorFromSphericalAngles(float theta, float phi) { - // With that, we are done drawing for one frame, and ready for the next. - // - gSwapchain->present(); + auto sinTheta = sin(theta); + auto cosTheta = cos(theta); + auto sinPhi = sin(phi); + auto cosPhi = cos(phi); + return glm::vec3(-sinTheta * cosPhi, sinPhi, -cosTheta * cosPhi); + } + void updateUniforms() + { + gUniforms.screenWidth = (float)windowWidth; + gUniforms.screenHeight = (float)windowHeight; + if (!lastTime) + lastTime = getCurrentTime(); + uint64_t currentTime = getCurrentTime(); + float deltaTime = float(double(currentTime - lastTime) / double(getTimerFrequency())); + lastTime = currentTime; + + auto camDir = + getVectorFromSphericalAngles(cameraOrientationAngles[0], cameraOrientationAngles[1]); + auto camUp = getVectorFromSphericalAngles( + cameraOrientationAngles[0], + cameraOrientationAngles[1] + glm::pi<float>() * 0.5f); + auto camRight = glm::cross(camDir, camUp); + + glm::vec3 movement = glm::vec3(0); + if (wPressed) + movement += camDir; + if (sPressed) + movement -= camDir; + if (aPressed) + movement -= camRight; + if (dPressed) + movement += camRight; + + cameraPosition += deltaTime * translationScale * movement; + + memcpy(gUniforms.cameraDir, &camDir, sizeof(float) * 3); + memcpy(gUniforms.cameraUp, &camUp, sizeof(float) * 3); + memcpy(gUniforms.cameraRight, &camRight, sizeof(float) * 3); + memcpy(gUniforms.cameraPosition, &cameraPosition, sizeof(float) * 3); + auto lightDir = glm::normalize(glm::vec3(1.0f, 3.0f, 2.0f)); + memcpy(gUniforms.lightDir, &lightDir, sizeof(float) * 3); } -} + virtual void renderFrame(int frameBufferIndex) override + { + updateUniforms(); + { + ComPtr<ICommandBuffer> renderCommandBuffer = + gTransientHeaps[frameBufferIndex]->createCommandBuffer(); + auto renderEncoder = renderCommandBuffer->encodeRayTracingCommands(); + IShaderObject* rootObject = nullptr; + renderEncoder->bindPipeline(gRenderPipelineState, &rootObject); + auto cursor = ShaderCursor(rootObject); + cursor["resultTexture"].setResource(gResultTextureUAV); + cursor["uniforms"].setData(&gUniforms, sizeof(Uniforms)); + cursor["sceneBVH"].setResource(gTLAS); + cursor["primitiveBuffer"].setResource(gPrimitiveBufferSRV); + renderEncoder->dispatchRays(0, gShaderTable, windowWidth, windowHeight, 1); + renderEncoder->endEncoding(); + renderCommandBuffer->close(); + gQueue->executeCommandBuffer(renderCommandBuffer); + } + + { + ComPtr<ICommandBuffer> presentCommandBuffer = + gTransientHeaps[frameBufferIndex]->createCommandBuffer(); + auto presentEncoder = presentCommandBuffer->encodeRenderCommands( + gRenderPass, + gFramebuffers[frameBufferIndex]); + gfx::Viewport viewport = {}; + viewport.maxZ = 1.0f; + viewport.extentX = (float)windowWidth; + viewport.extentY = (float)windowHeight; + presentEncoder->setViewportAndScissor(viewport); + auto rootObject = presentEncoder->bindPipeline(gPresentPipelineState); + auto cursor = ShaderCursor(rootObject->getEntryPoint(1)); + cursor["t"].setResource(gResultTextureUAV); + presentEncoder->setVertexBuffer(0, gFullScreenVertexBuffer); + presentEncoder->setPrimitiveTopology(PrimitiveTopology::TriangleList); + presentEncoder->draw(3); + presentEncoder->endEncoding(); + presentCommandBuffer->close(); + gQueue->executeCommandBuffer(presentCommandBuffer); + } + + if (!isTestMode()) + { + // With that, we are done drawing for one frame, and ready for the next. + // + gSwapchain->present(); + } + } }; // This macro instantiates an appropriate main function to diff --git a/examples/ray-tracing/main.cpp b/examples/ray-tracing/main.cpp index 4ec5070d8..22111149c 100644 --- a/examples/ray-tracing/main.cpp +++ b/examples/ray-tracing/main.cpp @@ -3,14 +3,14 @@ // This file implements an example of hardware ray-tracing using // Slang shaders and the `gfx` graphics API. -#include "slang.h" -#include "slang-gfx.h" +#include "examples/example-base/example-base.h" #include "gfx-util/shader-cursor.h" -#include "tools/platform/window.h" -#include "tools/platform/vector-math.h" #include "slang-com-ptr.h" +#include "slang-gfx.h" +#include "slang.h" #include "source/core/slang-basic.h" -#include "examples/example-base/example-base.h" +#include "tools/platform/vector-math.h" +#include "tools/platform/window.h" using namespace gfx; using namespace Slang; @@ -36,8 +36,7 @@ struct Vertex // Define geometry data for our test scene. // The scene contains a floor plane, and a cube placed on top of it at the center. static const int kVertexCount = 24; -static const Vertex kVertexData[kVertexCount] = -{ +static const Vertex kVertexData[kVertexCount] = { // Floor plane {{-100.0f, 0, 100.0f}}, {{100.0f, 0, 100.0f}}, @@ -70,15 +69,9 @@ static const Vertex kVertexData[kVertexCount] = {{1.0f, 0.0, -1.0f}}, }; static const int kIndexCount = 36; -static const int kIndexData[kIndexCount] = -{ - 0, 1, 2, 0, 2, 3, - 4, 5, 6, 4, 6, 7, - 8, 9, 10, 8, 10, 11, - 12, 13, 14, 12, 14, 15, - 16, 17, 18, 16, 18, 19, - 20, 21, 22, 20, 22, 23 -}; +static const int kIndexData[kIndexCount] = {0, 1, 2, 0, 2, 3, 4, 5, 6, 4, 6, 7, + 8, 9, 10, 8, 10, 11, 12, 13, 14, 12, 14, 15, + 16, 17, 18, 16, 18, 19, 20, 21, 22, 20, 22, 23}; struct Primitive { @@ -86,8 +79,7 @@ struct Primitive float color[4]; }; static const int kPrimitiveCount = 12; -static const Primitive kPrimitiveData[kPrimitiveCount] = -{ +static const Primitive kPrimitiveData[kPrimitiveCount] = { {{0.0f, 1.0f, 0.0f, 0.0f}, {0.75f, 0.8f, 0.85f, 1.0f}}, {{0.0f, 1.0f, 0.0f, 0.0f}, {0.75f, 0.8f, 0.85f, 1.0f}}, {{0.0f, 1.0f, 0.0f, 0.0f}, {0.95f, 0.85f, 0.05f, 1.0f}}, @@ -134,525 +126,539 @@ struct RayTracing : public WindowedAppBase { -Uniforms gUniforms = {}; + Uniforms gUniforms = {}; -// Many Slang API functions return detailed diagnostic information -// (error messages, warnings, etc.) as a "blob" of data, or return -// a null blob pointer instead if there were no issues. -// -// For convenience, we define a subroutine that will dump the information -// in a diagnostic blob if one is produced, and skip it otherwise. -// -void diagnoseIfNeeded(slang::IBlob* diagnosticsBlob) -{ - if( diagnosticsBlob != nullptr ) + // Many Slang API functions return detailed diagnostic information + // (error messages, warnings, etc.) as a "blob" of data, or return + // a null blob pointer instead if there were no issues. + // + // For convenience, we define a subroutine that will dump the information + // in a diagnostic blob if one is produced, and skip it otherwise. + // + void diagnoseIfNeeded(slang::IBlob* diagnosticsBlob) { - printf("%s", (const char*) diagnosticsBlob->getBufferPointer()); + if (diagnosticsBlob != nullptr) + { + printf("%s", (const char*)diagnosticsBlob->getBufferPointer()); #ifdef _WIN32 - _Win32OutputDebugString((const char*)diagnosticsBlob->getBufferPointer()); + _Win32OutputDebugString((const char*)diagnosticsBlob->getBufferPointer()); #endif + } } -} -// Load and compile shader code from souce. -gfx::Result loadShaderProgram( - gfx::IDevice* device, bool isComputePipeline, gfx::IShaderProgram** outProgram) -{ - ComPtr<slang::ISession> slangSession; - slangSession = device->getSlangSession(); - - ComPtr<slang::IBlob> diagnosticsBlob; - Slang::String path = resourceBase.resolveResource("shaders.slang"); - slang::IModule* module = slangSession->loadModule(path.getBuffer(), diagnosticsBlob.writeRef()); - diagnoseIfNeeded(diagnosticsBlob); - if(!module) - return SLANG_FAIL; - - Slang::List<slang::IComponentType*> componentTypes; - componentTypes.add(module); - if (isComputePipeline) + // Load and compile shader code from souce. + gfx::Result loadShaderProgram( + gfx::IDevice* device, + bool isComputePipeline, + gfx::IShaderProgram** outProgram) { - ComPtr<slang::IEntryPoint> computeEntryPoint; - SLANG_RETURN_ON_FAIL(module->findEntryPointByName("computeMain", computeEntryPoint.writeRef())); - componentTypes.add(computeEntryPoint); - } - else - { - ComPtr<slang::IEntryPoint> entryPoint; - SLANG_RETURN_ON_FAIL(module->findEntryPointByName("vertexMain", entryPoint.writeRef())); - componentTypes.add(entryPoint); - SLANG_RETURN_ON_FAIL(module->findEntryPointByName("fragmentMain", entryPoint.writeRef())); - componentTypes.add(entryPoint); - } - - ComPtr<slang::IComponentType> linkedProgram; - SlangResult result = slangSession->createCompositeComponentType( - componentTypes.getBuffer(), - componentTypes.getCount(), - linkedProgram.writeRef(), - diagnosticsBlob.writeRef()); - diagnoseIfNeeded(diagnosticsBlob); - SLANG_RETURN_ON_FAIL(result); + ComPtr<slang::ISession> slangSession; + slangSession = device->getSlangSession(); + + ComPtr<slang::IBlob> diagnosticsBlob; + Slang::String path = resourceBase.resolveResource("shaders.slang"); + slang::IModule* module = + slangSession->loadModule(path.getBuffer(), diagnosticsBlob.writeRef()); + diagnoseIfNeeded(diagnosticsBlob); + if (!module) + return SLANG_FAIL; - if (isTestMode()) - { - printEntrypointHashes(componentTypes.getCount() - 1, 1, linkedProgram); + Slang::List<slang::IComponentType*> componentTypes; + componentTypes.add(module); + if (isComputePipeline) + { + ComPtr<slang::IEntryPoint> computeEntryPoint; + SLANG_RETURN_ON_FAIL( + module->findEntryPointByName("computeMain", computeEntryPoint.writeRef())); + componentTypes.add(computeEntryPoint); + } + else + { + ComPtr<slang::IEntryPoint> entryPoint; + SLANG_RETURN_ON_FAIL(module->findEntryPointByName("vertexMain", entryPoint.writeRef())); + componentTypes.add(entryPoint); + SLANG_RETURN_ON_FAIL( + module->findEntryPointByName("fragmentMain", entryPoint.writeRef())); + componentTypes.add(entryPoint); + } + + ComPtr<slang::IComponentType> linkedProgram; + SlangResult result = slangSession->createCompositeComponentType( + componentTypes.getBuffer(), + componentTypes.getCount(), + linkedProgram.writeRef(), + diagnosticsBlob.writeRef()); + diagnoseIfNeeded(diagnosticsBlob); + SLANG_RETURN_ON_FAIL(result); + + if (isTestMode()) + { + printEntrypointHashes(componentTypes.getCount() - 1, 1, linkedProgram); + } + + gfx::IShaderProgram::Desc programDesc = {}; + programDesc.slangGlobalScope = linkedProgram; + SLANG_RETURN_ON_FAIL(device->createProgram(programDesc, outProgram)); + + return SLANG_OK; } - gfx::IShaderProgram::Desc programDesc = {}; - programDesc.slangGlobalScope = linkedProgram; - SLANG_RETURN_ON_FAIL(device->createProgram(programDesc, outProgram)); - - return SLANG_OK; -} - -ComPtr<gfx::IPipelineState> gPresentPipelineState; -ComPtr<gfx::IPipelineState> gRenderPipelineState; -ComPtr<gfx::IBufferResource> gFullScreenVertexBuffer; -ComPtr<gfx::IBufferResource> gVertexBuffer; -ComPtr<gfx::IBufferResource> gIndexBuffer; -ComPtr<gfx::IBufferResource> gPrimitiveBuffer; -ComPtr<gfx::IBufferResource> gTransformBuffer; -ComPtr<gfx::IResourceView> gPrimitiveBufferSRV; -ComPtr<gfx::IBufferResource> gInstanceBuffer; -ComPtr<gfx::IBufferResource> gBLASBuffer; -ComPtr<gfx::IAccelerationStructure> gBLAS; -ComPtr<gfx::IBufferResource> gTLASBuffer; -ComPtr<gfx::IAccelerationStructure> gTLAS; -ComPtr<gfx::ITextureResource> gResultTexture; -ComPtr<gfx::IResourceView> gResultTextureUAV; - -uint64_t lastTime = 0; - -// glm::vec3 lightDir = normalize(glm::vec3(10, 10, 10)); -// glm::vec3 lightColor = glm::vec3(1, 1, 1); - -glm::vec3 cameraPosition = glm::vec3(-2.53f, 2.72f, 4.3f); -float cameraOrientationAngles[2] = {-0.475f, -0.35f}; // Spherical angles (theta, phi). - -float translationScale = 0.5f; -float rotationScale = 0.01f; - -// In order to control camera movement, we will -// use good old WASD -bool wPressed = false; -bool aPressed = false; -bool sPressed = false; -bool dPressed = false; - -bool isMouseDown = false; -float lastMouseX = 0.0f; -float lastMouseY = 0.0f; - -void setKeyState(platform::KeyCode key, bool state) -{ - switch (key) + ComPtr<gfx::IPipelineState> gPresentPipelineState; + ComPtr<gfx::IPipelineState> gRenderPipelineState; + ComPtr<gfx::IBufferResource> gFullScreenVertexBuffer; + ComPtr<gfx::IBufferResource> gVertexBuffer; + ComPtr<gfx::IBufferResource> gIndexBuffer; + ComPtr<gfx::IBufferResource> gPrimitiveBuffer; + ComPtr<gfx::IBufferResource> gTransformBuffer; + ComPtr<gfx::IResourceView> gPrimitiveBufferSRV; + ComPtr<gfx::IBufferResource> gInstanceBuffer; + ComPtr<gfx::IBufferResource> gBLASBuffer; + ComPtr<gfx::IAccelerationStructure> gBLAS; + ComPtr<gfx::IBufferResource> gTLASBuffer; + ComPtr<gfx::IAccelerationStructure> gTLAS; + ComPtr<gfx::ITextureResource> gResultTexture; + ComPtr<gfx::IResourceView> gResultTextureUAV; + + uint64_t lastTime = 0; + + // glm::vec3 lightDir = normalize(glm::vec3(10, 10, 10)); + // glm::vec3 lightColor = glm::vec3(1, 1, 1); + + glm::vec3 cameraPosition = glm::vec3(-2.53f, 2.72f, 4.3f); + float cameraOrientationAngles[2] = {-0.475f, -0.35f}; // Spherical angles (theta, phi). + + float translationScale = 0.5f; + float rotationScale = 0.01f; + + // In order to control camera movement, we will + // use good old WASD + bool wPressed = false; + bool aPressed = false; + bool sPressed = false; + bool dPressed = false; + + bool isMouseDown = false; + float lastMouseX = 0.0f; + float lastMouseY = 0.0f; + + void setKeyState(platform::KeyCode key, bool state) { - default: - break; - case platform::KeyCode::W: - wPressed = state; - break; - case platform::KeyCode::A: - aPressed = state; - break; - case platform::KeyCode::S: - sPressed = state; - break; - case platform::KeyCode::D: - dPressed = state; - break; + switch (key) + { + default: break; + case platform::KeyCode::W: wPressed = state; break; + case platform::KeyCode::A: aPressed = state; break; + case platform::KeyCode::S: sPressed = state; break; + case platform::KeyCode::D: dPressed = state; break; + } } -} -void onKeyDown(platform::KeyEventArgs args) { setKeyState(args.key, true); } -void onKeyUp(platform::KeyEventArgs args) { setKeyState(args.key, false); } + void onKeyDown(platform::KeyEventArgs args) { setKeyState(args.key, true); } + void onKeyUp(platform::KeyEventArgs args) { setKeyState(args.key, false); } -void onMouseDown(platform::MouseEventArgs args) -{ - isMouseDown = true; - lastMouseX = (float)args.x; - lastMouseY = (float)args.y; -} - -void onMouseMove(platform::MouseEventArgs args) -{ - if (isMouseDown) + void onMouseDown(platform::MouseEventArgs args) { - float deltaX = args.x - lastMouseX; - float deltaY = args.y - lastMouseY; - - cameraOrientationAngles[0] += -deltaX * rotationScale; - cameraOrientationAngles[1] += -deltaY * rotationScale; + isMouseDown = true; lastMouseX = (float)args.x; lastMouseY = (float)args.y; } -} -void onMouseUp(platform::MouseEventArgs args) { isMouseDown = false; } - -Slang::Result initialize() -{ - initializeBase("Ray Tracing", 1024, 768); - if (!isTestMode()) + void onMouseMove(platform::MouseEventArgs args) { - gWindow->events.mouseMove = [this](const platform::MouseEventArgs& e) { onMouseMove(e); }; - gWindow->events.mouseUp = [this](const platform::MouseEventArgs& e) { onMouseUp(e); }; - gWindow->events.mouseDown = [this](const platform::MouseEventArgs& e) { onMouseDown(e); }; - gWindow->events.keyDown = [this](const platform::KeyEventArgs& e) { onKeyDown(e); }; - gWindow->events.keyUp = [this](const platform::KeyEventArgs& e) { onKeyUp(e); }; + if (isMouseDown) + { + float deltaX = args.x - lastMouseX; + float deltaY = args.y - lastMouseY; + + cameraOrientationAngles[0] += -deltaX * rotationScale; + cameraOrientationAngles[1] += -deltaY * rotationScale; + lastMouseX = (float)args.x; + lastMouseY = (float)args.y; + } } + void onMouseUp(platform::MouseEventArgs args) { isMouseDown = false; } - IBufferResource::Desc vertexBufferDesc; - vertexBufferDesc.type = IResource::Type::Buffer; - vertexBufferDesc.sizeInBytes = kVertexCount * sizeof(Vertex); - vertexBufferDesc.defaultState = ResourceState::ShaderResource; - gVertexBuffer = gDevice->createBufferResource(vertexBufferDesc, &kVertexData[0]); - if(!gVertexBuffer) return SLANG_FAIL; - - IBufferResource::Desc indexBufferDesc; - indexBufferDesc.type = IResource::Type::Buffer; - indexBufferDesc.sizeInBytes = kIndexCount * sizeof(int32_t); - indexBufferDesc.defaultState = ResourceState::ShaderResource; - gIndexBuffer = gDevice->createBufferResource(indexBufferDesc, &kIndexData[0]); - if (!gIndexBuffer) - return SLANG_FAIL; - - IBufferResource::Desc primitiveBufferDesc; - primitiveBufferDesc.type = IResource::Type::Buffer; - primitiveBufferDesc.sizeInBytes = kPrimitiveCount * sizeof(Primitive); - primitiveBufferDesc.elementSize = sizeof(Primitive); - primitiveBufferDesc.defaultState = ResourceState::ShaderResource; - gPrimitiveBuffer = gDevice->createBufferResource(primitiveBufferDesc, &kPrimitiveData[0]); - if (!gPrimitiveBuffer) - return SLANG_FAIL; - - IResourceView::Desc primitiveSRVDesc = {}; - primitiveSRVDesc.format = Format::Unknown; - primitiveSRVDesc.type = IResourceView::Type::ShaderResource; - gPrimitiveBufferSRV = gDevice->createBufferView(gPrimitiveBuffer, nullptr, primitiveSRVDesc); - - IBufferResource::Desc transformBufferDesc; - transformBufferDesc.type = IResource::Type::Buffer; - transformBufferDesc.sizeInBytes = sizeof(float) * 12; - transformBufferDesc.defaultState = ResourceState::ShaderResource; - float transformData[12] = { - 1.0f, 0.0f, 0.0f, 0.0f, 0.0f, 1.0f, 0.0f, 0.0f, 0.0f, 0.0f, 1.0f, 0.0f}; - gTransformBuffer = gDevice->createBufferResource(transformBufferDesc, &transformData); - if (!gTransformBuffer) - return SLANG_FAIL; - // Build bottom level acceleration structure. + Slang::Result initialize() { - IAccelerationStructure::BuildInputs accelerationStructureBuildInputs; - IAccelerationStructure::PrebuildInfo accelerationStructurePrebuildInfo; - accelerationStructureBuildInputs.descCount = 1; - accelerationStructureBuildInputs.kind = IAccelerationStructure::Kind::BottomLevel; - accelerationStructureBuildInputs.flags = - IAccelerationStructure::BuildFlags::AllowCompaction; - IAccelerationStructure::GeometryDesc geomDesc; - geomDesc.flags = IAccelerationStructure::GeometryFlags::Opaque; - geomDesc.type = IAccelerationStructure::GeometryType::Triangles; - geomDesc.content.triangles.indexCount = kIndexCount; - geomDesc.content.triangles.indexData = gIndexBuffer->getDeviceAddress(); - geomDesc.content.triangles.indexFormat = Format::R32_UINT; - geomDesc.content.triangles.vertexCount = kVertexCount; - geomDesc.content.triangles.vertexData = gVertexBuffer->getDeviceAddress(); - geomDesc.content.triangles.vertexFormat = Format::R32G32B32_FLOAT; - geomDesc.content.triangles.vertexStride = sizeof(Vertex); - geomDesc.content.triangles.transform3x4 = gTransformBuffer->getDeviceAddress(); - accelerationStructureBuildInputs.geometryDescs = &geomDesc; - - // Query buffer size for acceleration structure build. - SLANG_RETURN_ON_FAIL(gDevice->getAccelerationStructurePrebuildInfo( - accelerationStructureBuildInputs, &accelerationStructurePrebuildInfo)); - // Allocate buffers for acceleration structure. - IBufferResource::Desc asDraftBufferDesc; - asDraftBufferDesc.type = IResource::Type::Buffer; - asDraftBufferDesc.defaultState = ResourceState::AccelerationStructure; - asDraftBufferDesc.sizeInBytes = accelerationStructurePrebuildInfo.resultDataMaxSize; - ComPtr<IBufferResource> draftBuffer = gDevice->createBufferResource(asDraftBufferDesc); - IBufferResource::Desc scratchBufferDesc; - scratchBufferDesc.type = IResource::Type::Buffer; - scratchBufferDesc.defaultState = ResourceState::UnorderedAccess; - scratchBufferDesc.sizeInBytes = accelerationStructurePrebuildInfo.scratchDataSize; - ComPtr<IBufferResource> scratchBuffer = gDevice->createBufferResource(scratchBufferDesc); - - // Build acceleration structure. - ComPtr<IQueryPool> compactedSizeQuery; - IQueryPool::Desc queryPoolDesc; - queryPoolDesc.count = 1; - queryPoolDesc.type = QueryType::AccelerationStructureCompactedSize; - SLANG_RETURN_ON_FAIL( - gDevice->createQueryPool(queryPoolDesc, compactedSizeQuery.writeRef())); - - ComPtr<IAccelerationStructure> draftAS; - IAccelerationStructure::CreateDesc draftCreateDesc; - draftCreateDesc.buffer = draftBuffer; - draftCreateDesc.kind = IAccelerationStructure::Kind::BottomLevel; - draftCreateDesc.offset = 0; - draftCreateDesc.size = accelerationStructurePrebuildInfo.resultDataMaxSize; - SLANG_RETURN_ON_FAIL( - gDevice->createAccelerationStructure(draftCreateDesc, draftAS.writeRef())); - - compactedSizeQuery->reset(); - - auto commandBuffer = gTransientHeaps[0]->createCommandBuffer(); - auto encoder = commandBuffer->encodeRayTracingCommands(); - IAccelerationStructure::BuildDesc buildDesc = {}; - buildDesc.dest = draftAS; - buildDesc.inputs = accelerationStructureBuildInputs; - buildDesc.scratchData = scratchBuffer->getDeviceAddress(); - AccelerationStructureQueryDesc compactedSizeQueryDesc = {}; - compactedSizeQueryDesc.queryPool = compactedSizeQuery; - compactedSizeQueryDesc.queryType = QueryType::AccelerationStructureCompactedSize; - encoder->buildAccelerationStructure(buildDesc, 1, &compactedSizeQueryDesc); - encoder->endEncoding(); - commandBuffer->close(); - gQueue->executeCommandBuffer(commandBuffer); - gQueue->waitOnHost(); - - uint64_t compactedSize = 0; - compactedSizeQuery->getResult(0, 1, &compactedSize); - IBufferResource::Desc asBufferDesc; - asBufferDesc.type = IResource::Type::Buffer; - asBufferDesc.defaultState = ResourceState::AccelerationStructure; - asBufferDesc.sizeInBytes = (Size)compactedSize; - gBLASBuffer = gDevice->createBufferResource(asBufferDesc); - IAccelerationStructure::CreateDesc createDesc; - createDesc.buffer = gBLASBuffer; - createDesc.kind = IAccelerationStructure::Kind::BottomLevel; - createDesc.offset = 0; - createDesc.size = (Size)compactedSize; - gDevice->createAccelerationStructure(createDesc, gBLAS.writeRef()); - - commandBuffer = gTransientHeaps[0]->createCommandBuffer(); - encoder = commandBuffer->encodeRayTracingCommands(); - encoder->copyAccelerationStructure(gBLAS, draftAS, AccelerationStructureCopyMode::Compact); - encoder->endEncoding(); - commandBuffer->close(); - gQueue->executeCommandBuffer(commandBuffer); - gQueue->waitOnHost(); - } + initializeBase("Ray Tracing", 1024, 768); + + if (!isTestMode()) + { + gWindow->events.mouseMove = [this](const platform::MouseEventArgs& e) + { onMouseMove(e); }; + gWindow->events.mouseUp = [this](const platform::MouseEventArgs& e) { onMouseUp(e); }; + gWindow->events.mouseDown = [this](const platform::MouseEventArgs& e) + { onMouseDown(e); }; + gWindow->events.keyDown = [this](const platform::KeyEventArgs& e) { onKeyDown(e); }; + gWindow->events.keyUp = [this](const platform::KeyEventArgs& e) { onKeyUp(e); }; + } + + IBufferResource::Desc vertexBufferDesc; + vertexBufferDesc.type = IResource::Type::Buffer; + vertexBufferDesc.sizeInBytes = kVertexCount * sizeof(Vertex); + vertexBufferDesc.defaultState = ResourceState::ShaderResource; + gVertexBuffer = gDevice->createBufferResource(vertexBufferDesc, &kVertexData[0]); + if (!gVertexBuffer) + return SLANG_FAIL; - // Build top level acceleration structure. - { - List<IAccelerationStructure::InstanceDesc> instanceDescs; - instanceDescs.setCount(1); - instanceDescs[0].accelerationStructure = gBLAS->getDeviceAddress(); - instanceDescs[0].flags = - IAccelerationStructure::GeometryInstanceFlags::TriangleFacingCullDisable; - instanceDescs[0].instanceContributionToHitGroupIndex = 0; - instanceDescs[0].instanceID = 0; - instanceDescs[0].instanceMask = 0xFF; - float transformMatrix[] = {1.0f, 0.0f, 0.0f, 0.0f, 0.0f, 1.0f, 0.0f, 0.0f, 0.0f, 0.0f, 1.0f, 0.0f}; - memcpy(&instanceDescs[0].transform[0][0], transformMatrix, sizeof(float) * 12); - - IBufferResource::Desc instanceBufferDesc; - instanceBufferDesc.type = IResource::Type::Buffer; - instanceBufferDesc.sizeInBytes = - instanceDescs.getCount() * sizeof(IAccelerationStructure::InstanceDesc); - instanceBufferDesc.defaultState = ResourceState::ShaderResource; - gInstanceBuffer = gDevice->createBufferResource(instanceBufferDesc, instanceDescs.getBuffer()); - if (!gInstanceBuffer) + IBufferResource::Desc indexBufferDesc; + indexBufferDesc.type = IResource::Type::Buffer; + indexBufferDesc.sizeInBytes = kIndexCount * sizeof(int32_t); + indexBufferDesc.defaultState = ResourceState::ShaderResource; + gIndexBuffer = gDevice->createBufferResource(indexBufferDesc, &kIndexData[0]); + if (!gIndexBuffer) return SLANG_FAIL; - IAccelerationStructure::BuildInputs accelerationStructureBuildInputs = {}; - IAccelerationStructure::PrebuildInfo accelerationStructurePrebuildInfo = {}; - accelerationStructureBuildInputs.descCount = 1; - accelerationStructureBuildInputs.kind = IAccelerationStructure::Kind::TopLevel; - accelerationStructureBuildInputs.instanceDescs = gInstanceBuffer->getDeviceAddress(); - - // Query buffer size for acceleration structure build. - SLANG_RETURN_ON_FAIL(gDevice->getAccelerationStructurePrebuildInfo( - accelerationStructureBuildInputs, &accelerationStructurePrebuildInfo)); - - IBufferResource::Desc asBufferDesc; - asBufferDesc.type = IResource::Type::Buffer; - asBufferDesc.defaultState = ResourceState::AccelerationStructure; - asBufferDesc.sizeInBytes = accelerationStructurePrebuildInfo.resultDataMaxSize; - gTLASBuffer = gDevice->createBufferResource(asBufferDesc); - - IBufferResource::Desc scratchBufferDesc; - scratchBufferDesc.type = IResource::Type::Buffer; - scratchBufferDesc.defaultState = ResourceState::UnorderedAccess; - scratchBufferDesc.sizeInBytes = accelerationStructurePrebuildInfo.scratchDataSize; - ComPtr<IBufferResource> scratchBuffer = gDevice->createBufferResource(scratchBufferDesc); - - IAccelerationStructure::CreateDesc createDesc; - createDesc.buffer = gTLASBuffer; - createDesc.kind = IAccelerationStructure::Kind::TopLevel; - createDesc.offset = 0; - createDesc.size = accelerationStructurePrebuildInfo.resultDataMaxSize; - SLANG_RETURN_ON_FAIL(gDevice->createAccelerationStructure(createDesc, gTLAS.writeRef())); - - auto commandBuffer = gTransientHeaps[0]->createCommandBuffer(); - auto encoder = commandBuffer->encodeRayTracingCommands(); - IAccelerationStructure::BuildDesc buildDesc = {}; - buildDesc.dest = gTLAS; - buildDesc.inputs = accelerationStructureBuildInputs; - buildDesc.scratchData = scratchBuffer->getDeviceAddress(); - encoder->buildAccelerationStructure(buildDesc, 0, nullptr); - encoder->endEncoding(); - commandBuffer->close(); - gQueue->executeCommandBuffer(commandBuffer); - gQueue->waitOnHost(); - } + IBufferResource::Desc primitiveBufferDesc; + primitiveBufferDesc.type = IResource::Type::Buffer; + primitiveBufferDesc.sizeInBytes = kPrimitiveCount * sizeof(Primitive); + primitiveBufferDesc.elementSize = sizeof(Primitive); + primitiveBufferDesc.defaultState = ResourceState::ShaderResource; + gPrimitiveBuffer = gDevice->createBufferResource(primitiveBufferDesc, &kPrimitiveData[0]); + if (!gPrimitiveBuffer) + return SLANG_FAIL; - IBufferResource::Desc fullScreenVertexBufferDesc; - fullScreenVertexBufferDesc.type = IResource::Type::Buffer; - fullScreenVertexBufferDesc.sizeInBytes = - FullScreenTriangle::kVertexCount * sizeof(FullScreenTriangle::Vertex); - fullScreenVertexBufferDesc.defaultState = ResourceState::VertexBuffer; - gFullScreenVertexBuffer = gDevice->createBufferResource( - fullScreenVertexBufferDesc, &FullScreenTriangle::kVertices[0]); - if (!gFullScreenVertexBuffer) - return SLANG_FAIL; - - InputElementDesc inputElements[] = { - {"POSITION", 0, Format::R32G32_FLOAT, offsetof(FullScreenTriangle::Vertex, position)}, - }; - auto inputLayout = gDevice->createInputLayout(sizeof(FullScreenTriangle::Vertex), &inputElements[0], SLANG_COUNT_OF(inputElements)); - if (!inputLayout) - return SLANG_FAIL; - - ComPtr<IShaderProgram> shaderProgram; - SLANG_RETURN_ON_FAIL(loadShaderProgram(gDevice, false, shaderProgram.writeRef())); - GraphicsPipelineStateDesc desc; - desc.inputLayout = inputLayout; - desc.program = shaderProgram; - desc.framebufferLayout = gFramebufferLayout; - gPresentPipelineState = gDevice->createGraphicsPipelineState(desc); - if (!gPresentPipelineState) - return SLANG_FAIL; - - ComPtr<IShaderProgram> computeProgram; - SLANG_RETURN_ON_FAIL(loadShaderProgram(gDevice, true, computeProgram.writeRef())); - ComputePipelineStateDesc computeDesc; - computeDesc.program = computeProgram; - gRenderPipelineState = gDevice->createComputePipelineState(computeDesc); - if (!gRenderPipelineState) - return SLANG_FAIL; - - createResultTexture(); - return SLANG_OK; -} - -void createResultTexture() -{ - ITextureResource::Desc resultTextureDesc = {}; - resultTextureDesc.type = IResource::Type::Texture2D; - resultTextureDesc.numMipLevels = 1; - resultTextureDesc.size.width = windowWidth; - resultTextureDesc.size.height = windowHeight; - resultTextureDesc.size.depth = 1; - resultTextureDesc.defaultState = ResourceState::UnorderedAccess; - resultTextureDesc.format = Format::R16G16B16A16_FLOAT; - gResultTexture = gDevice->createTextureResource(resultTextureDesc); - IResourceView::Desc resultUAVDesc = {}; - resultUAVDesc.format = resultTextureDesc.format; - resultUAVDesc.type = IResourceView::Type::UnorderedAccess; - gResultTextureUAV = gDevice->createTextureView(gResultTexture, resultUAVDesc); -} - -virtual void windowSizeChanged() override -{ - WindowedAppBase::windowSizeChanged(); - createResultTexture(); -} + IResourceView::Desc primitiveSRVDesc = {}; + primitiveSRVDesc.format = Format::Unknown; + primitiveSRVDesc.type = IResourceView::Type::ShaderResource; + gPrimitiveBufferSRV = + gDevice->createBufferView(gPrimitiveBuffer, nullptr, primitiveSRVDesc); + + IBufferResource::Desc transformBufferDesc; + transformBufferDesc.type = IResource::Type::Buffer; + transformBufferDesc.sizeInBytes = sizeof(float) * 12; + transformBufferDesc.defaultState = ResourceState::ShaderResource; + float transformData[12] = + {1.0f, 0.0f, 0.0f, 0.0f, 0.0f, 1.0f, 0.0f, 0.0f, 0.0f, 0.0f, 1.0f, 0.0f}; + gTransformBuffer = gDevice->createBufferResource(transformBufferDesc, &transformData); + if (!gTransformBuffer) + return SLANG_FAIL; + // Build bottom level acceleration structure. + { + IAccelerationStructure::BuildInputs accelerationStructureBuildInputs; + IAccelerationStructure::PrebuildInfo accelerationStructurePrebuildInfo; + accelerationStructureBuildInputs.descCount = 1; + accelerationStructureBuildInputs.kind = IAccelerationStructure::Kind::BottomLevel; + accelerationStructureBuildInputs.flags = + IAccelerationStructure::BuildFlags::AllowCompaction; + IAccelerationStructure::GeometryDesc geomDesc; + geomDesc.flags = IAccelerationStructure::GeometryFlags::Opaque; + geomDesc.type = IAccelerationStructure::GeometryType::Triangles; + geomDesc.content.triangles.indexCount = kIndexCount; + geomDesc.content.triangles.indexData = gIndexBuffer->getDeviceAddress(); + geomDesc.content.triangles.indexFormat = Format::R32_UINT; + geomDesc.content.triangles.vertexCount = kVertexCount; + geomDesc.content.triangles.vertexData = gVertexBuffer->getDeviceAddress(); + geomDesc.content.triangles.vertexFormat = Format::R32G32B32_FLOAT; + geomDesc.content.triangles.vertexStride = sizeof(Vertex); + geomDesc.content.triangles.transform3x4 = gTransformBuffer->getDeviceAddress(); + accelerationStructureBuildInputs.geometryDescs = &geomDesc; + + // Query buffer size for acceleration structure build. + SLANG_RETURN_ON_FAIL(gDevice->getAccelerationStructurePrebuildInfo( + accelerationStructureBuildInputs, + &accelerationStructurePrebuildInfo)); + // Allocate buffers for acceleration structure. + IBufferResource::Desc asDraftBufferDesc; + asDraftBufferDesc.type = IResource::Type::Buffer; + asDraftBufferDesc.defaultState = ResourceState::AccelerationStructure; + asDraftBufferDesc.sizeInBytes = accelerationStructurePrebuildInfo.resultDataMaxSize; + ComPtr<IBufferResource> draftBuffer = gDevice->createBufferResource(asDraftBufferDesc); + IBufferResource::Desc scratchBufferDesc; + scratchBufferDesc.type = IResource::Type::Buffer; + scratchBufferDesc.defaultState = ResourceState::UnorderedAccess; + scratchBufferDesc.sizeInBytes = accelerationStructurePrebuildInfo.scratchDataSize; + ComPtr<IBufferResource> scratchBuffer = + gDevice->createBufferResource(scratchBufferDesc); + + // Build acceleration structure. + ComPtr<IQueryPool> compactedSizeQuery; + IQueryPool::Desc queryPoolDesc; + queryPoolDesc.count = 1; + queryPoolDesc.type = QueryType::AccelerationStructureCompactedSize; + SLANG_RETURN_ON_FAIL( + gDevice->createQueryPool(queryPoolDesc, compactedSizeQuery.writeRef())); + + ComPtr<IAccelerationStructure> draftAS; + IAccelerationStructure::CreateDesc draftCreateDesc; + draftCreateDesc.buffer = draftBuffer; + draftCreateDesc.kind = IAccelerationStructure::Kind::BottomLevel; + draftCreateDesc.offset = 0; + draftCreateDesc.size = accelerationStructurePrebuildInfo.resultDataMaxSize; + SLANG_RETURN_ON_FAIL( + gDevice->createAccelerationStructure(draftCreateDesc, draftAS.writeRef())); + + compactedSizeQuery->reset(); + + auto commandBuffer = gTransientHeaps[0]->createCommandBuffer(); + auto encoder = commandBuffer->encodeRayTracingCommands(); + IAccelerationStructure::BuildDesc buildDesc = {}; + buildDesc.dest = draftAS; + buildDesc.inputs = accelerationStructureBuildInputs; + buildDesc.scratchData = scratchBuffer->getDeviceAddress(); + AccelerationStructureQueryDesc compactedSizeQueryDesc = {}; + compactedSizeQueryDesc.queryPool = compactedSizeQuery; + compactedSizeQueryDesc.queryType = QueryType::AccelerationStructureCompactedSize; + encoder->buildAccelerationStructure(buildDesc, 1, &compactedSizeQueryDesc); + encoder->endEncoding(); + commandBuffer->close(); + gQueue->executeCommandBuffer(commandBuffer); + gQueue->waitOnHost(); + + uint64_t compactedSize = 0; + compactedSizeQuery->getResult(0, 1, &compactedSize); + IBufferResource::Desc asBufferDesc; + asBufferDesc.type = IResource::Type::Buffer; + asBufferDesc.defaultState = ResourceState::AccelerationStructure; + asBufferDesc.sizeInBytes = (Size)compactedSize; + gBLASBuffer = gDevice->createBufferResource(asBufferDesc); + IAccelerationStructure::CreateDesc createDesc; + createDesc.buffer = gBLASBuffer; + createDesc.kind = IAccelerationStructure::Kind::BottomLevel; + createDesc.offset = 0; + createDesc.size = (Size)compactedSize; + gDevice->createAccelerationStructure(createDesc, gBLAS.writeRef()); + + commandBuffer = gTransientHeaps[0]->createCommandBuffer(); + encoder = commandBuffer->encodeRayTracingCommands(); + encoder->copyAccelerationStructure( + gBLAS, + draftAS, + AccelerationStructureCopyMode::Compact); + encoder->endEncoding(); + commandBuffer->close(); + gQueue->executeCommandBuffer(commandBuffer); + gQueue->waitOnHost(); + } + + // Build top level acceleration structure. + { + List<IAccelerationStructure::InstanceDesc> instanceDescs; + instanceDescs.setCount(1); + instanceDescs[0].accelerationStructure = gBLAS->getDeviceAddress(); + instanceDescs[0].flags = + IAccelerationStructure::GeometryInstanceFlags::TriangleFacingCullDisable; + instanceDescs[0].instanceContributionToHitGroupIndex = 0; + instanceDescs[0].instanceID = 0; + instanceDescs[0].instanceMask = 0xFF; + float transformMatrix[] = + {1.0f, 0.0f, 0.0f, 0.0f, 0.0f, 1.0f, 0.0f, 0.0f, 0.0f, 0.0f, 1.0f, 0.0f}; + memcpy(&instanceDescs[0].transform[0][0], transformMatrix, sizeof(float) * 12); + + IBufferResource::Desc instanceBufferDesc; + instanceBufferDesc.type = IResource::Type::Buffer; + instanceBufferDesc.sizeInBytes = + instanceDescs.getCount() * sizeof(IAccelerationStructure::InstanceDesc); + instanceBufferDesc.defaultState = ResourceState::ShaderResource; + gInstanceBuffer = + gDevice->createBufferResource(instanceBufferDesc, instanceDescs.getBuffer()); + if (!gInstanceBuffer) + return SLANG_FAIL; + + IAccelerationStructure::BuildInputs accelerationStructureBuildInputs = {}; + IAccelerationStructure::PrebuildInfo accelerationStructurePrebuildInfo = {}; + accelerationStructureBuildInputs.descCount = 1; + accelerationStructureBuildInputs.kind = IAccelerationStructure::Kind::TopLevel; + accelerationStructureBuildInputs.instanceDescs = gInstanceBuffer->getDeviceAddress(); + + // Query buffer size for acceleration structure build. + SLANG_RETURN_ON_FAIL(gDevice->getAccelerationStructurePrebuildInfo( + accelerationStructureBuildInputs, + &accelerationStructurePrebuildInfo)); + + IBufferResource::Desc asBufferDesc; + asBufferDesc.type = IResource::Type::Buffer; + asBufferDesc.defaultState = ResourceState::AccelerationStructure; + asBufferDesc.sizeInBytes = accelerationStructurePrebuildInfo.resultDataMaxSize; + gTLASBuffer = gDevice->createBufferResource(asBufferDesc); + + IBufferResource::Desc scratchBufferDesc; + scratchBufferDesc.type = IResource::Type::Buffer; + scratchBufferDesc.defaultState = ResourceState::UnorderedAccess; + scratchBufferDesc.sizeInBytes = accelerationStructurePrebuildInfo.scratchDataSize; + ComPtr<IBufferResource> scratchBuffer = + gDevice->createBufferResource(scratchBufferDesc); + + IAccelerationStructure::CreateDesc createDesc; + createDesc.buffer = gTLASBuffer; + createDesc.kind = IAccelerationStructure::Kind::TopLevel; + createDesc.offset = 0; + createDesc.size = accelerationStructurePrebuildInfo.resultDataMaxSize; + SLANG_RETURN_ON_FAIL( + gDevice->createAccelerationStructure(createDesc, gTLAS.writeRef())); + + auto commandBuffer = gTransientHeaps[0]->createCommandBuffer(); + auto encoder = commandBuffer->encodeRayTracingCommands(); + IAccelerationStructure::BuildDesc buildDesc = {}; + buildDesc.dest = gTLAS; + buildDesc.inputs = accelerationStructureBuildInputs; + buildDesc.scratchData = scratchBuffer->getDeviceAddress(); + encoder->buildAccelerationStructure(buildDesc, 0, nullptr); + encoder->endEncoding(); + commandBuffer->close(); + gQueue->executeCommandBuffer(commandBuffer); + gQueue->waitOnHost(); + } + + IBufferResource::Desc fullScreenVertexBufferDesc; + fullScreenVertexBufferDesc.type = IResource::Type::Buffer; + fullScreenVertexBufferDesc.sizeInBytes = + FullScreenTriangle::kVertexCount * sizeof(FullScreenTriangle::Vertex); + fullScreenVertexBufferDesc.defaultState = ResourceState::VertexBuffer; + gFullScreenVertexBuffer = gDevice->createBufferResource( + fullScreenVertexBufferDesc, + &FullScreenTriangle::kVertices[0]); + if (!gFullScreenVertexBuffer) + return SLANG_FAIL; -glm::vec3 getVectorFromSphericalAngles(float theta, float phi) -{ - auto sinTheta = sin(theta); - auto cosTheta = cos(theta); - auto sinPhi = sin(phi); - auto cosPhi = cos(phi); - return glm::vec3(-sinTheta * cosPhi, sinPhi, -cosTheta * cosPhi); -} -void updateUniforms() -{ - gUniforms.screenWidth = (float)windowWidth; - gUniforms.screenHeight = (float)windowHeight; - if (!lastTime) - lastTime = getCurrentTime(); - uint64_t currentTime = getCurrentTime(); - float deltaTime = float(double(currentTime - lastTime) / double(getTimerFrequency())); - lastTime = currentTime; - - auto camDir = - getVectorFromSphericalAngles(cameraOrientationAngles[0], cameraOrientationAngles[1]); - auto camUp = getVectorFromSphericalAngles( - cameraOrientationAngles[0], cameraOrientationAngles[1] + glm::pi<float>() * 0.5f); - auto camRight = glm::cross(camDir, camUp); - - glm::vec3 movement = glm::vec3(0); - if (wPressed) - movement += camDir; - if (sPressed) - movement -= camDir; - if (aPressed) - movement -= camRight; - if (dPressed) - movement += camRight; - - cameraPosition += deltaTime * translationScale * movement; - - memcpy(gUniforms.cameraDir, &camDir, sizeof(float) * 3); - memcpy(gUniforms.cameraUp, &camUp, sizeof(float) * 3); - memcpy(gUniforms.cameraRight, &camRight, sizeof(float) * 3); - memcpy(gUniforms.cameraPosition, &cameraPosition, sizeof(float) * 3); - auto lightDir = glm::normalize(glm::vec3(1.0f, 3.0f, 2.0f)); - memcpy(gUniforms.lightDir, &lightDir, sizeof(float) * 3); -} - -virtual void renderFrame(int frameBufferIndex) override -{ - updateUniforms(); + InputElementDesc inputElements[] = { + {"POSITION", 0, Format::R32G32_FLOAT, offsetof(FullScreenTriangle::Vertex, position)}, + }; + auto inputLayout = gDevice->createInputLayout( + sizeof(FullScreenTriangle::Vertex), + &inputElements[0], + SLANG_COUNT_OF(inputElements)); + if (!inputLayout) + return SLANG_FAIL; + + ComPtr<IShaderProgram> shaderProgram; + SLANG_RETURN_ON_FAIL(loadShaderProgram(gDevice, false, shaderProgram.writeRef())); + GraphicsPipelineStateDesc desc; + desc.inputLayout = inputLayout; + desc.program = shaderProgram; + desc.framebufferLayout = gFramebufferLayout; + gPresentPipelineState = gDevice->createGraphicsPipelineState(desc); + if (!gPresentPipelineState) + return SLANG_FAIL; + + ComPtr<IShaderProgram> computeProgram; + SLANG_RETURN_ON_FAIL(loadShaderProgram(gDevice, true, computeProgram.writeRef())); + ComputePipelineStateDesc computeDesc; + computeDesc.program = computeProgram; + gRenderPipelineState = gDevice->createComputePipelineState(computeDesc); + if (!gRenderPipelineState) + return SLANG_FAIL; + + createResultTexture(); + return SLANG_OK; + } + + void createResultTexture() { - ComPtr<ICommandBuffer> renderCommandBuffer = - gTransientHeaps[frameBufferIndex]->createCommandBuffer(); - auto renderEncoder = renderCommandBuffer->encodeComputeCommands(); - auto rootObject = renderEncoder->bindPipeline(gRenderPipelineState); - auto cursor = ShaderCursor(rootObject->getEntryPoint(0)); - cursor["resultTexture"].setResource(gResultTextureUAV); - cursor["uniforms"].setData(&gUniforms, sizeof(Uniforms)); - cursor["sceneBVH"].setResource(gTLAS); - cursor["primitiveBuffer"].setResource(gPrimitiveBufferSRV); - renderEncoder->dispatchCompute((windowWidth + 15) / 16, (windowHeight + 15) / 16, 1); - renderEncoder->endEncoding(); - renderCommandBuffer->close(); - gQueue->executeCommandBuffer(renderCommandBuffer); + ITextureResource::Desc resultTextureDesc = {}; + resultTextureDesc.type = IResource::Type::Texture2D; + resultTextureDesc.numMipLevels = 1; + resultTextureDesc.size.width = windowWidth; + resultTextureDesc.size.height = windowHeight; + resultTextureDesc.size.depth = 1; + resultTextureDesc.defaultState = ResourceState::UnorderedAccess; + resultTextureDesc.format = Format::R16G16B16A16_FLOAT; + gResultTexture = gDevice->createTextureResource(resultTextureDesc); + IResourceView::Desc resultUAVDesc = {}; + resultUAVDesc.format = resultTextureDesc.format; + resultUAVDesc.type = IResourceView::Type::UnorderedAccess; + gResultTextureUAV = gDevice->createTextureView(gResultTexture, resultUAVDesc); } + virtual void windowSizeChanged() override { - ComPtr<ICommandBuffer> presentCommandBuffer = - gTransientHeaps[frameBufferIndex]->createCommandBuffer(); - auto presentEncoder = presentCommandBuffer->encodeRenderCommands( - gRenderPass, gFramebuffers[frameBufferIndex]); - gfx::Viewport viewport = {}; - viewport.maxZ = 1.0f; - viewport.extentX = (float)windowWidth; - viewport.extentY = (float)windowHeight; - presentEncoder->setViewportAndScissor(viewport); - auto rootObject = presentEncoder->bindPipeline(gPresentPipelineState); - auto cursor = ShaderCursor(rootObject->getEntryPoint(1)); - cursor["t"].setResource(gResultTextureUAV); - presentEncoder->setVertexBuffer( - 0, gFullScreenVertexBuffer); - presentEncoder->setPrimitiveTopology(PrimitiveTopology::TriangleList); - presentEncoder->draw(3); - presentEncoder->endEncoding(); - presentCommandBuffer->close(); - gQueue->executeCommandBuffer(presentCommandBuffer); + WindowedAppBase::windowSizeChanged(); + createResultTexture(); } - if (!isTestMode()) + glm::vec3 getVectorFromSphericalAngles(float theta, float phi) { - // With that, we are done drawing for one frame, and ready for the next. - // - gSwapchain->present(); + auto sinTheta = sin(theta); + auto cosTheta = cos(theta); + auto sinPhi = sin(phi); + auto cosPhi = cos(phi); + return glm::vec3(-sinTheta * cosPhi, sinPhi, -cosTheta * cosPhi); + } + void updateUniforms() + { + gUniforms.screenWidth = (float)windowWidth; + gUniforms.screenHeight = (float)windowHeight; + if (!lastTime) + lastTime = getCurrentTime(); + uint64_t currentTime = getCurrentTime(); + float deltaTime = float(double(currentTime - lastTime) / double(getTimerFrequency())); + lastTime = currentTime; + + auto camDir = + getVectorFromSphericalAngles(cameraOrientationAngles[0], cameraOrientationAngles[1]); + auto camUp = getVectorFromSphericalAngles( + cameraOrientationAngles[0], + cameraOrientationAngles[1] + glm::pi<float>() * 0.5f); + auto camRight = glm::cross(camDir, camUp); + + glm::vec3 movement = glm::vec3(0); + if (wPressed) + movement += camDir; + if (sPressed) + movement -= camDir; + if (aPressed) + movement -= camRight; + if (dPressed) + movement += camRight; + + cameraPosition += deltaTime * translationScale * movement; + + memcpy(gUniforms.cameraDir, &camDir, sizeof(float) * 3); + memcpy(gUniforms.cameraUp, &camUp, sizeof(float) * 3); + memcpy(gUniforms.cameraRight, &camRight, sizeof(float) * 3); + memcpy(gUniforms.cameraPosition, &cameraPosition, sizeof(float) * 3); + auto lightDir = glm::normalize(glm::vec3(1.0f, 3.0f, 2.0f)); + memcpy(gUniforms.lightDir, &lightDir, sizeof(float) * 3); } -} + virtual void renderFrame(int frameBufferIndex) override + { + updateUniforms(); + { + ComPtr<ICommandBuffer> renderCommandBuffer = + gTransientHeaps[frameBufferIndex]->createCommandBuffer(); + auto renderEncoder = renderCommandBuffer->encodeComputeCommands(); + auto rootObject = renderEncoder->bindPipeline(gRenderPipelineState); + auto cursor = ShaderCursor(rootObject->getEntryPoint(0)); + cursor["resultTexture"].setResource(gResultTextureUAV); + cursor["uniforms"].setData(&gUniforms, sizeof(Uniforms)); + cursor["sceneBVH"].setResource(gTLAS); + cursor["primitiveBuffer"].setResource(gPrimitiveBufferSRV); + renderEncoder->dispatchCompute((windowWidth + 15) / 16, (windowHeight + 15) / 16, 1); + renderEncoder->endEncoding(); + renderCommandBuffer->close(); + gQueue->executeCommandBuffer(renderCommandBuffer); + } + + { + ComPtr<ICommandBuffer> presentCommandBuffer = + gTransientHeaps[frameBufferIndex]->createCommandBuffer(); + auto presentEncoder = presentCommandBuffer->encodeRenderCommands( + gRenderPass, + gFramebuffers[frameBufferIndex]); + gfx::Viewport viewport = {}; + viewport.maxZ = 1.0f; + viewport.extentX = (float)windowWidth; + viewport.extentY = (float)windowHeight; + presentEncoder->setViewportAndScissor(viewport); + auto rootObject = presentEncoder->bindPipeline(gPresentPipelineState); + auto cursor = ShaderCursor(rootObject->getEntryPoint(1)); + cursor["t"].setResource(gResultTextureUAV); + presentEncoder->setVertexBuffer(0, gFullScreenVertexBuffer); + presentEncoder->setPrimitiveTopology(PrimitiveTopology::TriangleList); + presentEncoder->draw(3); + presentEncoder->endEncoding(); + presentCommandBuffer->close(); + gQueue->executeCommandBuffer(presentCommandBuffer); + } + + if (!isTestMode()) + { + // With that, we are done drawing for one frame, and ready for the next. + // + gSwapchain->present(); + } + } }; // This macro instantiates an appropriate main function to diff --git a/examples/shader-object/main.cpp b/examples/shader-object/main.cpp index 603278bcf..74d81604b 100644 --- a/examples/shader-object/main.cpp +++ b/examples/shader-object/main.cpp @@ -8,14 +8,14 @@ // simplifies shader specialization and parameter binding when using `interface` typed // shader parameters. // -#include "slang.h" #include "slang-com-ptr.h" +#include "slang.h" using Slang::ComPtr; -#include "slang-gfx.h" +#include "examples/example-base/example-base.h" #include "gfx-util/shader-cursor.h" +#include "slang-gfx.h" #include "source/core/slang-basic.h" -#include "examples/example-base/example-base.h" using namespace gfx; @@ -37,7 +37,7 @@ Result loadShaderProgram( // creates a Slang compilation session for us, so we just grab and use it here. ComPtr<slang::ISession> slangSession; SLANG_RETURN_ON_FAIL(device->getSlangSession(slangSession.writeRef())); - + // Once the session has been obtained, we can start loading code into it. // // The simplest way to load code is by calling `loadModule` with the name of a Slang @@ -61,7 +61,7 @@ Result loadShaderProgram( Slang::String path = resourceBase.resolveResource("shader-object.slang"); slang::IModule* module = slangSession->loadModule(path.getBuffer(), diagnosticsBlob.writeRef()); diagnoseIfNeeded(diagnosticsBlob); - if(!module) + if (!module) return SLANG_FAIL; // Loading the `shader-object` module will compile and check all the shader code in it, @@ -74,13 +74,13 @@ Result loadShaderProgram( // is no umambiguous way for the compiler to know which functions represent entry // points when it parses your code via `loadModule()`. // - char const* computeEntryPointName = "computeMain"; + char const* computeEntryPointName = "computeMain"; ComPtr<slang::IEntryPoint> computeEntryPoint; SLANG_RETURN_ON_FAIL( module->findEntryPointByName(computeEntryPointName, computeEntryPoint.writeRef())); - + // At this point we have a few different Slang API objects that represent - // pieces of our code: `module`, `vertexEntryPoint`, and `fragmentEntryPoint`. + // 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 @@ -177,10 +177,8 @@ int main(int argc, char* argv[]) bufferDesc.memoryType = MemoryType::DeviceLocal; ComPtr<gfx::IBufferResource> numbersBuffer; - SLANG_RETURN_ON_FAIL(device->createBufferResource( - bufferDesc, - (void*)initialData, - numbersBuffer.writeRef())); + SLANG_RETURN_ON_FAIL( + device->createBufferResource(bufferDesc, (void*)initialData, numbersBuffer.writeRef())); // Create a resource view for the buffer. ComPtr<gfx::IResourceView> bufferView; @@ -217,7 +215,9 @@ int main(int argc, char* argv[]) // Now we can use this type to create a shader object that can be bound to the root object. ComPtr<gfx::IShaderObject> transformer; SLANG_RETURN_ON_FAIL(device->createShaderObject( - addTransformerType, ShaderObjectContainerType::None, transformer.writeRef())); + addTransformerType, + ShaderObjectContainerType::None, + transformer.writeRef())); // Set the `c` field of the `AddTransformer`. float c = 1.0f; gfx::ShaderCursor(transformer).getPath("c").setData(&c, sizeof(float)); @@ -244,7 +244,10 @@ int main(int argc, char* argv[]) // Read back the results. ComPtr<ISlangBlob> resultBlob; SLANG_RETURN_ON_FAIL(device->readBufferResource( - numbersBuffer, 0, numberCount * sizeof(float), resultBlob.writeRef())); + numbersBuffer, + 0, + numberCount * sizeof(float), + resultBlob.writeRef())); auto result = reinterpret_cast<const float*>(resultBlob->getBufferPointer()); for (int i = 0; i < numberCount; i++) printf("%f\n", result[i]); diff --git a/examples/shader-toy/main.cpp b/examples/shader-toy/main.cpp index 13a79c7ee..38212099e 100644 --- a/examples/shader-toy/main.cpp +++ b/examples/shader-toy/main.cpp @@ -10,8 +10,8 @@ // This example uses the Slang C/C++ API, alonmg with its optional type // for managing COM-style reference-counted pointers. // -#include "slang.h" #include "slang-com-ptr.h" +#include "slang.h" using Slang::ComPtr; // This example uses a graphics API abstraction layer that is implemented inside @@ -19,12 +19,12 @@ using Slang::ComPtr; // this layer is *not* required or assumed when using the Slang language, // compiler, and API. // +#include "examples/example-base/example-base.h" #include "slang-gfx.h" +#include "source/core/slang-basic.h" #include "tools/gfx-util/shader-cursor.h" -#include "tools/platform/window.h" #include "tools/platform/performance-counter.h" -#include "examples/example-base/example-base.h" -#include "source/core/slang-basic.h" +#include "tools/platform/window.h" #include <chrono> @@ -43,15 +43,17 @@ struct FullScreenTriangle float position[2]; }; - enum { kVertexCount = 3 }; + enum + { + kVertexCount = 3 + }; static const Vertex kVertices[kVertexCount]; }; -const FullScreenTriangle::Vertex FullScreenTriangle::kVertices[FullScreenTriangle::kVertexCount] = -{ - { { -1, -1 } }, - { { -1, 3 } }, - { { 3, -1 } }, +const FullScreenTriangle::Vertex FullScreenTriangle::kVertices[FullScreenTriangle::kVertexCount] = { + {{-1, -1}}, + {{-1, 3}}, + {{3, -1}}, }; // The application itself will be encapsulated in a C++ `struct` type @@ -60,333 +62,336 @@ const FullScreenTriangle::Vertex FullScreenTriangle::kVertices[FullScreenTriangl struct ShaderToyApp : public WindowedAppBase { -// The uniform data used by the shader is defined here as a simple -// POD ("plain old data") type. -// -// Note: This type must match the declaration of `ShaderToyUniforms` -// in the file `shader-toy.slang`. -// -// An application could instead use a shared header file to define -// this type, or use Slang's reflection capabilities to allocate -// and set parameters at runtime. For this simple example we did -// the expedient thing of having distinct Slang and C++ declarations. -// -struct Uniforms -{ - float iMouse[4]; - float iResolution[2]; - float iTime; -}; - -// The main interesting part of the host application code is where we -// load, compile, inspect, and compose the Slang shader code. -// -Result loadShaderProgram(gfx::IDevice* device, ComPtr<gfx::IShaderProgram>& outShaderProgram) -{ - // We need to obatin a compilation session (`slang::ISession`) that will provide - // a scope to all the compilation and loading of code we do. - // - // Our example application uses the `gfx` graphics API abstraction layer, which already - // creates a Slang compilation session for us, so we just grab and use it here. - ComPtr<slang::ISession> slangSession; - SLANG_RETURN_ON_FAIL(device->getSlangSession(slangSession.writeRef())); - - // Once the session has been obtained, we can start loading code into it. - // - // The simplest way to load code is by calling `loadModule` with the name of a Slang - // module. A call to `loadModule("MyStuff")` will behave more or less as if you - // wrote: - // - // import MyStuff; - // - // In a Slang shader file. The compiler will use its search paths to try to locate - // `MyModule.slang`, then compile and load that file. If a matching module had - // already been loaded previously, that would be used directly. + // The uniform data used by the shader is defined here as a simple + // POD ("plain old data") type. // - // Note: The only interesting wrinkle here is that our file is named `shader-toy` with - // a hyphen in it, so the name is not directly usable as an identifier in Slang code. - // Instead, when trying to import this module in the context of Slang code, a user - // needs to replace the hyphens with underscores: + // Note: This type must match the declaration of `ShaderToyUniforms` + // in the file `shader-toy.slang`. // - // import shader_toy; + // An application could instead use a shared header file to define + // this type, or use Slang's reflection capabilities to allocate + // and set parameters at runtime. For this simple example we did + // the expedient thing of having distinct Slang and C++ declarations. // - ComPtr<slang::IBlob> diagnosticsBlob; - Slang::String shaderToyPath = resourceBase.resolveResource("shader-toy.slang"); - slang::IModule* module = slangSession->loadModule(shaderToyPath.getBuffer(), diagnosticsBlob.writeRef()); - diagnoseIfNeeded(diagnosticsBlob); - if(!module) - return SLANG_FAIL; - - // Loading the `shader-toy` module will compile and check all the shader code in it, - // including the shader entry points we want to use. Now that the module is loaded - // we can look up those entry points by name. - // - // Note: If you are using this `loadModule` approach to load your shader code it is - // important to tag your entry point functions with the `[shader("...")]` attribute - // (e.g., `[shader("vertex")] void vertexMain(...)`). Without that information there - // is no umambiguous way for the compiler to know which functions represent entry - // points when it parses your code via `loadModule()`. - // - char const* vertexEntryPointName = "vertexMain"; - char const* fragmentEntryPointName = "fragmentMain"; - // - ComPtr<slang::IEntryPoint> vertexEntryPoint; - SLANG_RETURN_ON_FAIL(module->findEntryPointByName(vertexEntryPointName, vertexEntryPoint.writeRef())); - // - ComPtr<slang::IEntryPoint> fragmentEntryPoint; - SLANG_RETURN_ON_FAIL(module->findEntryPointByName(fragmentEntryPointName, fragmentEntryPoint.writeRef())); + struct Uniforms + { + float iMouse[4]; + float iResolution[2]; + float iTime; + }; - // 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> composedProgram; - SlangResult result = slangSession->createCompositeComponentType( - componentTypes.getBuffer(), - componentTypes.getCount(), - composedProgram.writeRef(), - diagnosticsBlob.writeRef()); - diagnoseIfNeeded(diagnosticsBlob); - SLANG_RETURN_ON_FAIL(result); - - // At this point, `composedProgram` represents the shader program - // we want to run, and the vertex and fragment shader there have - // been checked. - // - // We could use the Slang reflection API on `composedProgram` at this - // point to query things like the locations and offsets of the - // various uniform parameters, textures, etc. + // The main interesting part of the host application code is where we + // load, compile, inspect, and compose the Slang shader code. // - // What *cannot* be done yet at this point is actually generating - // kernel code, because `composedProgram` includes a generic type - // parameter as part of the `fragmentMain` entry point: - // - // void fragmentMain<T : IShaderToyImageShader>(...) - // - // Our next task is to load code for a type we'd like to plug in - // for `T` there. - // - // Because Slang supports modular programming, there is no requirement - // that a type we want to plug in for `T` has to come from the - // same module, and to demonstrate that we will load a different - // module to provide the effect type we will plug in. - // - const char* effectTypeName = "ExampleEffect"; - Slang::String effectModulePath = resourceBase.resolveResource("example-effect.slang"); - slang::IModule* effectModule = slangSession->loadModule(effectModulePath.getBuffer(), diagnosticsBlob.writeRef()); - diagnoseIfNeeded(diagnosticsBlob); - if(!module) - return SLANG_FAIL; - - // Once we've loaded the code module that defines out effect type, - // we can look it up by name using the reflection information on - // the module. - // - // Note: A future version of the Slang API will support enumerating - // the types declared in a module so that we do not have to hard-code - // the name here. - // - auto effectType = effectModule->getLayout()->findTypeByName(effectTypeName); + Result loadShaderProgram(gfx::IDevice* device, ComPtr<gfx::IShaderProgram>& outShaderProgram) + { + // We need to obatin a compilation session (`slang::ISession`) that will provide + // a scope to all the compilation and loading of code we do. + // + // Our example application uses the `gfx` graphics API abstraction layer, which already + // creates a Slang compilation session for us, so we just grab and use it here. + ComPtr<slang::ISession> slangSession; + SLANG_RETURN_ON_FAIL(device->getSlangSession(slangSession.writeRef())); - // Now that we have the `effectType` we want to plug in to our generic - // shader, we need to specialize the shader to that type. - // - // Because a shader program could have zero or more specialization parameters, - // we need to build up an array of specialization arguments. - // - Slang::List<slang::SpecializationArg> specializationArgs; + // Once the session has been obtained, we can start loading code into it. + // + // The simplest way to load code is by calling `loadModule` with the name of a Slang + // module. A call to `loadModule("MyStuff")` will behave more or less as if you + // wrote: + // + // import MyStuff; + // + // In a Slang shader file. The compiler will use its search paths to try to locate + // `MyModule.slang`, then compile and load that file. If a matching module had + // already been loaded previously, that would be used directly. + // + // Note: The only interesting wrinkle here is that our file is named `shader-toy` with + // a hyphen in it, so the name is not directly usable as an identifier in Slang code. + // Instead, when trying to import this module in the context of Slang code, a user + // needs to replace the hyphens with underscores: + // + // import shader_toy; + // + ComPtr<slang::IBlob> diagnosticsBlob; + Slang::String shaderToyPath = resourceBase.resolveResource("shader-toy.slang"); + slang::IModule* module = + slangSession->loadModule(shaderToyPath.getBuffer(), diagnosticsBlob.writeRef()); + diagnoseIfNeeded(diagnosticsBlob); + if (!module) + return SLANG_FAIL; + + // Loading the `shader-toy` module will compile and check all the shader code in it, + // including the shader entry points we want to use. Now that the module is loaded + // we can look up those entry points by name. + // + // Note: If you are using this `loadModule` approach to load your shader code it is + // important to tag your entry point functions with the `[shader("...")]` attribute + // (e.g., `[shader("vertex")] void vertexMain(...)`). Without that information there + // is no umambiguous way for the compiler to know which functions represent entry + // points when it parses your code via `loadModule()`. + // + char const* vertexEntryPointName = "vertexMain"; + char const* fragmentEntryPointName = "fragmentMain"; + // + ComPtr<slang::IEntryPoint> vertexEntryPoint; + SLANG_RETURN_ON_FAIL( + module->findEntryPointByName(vertexEntryPointName, vertexEntryPoint.writeRef())); + // + ComPtr<slang::IEntryPoint> fragmentEntryPoint; + SLANG_RETURN_ON_FAIL( + module->findEntryPointByName(fragmentEntryPointName, fragmentEntryPoint.writeRef())); - { - // In our case, we only have a single specialization argument we plan - // to use, and it is a type argument. + // 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> composedProgram; + SlangResult result = slangSession->createCompositeComponentType( + componentTypes.getBuffer(), + componentTypes.getCount(), + composedProgram.writeRef(), + diagnosticsBlob.writeRef()); + diagnoseIfNeeded(diagnosticsBlob); + SLANG_RETURN_ON_FAIL(result); + + // At this point, `composedProgram` represents the shader program + // we want to run, and the vertex and fragment shader there have + // been checked. + // + // We could use the Slang reflection API on `composedProgram` at this + // point to query things like the locations and offsets of the + // various uniform parameters, textures, etc. + // + // What *cannot* be done yet at this point is actually generating + // kernel code, because `composedProgram` includes a generic type + // parameter as part of the `fragmentMain` entry point: + // + // void fragmentMain<T : IShaderToyImageShader>(...) + // + // Our next task is to load code for a type we'd like to plug in + // for `T` there. + // + // Because Slang supports modular programming, there is no requirement + // that a type we want to plug in for `T` has to come from the + // same module, and to demonstrate that we will load a different + // module to provide the effect type we will plug in. + // + const char* effectTypeName = "ExampleEffect"; + Slang::String effectModulePath = resourceBase.resolveResource("example-effect.slang"); + slang::IModule* effectModule = + slangSession->loadModule(effectModulePath.getBuffer(), diagnosticsBlob.writeRef()); + diagnoseIfNeeded(diagnosticsBlob); + if (!module) + return SLANG_FAIL; + + // Once we've loaded the code module that defines out effect type, + // we can look it up by name using the reflection information on + // the module. + // + // Note: A future version of the Slang API will support enumerating + // the types declared in a module so that we do not have to hard-code + // the name here. + // + auto effectType = effectModule->getLayout()->findTypeByName(effectTypeName); + + // Now that we have the `effectType` we want to plug in to our generic + // shader, we need to specialize the shader to that type. + // + // Because a shader program could have zero or more specialization parameters, + // we need to build up an array of specialization arguments. // - slang::SpecializationArg effectTypeArg; - effectTypeArg.kind = slang::SpecializationArg::Kind::Type; - effectTypeArg.type = effectType; - specializationArgs.add(effectTypeArg); + Slang::List<slang::SpecializationArg> specializationArgs; + + { + // In our case, we only have a single specialization argument we plan + // to use, and it is a type argument. + // + slang::SpecializationArg effectTypeArg; + effectTypeArg.kind = slang::SpecializationArg::Kind::Type; + effectTypeArg.type = effectType; + specializationArgs.add(effectTypeArg); + } + + // Specialization of a component type is a single Slang API call, but + // we need to deal with the possibility of diagnostic output on failure. + // For example, if we tried to specialize the shader program to a + // type like `int` that doesn't support the `IShaderToyImageShader` interface, + // this is the step where we'd get an error message saying so. + // + ComPtr<slang::IComponentType> specializedProgram; + result = composedProgram->specialize( + specializationArgs.getBuffer(), + specializationArgs.getCount(), + specializedProgram.writeRef(), + diagnosticsBlob.writeRef()); + diagnoseIfNeeded(diagnosticsBlob); + SLANG_RETURN_ON_FAIL(result); + + // At this point we have a specialized shader program that represents our + // intention to run the `vertexMain` and `fragmentMain` entry points, + // specialized to the `ExampleEffect` type we loaded. + // + // We can now *link* the program, which ensures that all of the code that + // it transitively depends on has been pulled together into a single + // component type. + // + ComPtr<slang::IComponentType> linkedProgram; + result = specializedProgram->link(linkedProgram.writeRef(), diagnosticsBlob.writeRef()); + diagnoseIfNeeded(diagnosticsBlob); + SLANG_RETURN_ON_FAIL(result); + + gfx::IShaderProgram::Desc programDesc = {}; + programDesc.slangGlobalScope = linkedProgram.get(); + auto shaderProgram = device->createProgram(programDesc); + outShaderProgram = shaderProgram; + return SLANG_OK; } - // Specialization of a component type is a single Slang API call, but - // we need to deal with the possibility of diagnostic output on failure. - // For example, if we tried to specialize the shader program to a - // type like `int` that doesn't support the `IShaderToyImageShader` interface, - // this is the step where we'd get an error message saying so. - // - ComPtr<slang::IComponentType> specializedProgram; - result = composedProgram->specialize( - specializationArgs.getBuffer(), - specializationArgs.getCount(), - specializedProgram.writeRef(), - diagnosticsBlob.writeRef()); - diagnoseIfNeeded(diagnosticsBlob); - SLANG_RETURN_ON_FAIL(result); - - // At this point we have a specialized shader program that represents our - // intention to run the `vertexMain` and `fragmentMain` entry points, - // specialized to the `ExampleEffect` type we loaded. - // - // We can now *link* the program, which ensures that all of the code that - // it transitively depends on has been pulled together into a single - // component type. - // - ComPtr<slang::IComponentType> linkedProgram; - result = specializedProgram->link( - linkedProgram.writeRef(), - diagnosticsBlob.writeRef()); - diagnoseIfNeeded(diagnosticsBlob); - SLANG_RETURN_ON_FAIL(result); - - gfx::IShaderProgram::Desc programDesc = {}; - programDesc.slangGlobalScope = linkedProgram.get(); - auto shaderProgram = device->createProgram(programDesc); - outShaderProgram = shaderProgram; - return SLANG_OK; -} - -ComPtr<IShaderProgram> gShaderProgram; -ComPtr<gfx::IPipelineState> gPipelineState; -ComPtr<gfx::IBufferResource> gVertexBuffer; - -Result initialize() -{ - initializeBase("Shader Toy", 1024, 768); - gWindow->events.mouseMove = [this](const platform::MouseEventArgs& e) { handleEvent(e); }; - gWindow->events.mouseUp = [this](const platform::MouseEventArgs& e) { handleEvent(e); }; - gWindow->events.mouseDown = [this](const platform::MouseEventArgs& e) { handleEvent(e); }; + ComPtr<IShaderProgram> gShaderProgram; + ComPtr<gfx::IPipelineState> gPipelineState; + ComPtr<gfx::IBufferResource> gVertexBuffer; - InputElementDesc inputElements[] = { - { "POSITION", 0, Format::R32G32_FLOAT, offsetof(FullScreenTriangle::Vertex, position) }, - }; - auto inputLayout = gDevice->createInputLayout( - sizeof(FullScreenTriangle::Vertex), - &inputElements[0], - SLANG_COUNT_OF(inputElements)); - if(!inputLayout) return SLANG_FAIL; - - IBufferResource::Desc vertexBufferDesc; - vertexBufferDesc.type = IResource::Type::Buffer; - vertexBufferDesc.sizeInBytes = FullScreenTriangle::kVertexCount * sizeof(FullScreenTriangle::Vertex); - vertexBufferDesc.defaultState = ResourceState::VertexBuffer; - gVertexBuffer = gDevice->createBufferResource( - vertexBufferDesc, - &FullScreenTriangle::kVertices[0]); - if(!gVertexBuffer) return SLANG_FAIL; - - SLANG_RETURN_ON_FAIL(loadShaderProgram(gDevice, gShaderProgram)); - - // Create pipeline. - GraphicsPipelineStateDesc desc; - desc.inputLayout = inputLayout; - desc.program = gShaderProgram; - desc.framebufferLayout = gFramebufferLayout; - auto pipelineState = gDevice->createGraphicsPipelineState(desc); - if (!pipelineState) - return SLANG_FAIL; - - gPipelineState = pipelineState; - - return SLANG_OK; -} - -bool wasMouseDown = false; -bool isMouseDown = false; -float lastMouseX = 0.0f; -float lastMouseY = 0.0f; -float clickMouseX = 0.0f; -float clickMouseY = 0.0f; - -bool firstTime = true; -platform::TimePoint startTime; - -virtual void renderFrame(int frameIndex) override -{ - auto commandBuffer = gTransientHeaps[frameIndex]->createCommandBuffer(); - if( firstTime ) + Result initialize() { - startTime = platform::PerformanceCounter::now(); - firstTime = false; + initializeBase("Shader Toy", 1024, 768); + gWindow->events.mouseMove = [this](const platform::MouseEventArgs& e) { handleEvent(e); }; + gWindow->events.mouseUp = [this](const platform::MouseEventArgs& e) { handleEvent(e); }; + gWindow->events.mouseDown = [this](const platform::MouseEventArgs& e) { handleEvent(e); }; + + InputElementDesc inputElements[] = { + {"POSITION", 0, Format::R32G32_FLOAT, offsetof(FullScreenTriangle::Vertex, position)}, + }; + auto inputLayout = gDevice->createInputLayout( + sizeof(FullScreenTriangle::Vertex), + &inputElements[0], + SLANG_COUNT_OF(inputElements)); + if (!inputLayout) + return SLANG_FAIL; + + IBufferResource::Desc vertexBufferDesc; + vertexBufferDesc.type = IResource::Type::Buffer; + vertexBufferDesc.sizeInBytes = + FullScreenTriangle::kVertexCount * sizeof(FullScreenTriangle::Vertex); + vertexBufferDesc.defaultState = ResourceState::VertexBuffer; + gVertexBuffer = + gDevice->createBufferResource(vertexBufferDesc, &FullScreenTriangle::kVertices[0]); + if (!gVertexBuffer) + return SLANG_FAIL; + + SLANG_RETURN_ON_FAIL(loadShaderProgram(gDevice, gShaderProgram)); + + // Create pipeline. + GraphicsPipelineStateDesc desc; + desc.inputLayout = inputLayout; + desc.program = gShaderProgram; + desc.framebufferLayout = gFramebufferLayout; + auto pipelineState = gDevice->createGraphicsPipelineState(desc); + if (!pipelineState) + return SLANG_FAIL; + + gPipelineState = pipelineState; + + return SLANG_OK; } - // Update uniform buffer. + bool wasMouseDown = false; + bool isMouseDown = false; + float lastMouseX = 0.0f; + float lastMouseY = 0.0f; + float clickMouseX = 0.0f; + float clickMouseY = 0.0f; + + bool firstTime = true; + platform::TimePoint startTime; - Uniforms uniforms = {}; + virtual void renderFrame(int frameIndex) override { - bool isMouseClick = isMouseDown && !wasMouseDown; - wasMouseDown = isMouseDown; + auto commandBuffer = gTransientHeaps[frameIndex]->createCommandBuffer(); + if (firstTime) + { + startTime = platform::PerformanceCounter::now(); + firstTime = false; + } + + // Update uniform buffer. - if( isMouseClick ) + Uniforms uniforms = {}; { - clickMouseX = lastMouseX; - clickMouseY = lastMouseY; + bool isMouseClick = isMouseDown && !wasMouseDown; + wasMouseDown = isMouseDown; + + if (isMouseClick) + { + clickMouseX = lastMouseX; + clickMouseY = lastMouseY; + } + + uniforms.iMouse[0] = lastMouseX; + uniforms.iMouse[1] = lastMouseY; + uniforms.iMouse[2] = isMouseDown ? clickMouseX : -clickMouseX; + uniforms.iMouse[3] = isMouseClick ? clickMouseY : -clickMouseY; + uniforms.iTime = platform::PerformanceCounter::getElapsedTimeInSeconds(startTime); + uniforms.iResolution[0] = float(windowWidth); + uniforms.iResolution[1] = float(windowHeight); } - uniforms.iMouse[0] = lastMouseX; - uniforms.iMouse[1] = lastMouseY; - uniforms.iMouse[2] = isMouseDown ? clickMouseX : -clickMouseX; - uniforms.iMouse[3] = isMouseClick ? clickMouseY : -clickMouseY; - uniforms.iTime = platform::PerformanceCounter::getElapsedTimeInSeconds(startTime); - uniforms.iResolution[0] = float(windowWidth); - uniforms.iResolution[1] = float(windowHeight); + // Encode render commands. + auto encoder = commandBuffer->encodeRenderCommands(gRenderPass, gFramebuffers[frameIndex]); + + gfx::Viewport viewport = {}; + viewport.maxZ = 1.0f; + viewport.extentX = (float)windowWidth; + viewport.extentY = (float)windowHeight; + encoder->setViewportAndScissor(viewport); + auto rootObject = encoder->bindPipeline(gPipelineState); + auto constantBuffer = rootObject->getObject(ShaderOffset()); + constantBuffer->setData(ShaderOffset(), &uniforms, sizeof(uniforms)); + + encoder->setVertexBuffer(0, gVertexBuffer); + encoder->setPrimitiveTopology(PrimitiveTopology::TriangleList); + encoder->draw(3); + encoder->endEncoding(); + commandBuffer->close(); + + gQueue->executeCommandBuffer(commandBuffer); + gSwapchain->present(); + } + void handleEvent(const platform::MouseEventArgs& event) + { + isMouseDown = ((int)event.buttons & (int)platform::ButtonState::Enum::LeftButton) != 0; + lastMouseX = (float)event.x; + lastMouseY = (float)event.y; } - - // Encode render commands. - auto encoder = commandBuffer->encodeRenderCommands(gRenderPass, gFramebuffers[frameIndex]); - - gfx::Viewport viewport = {}; - viewport.maxZ = 1.0f; - viewport.extentX = (float)windowWidth; - viewport.extentY = (float)windowHeight; - encoder->setViewportAndScissor(viewport); - auto rootObject = encoder->bindPipeline(gPipelineState); - auto constantBuffer = rootObject->getObject(ShaderOffset()); - constantBuffer->setData(ShaderOffset(), &uniforms, sizeof(uniforms)); - - encoder->setVertexBuffer(0, gVertexBuffer); - encoder->setPrimitiveTopology(PrimitiveTopology::TriangleList); - encoder->draw(3); - encoder->endEncoding(); - commandBuffer->close(); - - gQueue->executeCommandBuffer(commandBuffer); - gSwapchain->present(); -} - -void handleEvent(const platform::MouseEventArgs& event) -{ - isMouseDown = ((int)event.buttons & (int)platform::ButtonState::Enum::LeftButton) != 0; - lastMouseX = (float)event.x; - lastMouseY = (float)event.y; -} }; // This macro instantiates an appropriate main function to diff --git a/examples/triangle/main.cpp b/examples/triangle/main.cpp index 88c55c416..d5f929bf2 100644 --- a/examples/triangle/main.cpp +++ b/examples/triangle/main.cpp @@ -32,12 +32,12 @@ // with Slang may depend on an application/engine making certain // design choices in their abstraction layer. // -#include "slang-gfx.h" +#include "examples/example-base/example-base.h" #include "gfx-util/shader-cursor.h" -#include "tools/platform/window.h" #include "slang-com-ptr.h" +#include "slang-gfx.h" #include "source/core/slang-basic.h" -#include "examples/example-base/example-base.h" +#include "tools/platform/window.h" using namespace gfx; using namespace Slang; @@ -55,11 +55,10 @@ 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}}, }; // The example application will be implemented as a `struct`, so that @@ -68,338 +67,340 @@ static const Vertex kVertexData[kVertexCount] = struct HelloWorld : public WindowedAppBase { -// Many Slang API functions return detailed diagnostic information -// (error messages, warnings, etc.) as a "blob" of data, or return -// a null blob pointer instead if there were no issues. -// -// For convenience, we define a subroutine that will dump the information -// in a diagnostic blob if one is produced, and skip it otherwise. -// -void diagnoseIfNeeded(slang::IBlob* diagnosticsBlob) -{ - if( diagnosticsBlob != nullptr ) - { - printf("%s", (const char*) diagnosticsBlob->getBufferPointer()); - } -} - -// The main task an application cares about is compiling shader code -// from souce (if needed) and loading it through the chosen graphics API. -// -// In addition, an application may want to receive reflection information -// about the program, which is what a `slang::ProgramLayout` provides. -// -gfx::Result loadShaderProgram( - gfx::IDevice* device, - gfx::IShaderProgram** outProgram) -{ - // We need to obatin a compilation session (`slang::ISession`) that will provide - // a scope to all the compilation and loading of code we do. - // - // Our example application uses the `gfx` graphics API abstraction layer, which already - // creates a Slang compilation session for us, so we just grab and use it here. - ComPtr<slang::ISession> slangSession; - slangSession = device->getSlangSession(); - - // We can now start loading code into the slang session. - // - // The simplest way to load code is by calling `loadModule` with the name of a Slang - // module. A call to `loadModule("MyStuff")` will behave more or less as if you - // wrote: - // - // import MyStuff; - // - // In a Slang shader file. The compiler will use its search paths to try to locate - // `MyModule.slang`, then compile and load that file. If a matching module had - // already been loaded previously, that would be used directly. + // Many Slang API functions return detailed diagnostic information + // (error messages, warnings, etc.) as a "blob" of data, or return + // a null blob pointer instead if there were no issues. // - ComPtr<slang::IBlob> diagnosticsBlob; - Slang::String path = resourceBase.resolveResource("shaders.slang"); - slang::IModule* module = slangSession->loadModule(path.getBuffer(), diagnosticsBlob.writeRef()); - diagnoseIfNeeded(diagnosticsBlob); - if(!module) - return SLANG_FAIL; - - // Loading the `shaders` module will compile and check all the shader code in it, - // including the shader entry points we want to use. Now that the module is loaded - // we can look up those entry points by name. + // For convenience, we define a subroutine that will dump the information + // in a diagnostic blob if one is produced, and skip it otherwise. // - // Note: If you are using this `loadModule` approach to load your shader code it is - // important to tag your entry point functions with the `[shader("...")]` attribute - // (e.g., `[shader("vertex")] void vertexMain(...)`). Without that information there - // is no umambiguous way for the compiler to know which functions represent entry - // points when it parses your code via `loadModule()`. - // - 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())); + void diagnoseIfNeeded(slang::IBlob* diagnosticsBlob) + { + if (diagnosticsBlob != nullptr) + { + printf("%s", (const char*)diagnosticsBlob->getBufferPointer()); + } + } - // 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. + // The main task an application cares about is compiling shader code + // from souce (if needed) and loading it through the chosen graphics API. // - ComPtr<slang::IComponentType> linkedProgram; - SlangResult result = slangSession->createCompositeComponentType( - componentTypes.getBuffer(), - componentTypes.getCount(), - linkedProgram.writeRef(), - diagnosticsBlob.writeRef()); - diagnoseIfNeeded(diagnosticsBlob); - SLANG_RETURN_ON_FAIL(result); - - // 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. + // In addition, an application may want to receive reflection information + // about the program, which is what a `slang::ProgramLayout` provides. // - gfx::IShaderProgram::Desc programDesc = {}; - programDesc.slangGlobalScope = linkedProgram; - SLANG_RETURN_ON_FAIL(device->createProgram(programDesc, outProgram)); - - if (isTestMode()) + gfx::Result loadShaderProgram(gfx::IDevice* device, gfx::IShaderProgram** outProgram) { - printEntrypointHashes(entryPointCount, 1, linkedProgram); - } + // We need to obatin a compilation session (`slang::ISession`) that will provide + // a scope to all the compilation and loading of code we do. + // + // Our example application uses the `gfx` graphics API abstraction layer, which already + // creates a Slang compilation session for us, so we just grab and use it here. + ComPtr<slang::ISession> slangSession; + slangSession = device->getSlangSession(); - return SLANG_OK; -} + // We can now start loading code into the slang session. + // + // The simplest way to load code is by calling `loadModule` with the name of a Slang + // module. A call to `loadModule("MyStuff")` will behave more or less as if you + // wrote: + // + // import MyStuff; + // + // In a Slang shader file. The compiler will use its search paths to try to locate + // `MyModule.slang`, then compile and load that file. If a matching module had + // already been loaded previously, that would be used directly. + // + ComPtr<slang::IBlob> diagnosticsBlob; + Slang::String path = resourceBase.resolveResource("shaders.slang"); + slang::IModule* module = + slangSession->loadModule(path.getBuffer(), diagnosticsBlob.writeRef()); + diagnoseIfNeeded(diagnosticsBlob); + if (!module) + return SLANG_FAIL; + + // Loading the `shaders` module will compile and check all the shader code in it, + // including the shader entry points we want to use. Now that the module is loaded + // we can look up those entry points by name. + // + // Note: If you are using this `loadModule` approach to load your shader code it is + // important to tag your entry point functions with the `[shader("...")]` attribute + // (e.g., `[shader("vertex")] void vertexMain(...)`). Without that information there + // is no umambiguous way for the compiler to know which functions represent entry + // points when it parses your code via `loadModule()`. + // + 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())); -// -// The above function shows the core of what is required to use the -// Slang API as a simple compiler (e.g., a drop-in replacement for -// fxc or dxc). -// -// The rest of this file implements an extremely simple rendering application -// that will execute the vertex/fragment shaders loaded with the function -// we have just defined. -// + // 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); + + // 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)); -// 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. -// -ComPtr<gfx::IPipelineState> gPipelineState; -ComPtr<gfx::IBufferResource> gVertexBuffer; + if (isTestMode()) + { + printEntrypointHashes(entryPointCount, 1, linkedProgram); + } -// 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. -// -Slang::Result initialize() -{ - // Create a window for our application to render into. - // - initializeBase("hello-world", 1024, 768); + return SLANG_OK; + } - // We will create objects needed to configur the "input assembler" - // (IA) stage of the D3D pipeline. // - // First, we create an input layout: + // The above function shows the core of what is required to use the + // Slang API as a simple compiler (e.g., a drop-in replacement for + // fxc or dxc). // - 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. + // The rest of this file implements an extremely simple rendering application + // that will execute the vertex/fragment shaders loaded with the function + // we have just defined. // - IBufferResource::Desc vertexBufferDesc; - vertexBufferDesc.type = IResource::Type::Buffer; - vertexBufferDesc.sizeInBytes = kVertexCount * sizeof(Vertex); - vertexBufferDesc.defaultState = ResourceState::VertexBuffer; - gVertexBuffer = gDevice->createBufferResource(vertexBufferDesc, &kVertexData[0]); - if(!gVertexBuffer) 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(gDevice, 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. + // We will define global variables for the various platform and + // graphics API objects that our application needs: // - GraphicsPipelineStateDesc desc; - desc.inputLayout = inputLayout; - desc.program = shaderProgram; - desc.framebufferLayout = gFramebufferLayout; - auto pipelineState = gDevice->createGraphicsPipelineState(desc); - if (!pipelineState) - return SLANG_FAIL; - - gPipelineState = pipelineState; - - return SLANG_OK; -} - -// With the initialization out of the way, we can now turn our attention -// to the per-frame rendering logic. As with the initialization, there is -// nothing really Slang-specific here, so the commentary doesn't need -// to be very detailed. -// -virtual void renderFrame(int frameBufferIndex) override -{ - ComPtr<ICommandBuffer> commandBuffer = gTransientHeaps[frameBufferIndex]->createCommandBuffer(); - auto renderEncoder = commandBuffer->encodeRenderCommands(gRenderPass, gFramebuffers[frameBufferIndex]); - - gfx::Viewport viewport = {}; - viewport.maxZ = 1.0f; - viewport.extentX = (float)windowWidth; - viewport.extentY = (float)windowHeight; - renderEncoder->setViewportAndScissor(viewport); - - // In order to bind shader parameters to the pipeline, we need - // to know how those parameters were assigned to locations/bindings/registers - // for the target graphics API. + // 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. // - // The Slang compiler assigns locations to parameters in a deterministic - // fashion, so it is possible for a programmer to hard-code locations - // into their application code that will match up with their shaders. - // - // Hard-coding of locations can become intractable as an application needs - // to support more different target platforms and graphics APIs, as well - // as more shaders with different specialized variants. - // - // Rather than rely on hard-coded locations, our examples will make use of - // reflection information provided by the Slang compiler (see `programLayout` - // above), and our example graphics API layer will translate that reflection - // information into a layout for a "root shader object." - // - // The root object will store values/bindings for all of the parameters in - // the `IShaderProgram` used to create the pipeline state. At a conceptual - // level we can think of `rootObject` as representing the "global scope" of - // the shader program that was loaded; it has entries for each global shader - // parameter that was declared. - // - // Readers who are familiar with D3D12 or Vulkan might think of this root - // layout as being similar in spirit to a "root signature" or "pipeline layout." - // - // We start parameter binding by binding the pipeline state in command encoder. - // This method will return a transient root shader object for us to write our - // shader parameters into. - // - auto rootObject = renderEncoder->bindPipeline(gPipelineState); + ComPtr<gfx::IPipelineState> gPipelineState; + ComPtr<gfx::IBufferResource> gVertexBuffer; - // We will update the model-view-projection matrix that is passed - // into the shader code via the `Uniforms` buffer on a per-frame - // basis, even though the data that is loaded does not change - // per-frame (we always use an identity matrix). + // 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. // - auto deviceInfo = gDevice->getDeviceInfo(); + Slang::Result initialize() + { + // Create a window for our application to render into. + // + initializeBase("hello-world", 1024, 768); - // We know that `rootObject` is a root shader object created - // from our program, and that it is set up to hold values for - // all the parameter of that program. In order to actually - // set values, we need to be able to look up the location - // of speciic parameter that we want to set. - // - // Our example graphics API layer supports this operation - // with the idea of a *shader cursor* which can be thought - // of as pointing "into" a particular shader object at - // some location/offset. This design choice abstracts over - // the many ways that different platforms and APIs represent - // the necessary offset information. - // - // We construct an initial shader cursor that points at the - // entire shader program. You can think of this as akin to - // a diretory path of `/` for the root directory in a file - // system. - // - ShaderCursor rootCursor(rootObject); - // - // Next, we use a convenience overload of `operator[]` to - // navigate from the root cursor down to the parameter we - // want to set. - // - // The operation `rootCursor["Uniforms"]` looks up the - // offset/location of the global shader parameter `Uniforms` - // (which is a uniform/constant buffer), and the subsequent - // `["modelViewProjection"]` step navigates from there down - // to the member named `modelViewProjection` in that buffer. - // - // Once we have formed a cursor that "points" at the - // model-view projection matrix, we can set its data directly. - // - rootCursor["Uniforms"]["modelViewProjection"].setData( - deviceInfo.identityProjectionMatrix, sizeof(float) * 16); - // - // Some readers might be concerned about the performance o - // the above operations because of the use of strings. For - // those readers, here are two things to note: - // - // * While these `operator[]` steps do need to perform string - // comparisons, they do *not* make copies of the strings or - // perform any heap allocation. - // - // * There are other overloads of `operator[]` that use the - // *index* of a parameter/field instead of its name, and those - // operations have fixed/constant overhead and perform no - // string comparisons. The indices used are independent of - // the target platform and graphics API, and can thus be - // hard-coded even in cross-platform code. - // + // 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; + gVertexBuffer = gDevice->createBufferResource(vertexBufferDesc, &kVertexData[0]); + if (!gVertexBuffer) + 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(gDevice, shaderProgram.writeRef())); - // 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, gVertexBuffer); - renderEncoder->setPrimitiveTopology(PrimitiveTopology::TriangleList); + // 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 = gFramebufferLayout; + auto pipelineState = gDevice->createGraphicsPipelineState(desc); + if (!pipelineState) + return SLANG_FAIL; - // Finally, we are ready to issue a draw call for a single triangle. - // - renderEncoder->draw(3); - renderEncoder->endEncoding(); - commandBuffer->close(); - gQueue->executeCommandBuffer(commandBuffer); + gPipelineState = pipelineState; + + return SLANG_OK; + } - if (!isTestMode()) + // With the initialization out of the way, we can now turn our attention + // to the per-frame rendering logic. As with the initialization, there is + // nothing really Slang-specific here, so the commentary doesn't need + // to be very detailed. + // + virtual void renderFrame(int frameBufferIndex) override { - // With that, we are done drawing for one frame, and ready for the next. + ComPtr<ICommandBuffer> commandBuffer = + gTransientHeaps[frameBufferIndex]->createCommandBuffer(); + auto renderEncoder = + commandBuffer->encodeRenderCommands(gRenderPass, gFramebuffers[frameBufferIndex]); + + gfx::Viewport viewport = {}; + viewport.maxZ = 1.0f; + viewport.extentX = (float)windowWidth; + viewport.extentY = (float)windowHeight; + renderEncoder->setViewportAndScissor(viewport); + + // In order to bind shader parameters to the pipeline, we need + // to know how those parameters were assigned to locations/bindings/registers + // for the target graphics API. // - gSwapchain->present(); - } -} + // The Slang compiler assigns locations to parameters in a deterministic + // fashion, so it is possible for a programmer to hard-code locations + // into their application code that will match up with their shaders. + // + // Hard-coding of locations can become intractable as an application needs + // to support more different target platforms and graphics APIs, as well + // as more shaders with different specialized variants. + // + // Rather than rely on hard-coded locations, our examples will make use of + // reflection information provided by the Slang compiler (see `programLayout` + // above), and our example graphics API layer will translate that reflection + // information into a layout for a "root shader object." + // + // The root object will store values/bindings for all of the parameters in + // the `IShaderProgram` used to create the pipeline state. At a conceptual + // level we can think of `rootObject` as representing the "global scope" of + // the shader program that was loaded; it has entries for each global shader + // parameter that was declared. + // + // Readers who are familiar with D3D12 or Vulkan might think of this root + // layout as being similar in spirit to a "root signature" or "pipeline layout." + // + // We start parameter binding by binding the pipeline state in command encoder. + // This method will return a transient root shader object for us to write our + // shader parameters into. + // + auto rootObject = renderEncoder->bindPipeline(gPipelineState); + + // We will update the model-view-projection matrix that is passed + // into the shader code via the `Uniforms` buffer on a per-frame + // basis, even though the data that is loaded does not change + // per-frame (we always use an identity matrix). + // + auto deviceInfo = gDevice->getDeviceInfo(); + // We know that `rootObject` is a root shader object created + // from our program, and that it is set up to hold values for + // all the parameter of that program. In order to actually + // set values, we need to be able to look up the location + // of speciic parameter that we want to set. + // + // Our example graphics API layer supports this operation + // with the idea of a *shader cursor* which can be thought + // of as pointing "into" a particular shader object at + // some location/offset. This design choice abstracts over + // the many ways that different platforms and APIs represent + // the necessary offset information. + // + // We construct an initial shader cursor that points at the + // entire shader program. You can think of this as akin to + // a diretory path of `/` for the root directory in a file + // system. + // + ShaderCursor rootCursor(rootObject); + // + // Next, we use a convenience overload of `operator[]` to + // navigate from the root cursor down to the parameter we + // want to set. + // + // The operation `rootCursor["Uniforms"]` looks up the + // offset/location of the global shader parameter `Uniforms` + // (which is a uniform/constant buffer), and the subsequent + // `["modelViewProjection"]` step navigates from there down + // to the member named `modelViewProjection` in that buffer. + // + // Once we have formed a cursor that "points" at the + // model-view projection matrix, we can set its data directly. + // + rootCursor["Uniforms"]["modelViewProjection"].setData( + deviceInfo.identityProjectionMatrix, + sizeof(float) * 16); + // + // Some readers might be concerned about the performance o + // the above operations because of the use of strings. For + // those readers, here are two things to note: + // + // * While these `operator[]` steps do need to perform string + // comparisons, they do *not* make copies of the strings or + // perform any heap allocation. + // + // * There are other overloads of `operator[]` that use the + // *index* of a parameter/field instead of its name, and those + // operations have fixed/constant overhead and perform no + // string comparisons. The indices used are independent of + // the target platform and graphics API, and can thus be + // hard-coded even in cross-platform code. + // + + // 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, gVertexBuffer); + renderEncoder->setPrimitiveTopology(PrimitiveTopology::TriangleList); + + // Finally, we are ready to issue a draw call for a single triangle. + // + renderEncoder->draw(3); + renderEncoder->endEncoding(); + commandBuffer->close(); + gQueue->executeCommandBuffer(commandBuffer); + + if (!isTestMode()) + { + // With that, we are done drawing for one frame, and ready for the next. + // + gSwapchain->present(); + } + } }; // This macro instantiates an appropriate main function to |
