From 2f44d9e01234911dd563f0456b9d861fd8db286d Mon Sep 17 00:00:00 2001 From: Yong He Date: Mon, 18 Oct 2021 12:19:45 -0700 Subject: GFX: implement mutable shader objects. (#1963) * GFX: implement mutable shader objects. * Revert unnecessary changes * Revert more changes. * Fix clang errors. * Fix clang/gcc errors. * Fix clang errors. * Remove CPU test. * Fix after merge. * Fix after merge. * Remove gl test * Code review fixes. * Fixing all vk validation errors. * Flush test output more often. * Fix a crash in `specializeDynamicAssociatedTypeLookup`. * temporarily disable std-lib-serialize test to see what happens * Fix crashes. * Make sure cpu gfx unit tests are properly disabled on TeamCity. * Disable cpu test. * Fix. * Fix cuda. * Disable nv-ray-tracing-motion-blur Co-authored-by: Yong He --- .github/workflows/windows.yml | 2 +- .../gfx-unit-test-tool/gfx-unit-test-tool.vcxproj | 2 + .../gfx-unit-test-tool.vcxproj.filters | 6 + build/visual-studio/gfx/gfx.vcxproj | 1 + build/visual-studio/gfx/gfx.vcxproj.filters | 3 + slang-gfx.h | 27 ++- .../nv-extensions/nv-ray-tracing-motion-blur.slang | 5 +- tools/gfx-unit-test/compute-smoke.cpp | 39 +--- tools/gfx-unit-test/gfx-test-util.cpp | 87 +++++++-- tools/gfx-unit-test/gfx-test-util.h | 33 ++++ tools/gfx-unit-test/mutable-shader-object.cpp | 135 +++++++++++++ tools/gfx-unit-test/mutable-shader-object.slang | 68 +++++++ tools/gfx/command-writer.h | 29 ++- tools/gfx/cpu/render-cpu.cpp | 35 ++-- tools/gfx/cuda/render-cuda.cpp | 45 ++--- tools/gfx/d3d11/render-d3d11.cpp | 134 ++++++------- tools/gfx/d3d12/render-d3d12.cpp | 115 +++++------ tools/gfx/debug-layer.cpp | 59 +++++- tools/gfx/debug-layer.h | 45 +++-- tools/gfx/immediate-renderer-base.cpp | 20 +- tools/gfx/mutable-shader-object.h | 215 +++++++++++++++++++++ tools/gfx/open-gl/render-gl.cpp | 47 ++--- tools/gfx/renderer-shared.cpp | 45 ++++- tools/gfx/renderer-shared.h | 114 +++++++++-- tools/gfx/simple-transient-resource-heap.h | 19 +- tools/gfx/transient-resource-heap-base.h | 18 +- tools/gfx/vulkan/render-vk.cpp | 134 +++++++------ tools/slang-test/options.cpp | 4 + tools/slang-test/options.h | 3 + tools/slang-test/slang-test-main.cpp | 8 +- tools/slang-test/test-reporter.cpp | 4 +- 31 files changed, 1070 insertions(+), 431 deletions(-) create mode 100644 tools/gfx-unit-test/mutable-shader-object.cpp create mode 100644 tools/gfx-unit-test/mutable-shader-object.slang create mode 100644 tools/gfx/mutable-shader-object.h diff --git a/.github/workflows/windows.yml b/.github/workflows/windows.yml index 091997303..b3e575780 100644 --- a/.github/workflows/windows.yml +++ b/.github/workflows/windows.yml @@ -49,4 +49,4 @@ jobs: $env:Path += ";$slangTestBinDir"; Invoke-WebRequest -uri "https://github.com/shader-slang/swiftshader/releases/download/v1.0/vk_swiftshader_windows_$testPlatform.zip" -Method "GET" -Outfile "swiftshader.zip"; Expand-Archive "swiftshader.zip" -DestinationPath $slangTestBinDir; - & "$slangTestBinDir\slang-test.exe" -api all-dx12 -appveyor -bindir "$slangTestBinDir\" -platform $testPlatform -configuration ${{matrix.configuration}} -category $testCategory; \ No newline at end of file + & "$slangTestBinDir\slang-test.exe" -api all-dx12 -appveyor -bindir "$slangTestBinDir\" -platform $testPlatform -configuration ${{matrix.configuration}} -category $testCategory 2>&1; \ No newline at end of file diff --git a/build/visual-studio/gfx-unit-test-tool/gfx-unit-test-tool.vcxproj b/build/visual-studio/gfx-unit-test-tool/gfx-unit-test-tool.vcxproj index 7907aac09..4ce8c25a5 100644 --- a/build/visual-studio/gfx-unit-test-tool/gfx-unit-test-tool.vcxproj +++ b/build/visual-studio/gfx-unit-test-tool/gfx-unit-test-tool.vcxproj @@ -280,11 +280,13 @@ + + diff --git a/build/visual-studio/gfx-unit-test-tool/gfx-unit-test-tool.vcxproj.filters b/build/visual-studio/gfx-unit-test-tool/gfx-unit-test-tool.vcxproj.filters index b428981e7..7418406e0 100644 --- a/build/visual-studio/gfx-unit-test-tool/gfx-unit-test-tool.vcxproj.filters +++ b/build/visual-studio/gfx-unit-test-tool/gfx-unit-test-tool.vcxproj.filters @@ -41,6 +41,9 @@ Source Files + + Source Files + Source Files @@ -52,5 +55,8 @@ Source Files + + Source Files + \ No newline at end of file diff --git a/build/visual-studio/gfx/gfx.vcxproj b/build/visual-studio/gfx/gfx.vcxproj index 728e01e72..0ecb21878 100644 --- a/build/visual-studio/gfx/gfx.vcxproj +++ b/build/visual-studio/gfx/gfx.vcxproj @@ -300,6 +300,7 @@ + diff --git a/build/visual-studio/gfx/gfx.vcxproj.filters b/build/visual-studio/gfx/gfx.vcxproj.filters index 6e188bf03..9afde72c1 100644 --- a/build/visual-studio/gfx/gfx.vcxproj.filters +++ b/build/visual-studio/gfx/gfx.vcxproj.filters @@ -48,6 +48,9 @@ Header Files + + Header Files + Header Files diff --git a/slang-gfx.h b/slang-gfx.h index d09c844a6..cf77113f0 100644 --- a/slang-gfx.h +++ b/slang-gfx.h @@ -110,6 +110,8 @@ enum class BindingStyle CountOf, }; +class ITransientResourceHeap; + class IShaderProgram: public ISlangUnknown { public: @@ -685,6 +687,20 @@ struct ShaderOffset SlangInt uniformOffset = 0; SlangInt bindingRangeIndex = 0; SlangInt bindingArrayIndex = 0; + uint32_t getHashCode() const + { + return (uint32_t)(((bindingRangeIndex << 20) + bindingArrayIndex) ^ uniformOffset); + } + bool operator==(const ShaderOffset& other) const + { + return uniformOffset == other.uniformOffset + && bindingRangeIndex == other.bindingRangeIndex + && bindingArrayIndex == other.bindingArrayIndex; + } + bool operator!=(const ShaderOffset& other) const + { + return !this->operator==(other); + } }; enum class ShaderObjectContainerType @@ -734,13 +750,16 @@ public: ShaderOffset const& offset, const slang::SpecializationArg* args, uint32_t count) = 0; + + virtual SLANG_NO_THROW Result SLANG_MCALL getCurrentVersion( + ITransientResourceHeap* transientHeap, + IShaderObject** outObject) = 0; }; #define SLANG_UUID_IShaderObject \ { \ 0xc1fa997e, 0x5ca2, 0x45ae, { 0x9b, 0xcb, 0xc4, 0x35, 0x9e, 0x85, 0x5, 0x85 } \ } - enum class StencilOp : uint8_t { Keep, @@ -1359,6 +1378,7 @@ public: 0xcd48bd29, 0xee72, 0x41b8, { 0xbc, 0xff, 0xa, 0x2b, 0x3a, 0xaa, 0x6d, 0xeb } \ } + class ISwapchain : public ISlangUnknown { public: @@ -1674,6 +1694,11 @@ public: return object; } + virtual SLANG_NO_THROW Result SLANG_MCALL createMutableShaderObject( + slang::TypeReflection* type, + ShaderObjectContainerType container, + IShaderObject** outObject) = 0; + virtual SLANG_NO_THROW Result SLANG_MCALL createProgram(const IShaderProgram::Desc& desc, IShaderProgram** outProgram) = 0; inline ComPtr createProgram(const IShaderProgram::Desc& desc) diff --git a/tests/nv-extensions/nv-ray-tracing-motion-blur.slang b/tests/nv-extensions/nv-ray-tracing-motion-blur.slang index 2c31da99a..f01d2186c 100644 --- a/tests/nv-extensions/nv-ray-tracing-motion-blur.slang +++ b/tests/nv-extensions/nv-ray-tracing-motion-blur.slang @@ -1,4 +1,7 @@ -//TEST:CROSS_COMPILE: -profile glsl_460+GL_EXT_ray_tracing -stage raygeneration -entry main -target spirv-assembly +// Note: this test is disabled because it causes an "error:xxx" line being printed by +// glsl-validator to the console output, this will leads to github actions to terminate +// the job and return failure status. +//DISABLE_TEST:CROSS_COMPILE: -profile glsl_460+GL_EXT_ray_tracing -stage raygeneration -entry main -target spirv-assembly #define TRACING_EPSILON 1e-6 diff --git a/tools/gfx-unit-test/compute-smoke.cpp b/tools/gfx-unit-test/compute-smoke.cpp index 9038cd5b6..9a8298bf8 100644 --- a/tools/gfx-unit-test/compute-smoke.cpp +++ b/tools/gfx-unit-test/compute-smoke.cpp @@ -96,49 +96,14 @@ namespace gfx_test Slang::makeArray(11.0f, 12.0f, 13.0f, 14.0f)); } - void computeSmokeTestAPI(UnitTestContext* context, Slang::RenderApiFlag::Enum api) - { - if ((api & context->enabledApis) == 0) - { - SLANG_IGNORE_TEST - } - Slang::ComPtr device; - IDevice::Desc deviceDesc = {}; - switch (api) - { - case Slang::RenderApiFlag::D3D11: - deviceDesc.deviceType = gfx::DeviceType::DirectX11; - break; - case Slang::RenderApiFlag::D3D12: - deviceDesc.deviceType = gfx::DeviceType::DirectX12; - break; - case Slang::RenderApiFlag::Vulkan: - deviceDesc.deviceType = gfx::DeviceType::Vulkan; - break; - default: - SLANG_IGNORE_TEST - } - deviceDesc.slang.slangGlobalSession = context->slangGlobalSession; - const char* searchPaths[] = { "", "../../tools/gfx-unit-test", "tools/gfx-unit-test" }; - deviceDesc.slang.searchPathCount = (SlangInt)SLANG_COUNT_OF(searchPaths); - deviceDesc.slang.searchPaths = searchPaths; - auto createDeviceResult = gfxCreateDevice(&deviceDesc, device.writeRef()); - if (SLANG_FAILED(createDeviceResult)) - { - SLANG_IGNORE_TEST - } - - computeSmokeTestImpl(device, context); - } - SLANG_UNIT_TEST(computeSmokeD3D11) { - computeSmokeTestAPI(unitTestContext, Slang::RenderApiFlag::D3D11); + runTestImpl(computeSmokeTestImpl, unitTestContext, Slang::RenderApiFlag::D3D11); } SLANG_UNIT_TEST(computeSmokeVulkan) { - computeSmokeTestAPI(unitTestContext, Slang::RenderApiFlag::Vulkan); + runTestImpl(computeSmokeTestImpl, unitTestContext, Slang::RenderApiFlag::Vulkan); } } diff --git a/tools/gfx-unit-test/gfx-test-util.cpp b/tools/gfx-unit-test/gfx-test-util.cpp index 1b23047a1..f1dd6be58 100644 --- a/tools/gfx-unit-test/gfx-test-util.cpp +++ b/tools/gfx-unit-test/gfx-test-util.cpp @@ -3,6 +3,14 @@ #include +#define GFX_ENABLE_RENDERDOC_INTEGRATION 0 + +#if GFX_ENABLE_RENDERDOC_INTEGRATION +# include "external/renderdoc_app.h" +# define WIN32_LEAN_AND_MEAN +# include +#endif + using Slang::ComPtr; namespace gfx_test @@ -64,23 +72,76 @@ namespace gfx_test ComPtr resultBlob; GFX_CHECK_CALL_ABORT(device->readBufferResource( buffer, 0, expectedBufferSize, resultBlob.writeRef())); - if (resultBlob->getBufferSize() < expectedBufferSize) + SLANG_CHECK(resultBlob->getBufferSize() == expectedBufferSize); + // Compare results. + SLANG_CHECK(memcmp(resultBlob->getBufferPointer(), expectedResult, expectedBufferSize) == 0); + } + + Slang::ComPtr createTestingDevice(UnitTestContext* context, Slang::RenderApiFlag::Enum api) + { + Slang::ComPtr device; + gfx::IDevice::Desc deviceDesc = {}; + switch (api) { - getTestReporter()->addResult(TestResult::Fail); - return; + case Slang::RenderApiFlag::D3D11: + deviceDesc.deviceType = gfx::DeviceType::DirectX11; + break; + case Slang::RenderApiFlag::D3D12: + deviceDesc.deviceType = gfx::DeviceType::DirectX12; + break; + case Slang::RenderApiFlag::Vulkan: + deviceDesc.deviceType = gfx::DeviceType::Vulkan; + break; + case Slang::RenderApiFlag::CPU: + deviceDesc.deviceType = gfx::DeviceType::CPU; + break; + case Slang::RenderApiFlag::CUDA: + deviceDesc.deviceType = gfx::DeviceType::CUDA; + break; + case Slang::RenderApiFlag::OpenGl: + deviceDesc.deviceType = gfx::DeviceType::OpenGl; + break; + default: + SLANG_IGNORE_TEST } - - // Compare results. - auto result = reinterpret_cast(resultBlob->getBufferPointer()); - for (int i = 0; i < expectedBufferSize; i++) + deviceDesc.slang.slangGlobalSession = context->slangGlobalSession; + const char* searchPaths[] = { "", "../../tools/gfx-unit-test", "tools/gfx-unit-test" }; + deviceDesc.slang.searchPathCount = (SlangInt)SLANG_COUNT_OF(searchPaths); + deviceDesc.slang.searchPaths = searchPaths; + auto createDeviceResult = gfxCreateDevice(&deviceDesc, device.writeRef()); + if (SLANG_FAILED(createDeviceResult)) { - if (expectedResult[i] != result[i]) - { - getTestReporter()->addResult(TestResult::Fail); - return; - } + SLANG_IGNORE_TEST + } + return device; + } +#if GFX_ENABLE_RENDERDOC_INTEGRATION + RENDERDOC_API_1_1_2* rdoc_api = NULL; + void initializeRenderDoc() + { + if (HMODULE mod = GetModuleHandleA("renderdoc.dll")) + { + pRENDERDOC_GetAPI RENDERDOC_GetAPI = + (pRENDERDOC_GetAPI)GetProcAddress(mod, "RENDERDOC_GetAPI"); + int ret = RENDERDOC_GetAPI(eRENDERDOC_API_Version_1_1_2, (void**)&rdoc_api); + assert(ret == 1); } - getTestReporter()->addResult(TestResult::Pass); } + void renderDocBeginFrame() + { + if (!rdoc_api) initializeRenderDoc(); + if (rdoc_api) rdoc_api->StartFrameCapture(nullptr, nullptr); + } + void renderDocEndFrame() + { + if (rdoc_api) + rdoc_api->EndFrameCapture(nullptr, nullptr); + _fgetchar(); + } +#else + void initializeRenderDoc() {} + void renderDocBeginFrame() {} + void renderDocEndFrame() {} +#endif } diff --git a/tools/gfx-unit-test/gfx-test-util.h b/tools/gfx-unit-test/gfx-test-util.h index c7db19394..b357a310a 100644 --- a/tools/gfx-unit-test/gfx-test-util.h +++ b/tools/gfx-unit-test/gfx-test-util.h @@ -2,6 +2,8 @@ #include "slang-gfx.h" #include "source/core/slang-basic.h" +#include "source/core/slang-render-api-util.h" +#include "tools/unit-test/slang-unit-test.h" namespace gfx_test { @@ -35,6 +37,37 @@ namespace gfx_test memcpy(expectedBuffer.getBuffer(), expectedResult.begin(), bufferSize); return compareComputeResult(device, buffer, expectedBuffer.getBuffer(), bufferSize); } + + Slang::ComPtr createTestingDevice(UnitTestContext* context, Slang::RenderApiFlag::Enum api); + + void initializeRenderDoc(); + void renderDocBeginFrame(); + void renderDocEndFrame(); + + template + void runTestImpl(const ImplFunc& f, UnitTestContext* context, Slang::RenderApiFlag::Enum api) + { + if ((api & context->enabledApis) == 0) + { + SLANG_IGNORE_TEST + } + auto device = createTestingDevice(context, api); + if (!device) + { + SLANG_IGNORE_TEST + } + try + { + renderDocBeginFrame(); + f(device, context); + } + catch (AbortTestException& e) + { + renderDocEndFrame(); + throw e; + } + renderDocEndFrame(); + } #define GFX_CHECK_CALL(x) SLANG_CHECK(!SLANG_FAILED(x)) #define GFX_CHECK_CALL_ABORT(x) SLANG_CHECK_ABORT(!SLANG_FAILED(x)) diff --git a/tools/gfx-unit-test/mutable-shader-object.cpp b/tools/gfx-unit-test/mutable-shader-object.cpp new file mode 100644 index 000000000..efaf95761 --- /dev/null +++ b/tools/gfx-unit-test/mutable-shader-object.cpp @@ -0,0 +1,135 @@ +#include "tools/unit-test/slang-unit-test.h" + +#include "slang-gfx.h" +#include "gfx-test-util.h" +#include "tools/gfx-util/shader-cursor.h" +#include "source/core/slang-basic.h" + +using namespace gfx; + +namespace gfx_test +{ + void mutableShaderObjectTestImpl(IDevice* device, UnitTestContext* context) + { + Slang::ComPtr transientHeap; + ITransientResourceHeap::Desc transientHeapDesc = {}; + transientHeapDesc.constantBufferSize = 4096; + GFX_CHECK_CALL_ABORT( + device->createTransientResourceHeap(transientHeapDesc, transientHeap.writeRef())); + + ComPtr shaderProgram; + slang::ProgramLayout* slangReflection; + GFX_CHECK_CALL_ABORT(loadComputeProgram(device, shaderProgram, "mutable-shader-object", "computeMain", slangReflection)); + + ComputePipelineStateDesc pipelineDesc = {}; + pipelineDesc.program = shaderProgram.get(); + ComPtr pipelineState; + GFX_CHECK_CALL_ABORT( + device->createComputePipelineState(pipelineDesc, pipelineState.writeRef())); + + float initialData[] = { 0.0f, 1.0f, 2.0f, 3.0f }; + const int numberCount = SLANG_COUNT_OF(initialData); + IBufferResource::Desc bufferDesc = {}; + bufferDesc.sizeInBytes = sizeof(initialData); + bufferDesc.format = gfx::Format::Unknown; + bufferDesc.elementSize = sizeof(float); + bufferDesc.allowedStates = ResourceStateSet( + ResourceState::ShaderResource, + ResourceState::UnorderedAccess, + ResourceState::CopyDestination, + ResourceState::CopySource); + bufferDesc.defaultState = ResourceState::UnorderedAccess; + bufferDesc.cpuAccessFlags = AccessFlag::Write | AccessFlag::Read; + + ComPtr numbersBuffer; + GFX_CHECK_CALL_ABORT(device->createBufferResource( + bufferDesc, + (void*)initialData, + numbersBuffer.writeRef())); + + ComPtr bufferView; + IResourceView::Desc viewDesc = {}; + viewDesc.type = IResourceView::Type::UnorderedAccess; + viewDesc.format = Format::Unknown; + GFX_CHECK_CALL_ABORT(device->createBufferView(numbersBuffer, viewDesc, bufferView.writeRef())); + + { + slang::TypeReflection* addTransformerType = + slangReflection->findTypeByName("AddTransformer"); + + ComPtr transformer; + GFX_CHECK_CALL_ABORT(device->createMutableShaderObject( + addTransformerType, ShaderObjectContainerType::None, transformer.writeRef())); + // Set the `c` field of the `AddTransformer`. + float c = 1.0f; + ShaderCursor(transformer).getPath("c").setData(&c, sizeof(float)); + + ICommandQueue::Desc queueDesc = { ICommandQueue::QueueType::Graphics }; + auto queue = device->createCommandQueue(queueDesc); + + auto commandBuffer = transientHeap->createCommandBuffer(); + auto encoder = commandBuffer->encodeComputeCommands(); + + auto rootObject = encoder->bindPipeline(pipelineState); + + auto entryPointCursor = ShaderCursor(rootObject->getEntryPoint(0)); + + entryPointCursor.getPath("buffer").setResource(bufferView); + + // Bind the previously created transformer object to root object. + ComPtr transformerVersion; + transformer->getCurrentVersion(transientHeap, transformerVersion.writeRef()); + entryPointCursor.getPath("transformer").setObject(transformerVersion); + + encoder->dispatchCompute(1, 1, 1); + encoder->endEncoding(); + + auto barrierEncoder = commandBuffer->encodeResourceCommands(); + barrierEncoder->bufferBarrier(1, numbersBuffer.readRef(), ResourceState::UnorderedAccess, ResourceState::UnorderedAccess); + barrierEncoder->endEncoding(); + + encoder = commandBuffer->encodeComputeCommands(); + + rootObject = encoder->bindPipeline(pipelineState); + entryPointCursor = ShaderCursor(rootObject->getEntryPoint(0)); + + // Mutate `transformer` object and run again. + c = 2.0f; + ShaderCursor(transformer).getPath("c").setData(&c, sizeof(float)); + transformer->getCurrentVersion(transientHeap, transformerVersion.writeRef()); + entryPointCursor.getPath("buffer").setResource(bufferView); + entryPointCursor.getPath("transformer").setObject(transformerVersion); + encoder->dispatchCompute(1, 1, 1); + encoder->endEncoding(); + + commandBuffer->close(); + queue->executeCommandBuffer(commandBuffer); + queue->wait(); + } + + compareComputeResult( + device, + numbersBuffer, + Slang::makeArray(3.0f, 4.0f, 5.0f, 6.0f)); + } + + //SLANG_UNIT_TEST(mutableShaderObjectCPU) + //{ + // runTestImpl(mutableShaderObjectTestImpl, unitTestContext, Slang::RenderApiFlag::CPU); + //} + + SLANG_UNIT_TEST(mutableShaderObjectD3D11) + { + runTestImpl(mutableShaderObjectTestImpl, unitTestContext, Slang::RenderApiFlag::D3D11); + } + + SLANG_UNIT_TEST(mutableShaderObjectD3D12) + { + runTestImpl(mutableShaderObjectTestImpl, unitTestContext, Slang::RenderApiFlag::D3D12); + } + + SLANG_UNIT_TEST(mutableShaderObjectVulkan) + { + runTestImpl(mutableShaderObjectTestImpl, unitTestContext, Slang::RenderApiFlag::Vulkan); + } +} diff --git a/tools/gfx-unit-test/mutable-shader-object.slang b/tools/gfx-unit-test/mutable-shader-object.slang new file mode 100644 index 000000000..42d520f13 --- /dev/null +++ b/tools/gfx-unit-test/mutable-shader-object.slang @@ -0,0 +1,68 @@ +// mutable-shader-object.slang + +// This is a copy of `shader-object.slang` in `shader-object` example +// for use by mutable-shader-object gfx unit test. + +// This file implements a simple compute shader that transforms +// input floating point numbers stored in a `RWStructuredBuffer`. +// Specifically, for each number x from input buffer, compute +// f(x) and store the result back in the same buffer. + +// The compute shader supports multiple transformation functions, +// such add(x, c) which returns x+c, or mul(x, c) which returns x*c. +// This functions are implemented as types that conforms to the +// `ITransformer` interface. + +// The main entry point function takes a parameter of `ITransformer` +// type, and applies the transformation to numbers in the input +// buffer. By defining the shader parameter using interfaces, +// we enable the flexiblity to generate either specialized compute +// kernels that performs specific transformation or a general +// kernel that can perform any transformations encoded by the +// parameter at run-time, without changing any shader code or +// host-application logic for setting and preparing shader parameters. + +// Defines the transformer interface, which implements a single +// `transform` operation. +interface ITransformer +{ + float transform(float x); +} + +// Represents a transform function f(x) = x + c. +struct AddTransformer : ITransformer +{ + float c; + float transform(float x) { return x + c; } +}; + +// Represents a transform function f(x) = x * c. +struct MulTransformer : ITransformer +{ + float c; + float transform(float x) { return x * c; } +}; + +// Represents a composite function f(x) = f0(f1(x)); +struct CompositeTransformer : ITransformer +{ + ITransformer func0; + ITransformer func1; + float transform(float x) + { + return func0.transform(func1.transform(x)); + } +}; + +// Main entry-point. Applies the transformation encoded by `transformer` +// to all elements in `buffer`. +[shader("compute")] +[numthreads(4,1,1)] +void computeMain( + uint3 sv_dispatchThreadID : SV_DispatchThreadID, + uniform RWStructuredBuffer buffer, + uniform ITransformer transformer) +{ + var input = buffer[sv_dispatchThreadID.x]; + buffer[sv_dispatchThreadID.x] = transformer.transform(input); +} diff --git a/tools/gfx/command-writer.h b/tools/gfx/command-writer.h index adbb53d7a..04be1be86 100644 --- a/tools/gfx/command-writer.h +++ b/tools/gfx/command-writer.h @@ -3,6 +3,7 @@ #include "slang-gfx.h" #include "slang-com-ptr.h" #include "core/slang-basic.h" +#include "renderer-shared.h" namespace gfx { @@ -81,7 +82,7 @@ class CommandWriter { public: Slang::List m_commands; - Slang::List> m_objects; + Slang::List> m_objects; Slang::List m_data; bool m_hasWriteTimestamps = false; @@ -105,18 +106,16 @@ public: return offset; } - uint32_t encodeObject(ISlangUnknown* obj) + uint32_t encodeObject(Slang::RefObject* obj) { uint32_t offset = (uint32_t)m_objects.getCount(); - ComPtr ptr; - ptr = obj; - m_objects.add(ptr); + m_objects.add(obj); return offset; } template T* getObject(uint32_t offset) { - return static_cast(m_objects[offset].get()); + return static_cast(m_objects[offset].Ptr()); } template T* getData(uint32_t offset) @@ -126,19 +125,19 @@ public: void setPipelineState(IPipelineState* state) { - auto offset = encodeObject(state); + auto offset = encodeObject(static_cast(state)); m_commands.add(Command(CommandName::SetPipelineState, offset)); } void bindRootShaderObject(IShaderObject* object) { - auto rootOffset = encodeObject(object); + auto rootOffset = encodeObject(static_cast(object)); m_commands.add(Command(CommandName::BindRootShaderObject, rootOffset)); } void uploadBufferData(IBufferResource* buffer, size_t offset, size_t size, void* data) { - auto bufferOffset = encodeObject(buffer); + auto bufferOffset = encodeObject(static_cast(buffer)); auto dataOffset = encodeData(data, size); m_commands.add(Command( CommandName::UploadBufferData, @@ -155,8 +154,8 @@ public: size_t srcOffset, size_t size) { - auto dstBuffer = encodeObject(dst); - auto srcBuffer = encodeObject(src); + auto dstBuffer = encodeObject(static_cast(dst)); + auto srcBuffer = encodeObject(static_cast(src)); m_commands.add(Command( CommandName::CopyBuffer, dstBuffer, @@ -168,7 +167,7 @@ public: void setFramebuffer(IFramebuffer* frameBuffer) { - uint32_t framebufferOffset = encodeObject(frameBuffer); + uint32_t framebufferOffset = encodeObject(static_cast(frameBuffer)); m_commands.add(Command(CommandName::SetFramebuffer, framebufferOffset)); } @@ -205,7 +204,7 @@ public: uint32_t bufferOffset = 0; for (UInt i = 0; i < slotCount; i++) { - auto offset = encodeObject(buffers[i]); + auto offset = encodeObject(static_cast(buffers[i])); if (i == 0) bufferOffset = offset; } @@ -222,7 +221,7 @@ public: void setIndexBuffer(IBufferResource* buffer, Format indexFormat, UInt offset) { - auto bufferOffset = encodeObject(buffer); + auto bufferOffset = encodeObject(static_cast(buffer)); m_commands.add(Command( CommandName::SetIndexBuffer, bufferOffset, (uint32_t)indexFormat, (uint32_t)offset)); } @@ -254,7 +253,7 @@ public: void writeTimestamp(IQueryPool* pool, SlangInt index) { - auto poolOffset = encodeObject(pool); + auto poolOffset = encodeObject(static_cast(pool)); m_commands.add( Command(CommandName::WriteTimestamp, poolOffset, (uint32_t)index)); m_hasWriteTimestamps = true; diff --git a/tools/gfx/cpu/render-cpu.cpp b/tools/gfx/cpu/render-cpu.cpp index 56804f15e..c6662b851 100644 --- a/tools/gfx/cpu/render-cpu.cpp +++ b/tools/gfx/cpu/render-cpu.cpp @@ -11,7 +11,7 @@ #include "../immediate-renderer-base.h" #include "../slang-context.h" - +#include "../mutable-shader-object.h" #define SLANG_PRELUDE_NAMESPACE slang_prelude #include "prelude/slang-cpp-types.h" @@ -942,6 +942,9 @@ public: char* getDataBuffer() { return m_data.getBuffer(); } }; +class CPUMutableShaderObject : public MutableShaderObject +{}; + class CPUEntryPointShaderObject : public CPUShaderObject { public: @@ -951,12 +954,9 @@ public: class CPURootShaderObject : public CPUShaderObject { public: - // Override default reference counting behavior to disable lifetime management via ComPtr. - // Root objects are managed by command buffer and does not need to be freed by the user. - SLANG_NO_THROW uint32_t SLANG_MCALL addRef() override { return 1; } - SLANG_NO_THROW uint32_t SLANG_MCALL release() override { return 1; } + virtual SLANG_NO_THROW uint32_t SLANG_MCALL addRef() override { return 1; } + virtual SLANG_NO_THROW uint32_t SLANG_MCALL release() override { return 1; } -public: SlangResult init(IDevice* device, CPUProgramLayout* programLayout); CPUProgramLayout* getLayout() { return static_cast(m_layout.Ptr()); } @@ -1007,16 +1007,8 @@ public: } }; -class CPUQueryPool : public IQueryPool, public ComObject +class CPUQueryPool : public QueryPoolBase { -public: - SLANG_COM_OBJECT_IUNKNOWN_ALL; - IQueryPool* getInterface(const Guid& guid) - { - if (guid == GfxGUID::IID_ISlangUnknown || guid == GfxGUID::IID_IQueryPool) - return static_cast(this); - return nullptr; - } public: List m_queries; Result init(const IQueryPool::Desc& desc) @@ -1216,6 +1208,19 @@ public: return SLANG_OK; } + virtual Result createMutableShaderObject( + ShaderObjectLayoutBase* layout, + IShaderObject** outObject) override + { + auto cpuLayout = static_cast(layout); + + RefPtr result = new CPUMutableShaderObject(); + SLANG_RETURN_ON_FAIL(result->init(this, cpuLayout)); + returnComPtr(outObject, result); + + return SLANG_OK; + } + virtual Result createRootShaderObject(IShaderProgram* program, ShaderObjectBase** outObject) override { auto cpuProgram = static_cast(program); diff --git a/tools/gfx/cuda/render-cuda.cpp b/tools/gfx/cuda/render-cuda.cpp index fccc1e3f0..ed22495cb 100644 --- a/tools/gfx/cuda/render-cuda.cpp +++ b/tools/gfx/cuda/render-cuda.cpp @@ -12,6 +12,7 @@ #include "slang-com-helper.h" #include "../command-writer.h" #include "../renderer-shared.h" +#include "../mutable-shader-object.h" #include "../simple-transient-resource-heap.h" #include "../slang-context.h" @@ -659,6 +660,9 @@ public: } }; +class CUDAMutableShaderObject : public MutableShaderObject< CUDAMutableShaderObject, CUDAShaderObjectLayout> +{}; + class CUDAEntryPointShaderObject : public CUDAShaderObject { public: @@ -668,11 +672,8 @@ public: class CUDARootShaderObject : public CUDAShaderObject { public: - // Override default reference counting behavior to disable lifetime management. - // Root objects are managed by command buffer and does not need to be freed by the user. - SLANG_NO_THROW uint32_t SLANG_MCALL addRef() override { return 1; } - SLANG_NO_THROW uint32_t SLANG_MCALL release() override { return 1; } - + virtual SLANG_NO_THROW uint32_t SLANG_MCALL addRef() override { return 1; } + virtual SLANG_NO_THROW uint32_t SLANG_MCALL release() override { return 1; } public: List> entryPointObjects; virtual SLANG_NO_THROW Result SLANG_MCALL @@ -723,16 +724,8 @@ public: } }; -class CUDAQueryPool : public IQueryPool, public ComObject +class CUDAQueryPool : public QueryPoolBase { -public: - SLANG_COM_OBJECT_IUNKNOWN_ALL; - IQueryPool* getInterface(const Guid& guid) - { - if (guid == GfxGUID::IID_ISlangUnknown || guid == GfxGUID::IID_IQueryPool) - return static_cast(this); - return nullptr; - } public: // The event object for each query. Owned by the pool. List m_events; @@ -1001,7 +994,6 @@ public: ResourceState src, ResourceState dst) override { - assert(!"Unimplemented"); } virtual SLANG_NO_THROW void SLANG_MCALL bufferBarrier( @@ -1010,7 +1002,6 @@ public: ResourceState src, ResourceState dst) override { - assert(!"Unimplemented"); } virtual SLANG_NO_THROW void SLANG_MCALL @@ -1236,11 +1227,11 @@ public: switch (cmd.name) { case CommandName::SetPipelineState: - setPipelineState(commandBuffer->getObject(cmd.operands[0])); + setPipelineState(commandBuffer->getObject(cmd.operands[0])); break; case CommandName::BindRootShaderObject: bindRootShaderObject( - commandBuffer->getObject(cmd.operands[0])); + commandBuffer->getObject(cmd.operands[0])); break; case CommandName::DispatchCompute: dispatchCompute( @@ -1248,22 +1239,22 @@ public: break; case CommandName::CopyBuffer: copyBuffer( - commandBuffer->getObject(cmd.operands[0]), + commandBuffer->getObject(cmd.operands[0]), cmd.operands[1], - commandBuffer->getObject(cmd.operands[2]), + commandBuffer->getObject(cmd.operands[2]), cmd.operands[3], cmd.operands[4]); break; case CommandName::UploadBufferData: uploadBufferData( - commandBuffer->getObject(cmd.operands[0]), + commandBuffer->getObject(cmd.operands[0]), cmd.operands[1], cmd.operands[2], commandBuffer->getData(cmd.operands[3])); break; case CommandName::WriteTimestamp: writeTimestamp( - commandBuffer->getObject(cmd.operands[0]), + commandBuffer->getObject(cmd.operands[0]), (SlangInt)cmd.operands[1]); } } @@ -1816,6 +1807,16 @@ public: return SLANG_OK; } + virtual Result createMutableShaderObject( + ShaderObjectLayoutBase* layout, + IShaderObject** outObject) override + { + RefPtr result = new CUDAMutableShaderObject(); + SLANG_RETURN_ON_FAIL(result->init(this, dynamic_cast(layout))); + returnComPtr(outObject, result); + return SLANG_OK; + } + Result createRootShaderObject(IShaderProgram* program, ShaderObjectBase** outObject) { auto cudaProgram = dynamic_cast(program); diff --git a/tools/gfx/d3d11/render-d3d11.cpp b/tools/gfx/d3d11/render-d3d11.cpp index 8c1c6be1e..ec89c0879 100644 --- a/tools/gfx/d3d11/render-d3d11.cpp +++ b/tools/gfx/d3d11/render-d3d11.cpp @@ -10,6 +10,7 @@ #include "../d3d/d3d-util.h" #include "../d3d/d3d-swapchain.h" #include "../nvapi/nvapi-util.h" +#include "../mutable-shader-object.h" // In order to use the Slang API, we need to include its header @@ -108,6 +109,7 @@ public: ShaderObjectLayoutBase** outLayout) override; virtual Result createShaderObject(ShaderObjectLayoutBase* layout, IShaderObject** outObject) override; + virtual Result createMutableShaderObject(ShaderObjectLayoutBase* layout, IShaderObject** outObject) override; virtual Result createRootShaderObject(IShaderProgram* program, ShaderObjectBase** outObject) override; virtual void bindRootShaderObject(IShaderObject* shaderObject) override; @@ -229,16 +231,8 @@ protected: }; - class SamplerStateImpl : public ISamplerState, public ComObject + class SamplerStateImpl : public SamplerStateBase { - public: - SLANG_COM_OBJECT_IUNKNOWN_ALL - ISamplerState* getInterface(const Guid& guid) - { - if (guid == GfxGUID::IID_ISlangUnknown || guid == GfxGUID::IID_ISamplerState) - return static_cast(this); - return nullptr; - } public: ComPtr m_sampler; }; @@ -291,19 +285,8 @@ protected: IFramebufferLayout::AttachmentLayout m_depthStencil; }; - class FramebufferImpl - : public IFramebuffer - , public ComObject + class FramebufferImpl : public FramebufferBase { - public: - SLANG_COM_OBJECT_IUNKNOWN_ALL - IFramebuffer* getInterface(const Guid& guid) - { - if (guid == GfxGUID::IID_ISlangUnknown || guid == GfxGUID::IID_IFramebuffer) - return static_cast(this); - return nullptr; - } - public: ShortList, kMaxRTVs> renderTargetViews; ShortList d3dRenderTargetViews; @@ -367,16 +350,8 @@ protected: ComPtr m_layout; }; - class QueryPoolImpl : public IQueryPool, public ComObject + class QueryPoolImpl : public QueryPoolBase { - public: - SLANG_COM_OBJECT_IUNKNOWN_ALL; - IQueryPool* getInterface(const Guid& guid) - { - if (guid == GfxGUID::IID_ISlangUnknown || guid == GfxGUID::IID_IQueryPool) - return static_cast(this); - return nullptr; - } public: List> m_queries; RefPtr m_device; @@ -1290,6 +1265,8 @@ protected: memcpy(dest + offset, data, size); + m_isConstantBufferDirty = true; + return SLANG_OK; } @@ -1510,49 +1487,43 @@ protected: D3D11Device* device, ShaderObjectLayoutImpl* specializedLayout) { - // If we have already created a buffer to hold ordinary data, then we should - // simply re-use that buffer rather than re-create it. - // - // TODO: Simply re-using the buffer without any kind of validation checks - // means that we are assuming that users cannot or will not perform any `set` - // operations on a shader object once an operation has requested this buffer - // be created. We need to enforce that rule if we want to rely on it. - // - if (m_ordinaryDataBuffer) - return SLANG_OK; - auto specializedOrdinaryDataSize = specializedLayout->getTotalOrdinaryDataSize(); if (specializedOrdinaryDataSize == 0) return SLANG_OK; - // Once we have computed how large the buffer should be, we can allocate - // it using the existing public `IDevice` API. - // + // If we have already created a buffer to hold ordinary data, then we should + // simply re-use that buffer rather than re-create it. + if (!m_ordinaryDataBuffer) + { + ComPtr bufferResourcePtr; + IBufferResource::Desc bufferDesc = {}; + bufferDesc.type = IResource::Type::Buffer; + bufferDesc.sizeInBytes = specializedOrdinaryDataSize; + bufferDesc.defaultState = ResourceState::ConstantBuffer; + bufferDesc.allowedStates = + ResourceStateSet(ResourceState::ConstantBuffer, ResourceState::CopyDestination); + bufferDesc.cpuAccessFlags |= AccessFlag::Write; + SLANG_RETURN_ON_FAIL( + device->createBufferResource(bufferDesc, nullptr, bufferResourcePtr.writeRef())); + m_ordinaryDataBuffer = static_cast(bufferResourcePtr.get()); + } - ComPtr bufferResourcePtr; - IBufferResource::Desc bufferDesc = {}; - bufferDesc.type = IResource::Type::Buffer; - bufferDesc.sizeInBytes = specializedOrdinaryDataSize; - bufferDesc.defaultState = ResourceState::ConstantBuffer; - bufferDesc.allowedStates = - ResourceStateSet(ResourceState::ConstantBuffer, ResourceState::CopyDestination); - bufferDesc.cpuAccessFlags |= AccessFlag::Write; - SLANG_RETURN_ON_FAIL( - device->createBufferResource(bufferDesc, nullptr, bufferResourcePtr.writeRef())); - m_ordinaryDataBuffer = static_cast(bufferResourcePtr.get()); - - // Once the buffer is allocated, we can use `_writeOrdinaryData` to fill it in. - // - // Note that `_writeOrdinaryData` is potentially recursive in the case - // where this object contains interface/existential-type fields, so we - // don't need or want to inline it into this call site. - // + if (m_isConstantBufferDirty) + { + // Once the buffer is allocated, we can use `_writeOrdinaryData` to fill it in. + // + // Note that `_writeOrdinaryData` is potentially recursive in the case + // where this object contains interface/existential-type fields, so we + // don't need or want to inline it into this call site. + // - auto ordinaryData = device->map(m_ordinaryDataBuffer, gfx::MapFlavor::WriteDiscard); - auto result = _writeOrdinaryData(ordinaryData, specializedOrdinaryDataSize, specializedLayout); - device->unmap(m_ordinaryDataBuffer, 0, specializedOrdinaryDataSize); - - return result; + auto ordinaryData = device->map(m_ordinaryDataBuffer, gfx::MapFlavor::WriteDiscard); + auto result = _writeOrdinaryData(ordinaryData, specializedOrdinaryDataSize, specializedLayout); + device->unmap(m_ordinaryDataBuffer, 0, specializedOrdinaryDataSize); + m_isConstantBufferDirty = false; + return result; + } + return SLANG_OK; } /// Bind the buffer for ordinary/uniform data, if needed @@ -1776,6 +1747,8 @@ protected: /// Created on demand with `_createOrdinaryDataBufferIfNeeded()` RefPtr m_ordinaryDataBuffer; + bool m_isConstantBufferDirty = true; + /// Get the layout of this shader object with specialization arguments considered /// /// This operation should only be called after the shader object has been @@ -1814,16 +1787,20 @@ protected: RefPtr m_specializedLayout; }; + class MutableShaderObjectImpl + : public MutableShaderObject< + MutableShaderObjectImpl, + ShaderObjectLayoutImpl> + {}; + class RootShaderObjectImpl : public ShaderObjectImpl { typedef ShaderObjectImpl Super; public: - // Override default reference counting behavior to disable lifetime management via ComPtr. - // Root objects are managed by command buffer and does not need to be freed by the user. - SLANG_NO_THROW uint32_t SLANG_MCALL addRef() override { return 1; } - SLANG_NO_THROW uint32_t SLANG_MCALL release() override { return 1; } - public: + virtual SLANG_NO_THROW uint32_t SLANG_MCALL addRef() override { return 1; } + virtual SLANG_NO_THROW uint32_t SLANG_MCALL release() override { return 1; } + static Result create(IDevice* device, RootShaderObjectLayoutImpl* layout, RootShaderObjectImpl** outShaderObject) { RefPtr object = new RootShaderObjectImpl(); @@ -3567,6 +3544,19 @@ Result D3D11Device::createShaderObject(ShaderObjectLayoutBase* layout, IShaderOb return SLANG_OK; } +Result D3D11Device::createMutableShaderObject( + ShaderObjectLayoutBase* layout, + IShaderObject** outObject) +{ + auto layoutImpl = static_cast(layout); + + RefPtr result = new MutableShaderObjectImpl(); + SLANG_RETURN_ON_FAIL(result->init(this, layoutImpl)); + returnComPtr(outObject, result); + + return SLANG_OK; +} + Result D3D11Device::createRootShaderObject(IShaderProgram* program, ShaderObjectBase** outObject) { auto programImpl = static_cast(program); diff --git a/tools/gfx/d3d12/render-d3d12.cpp b/tools/gfx/d3d12/render-d3d12.cpp index 28c00d08e..4d5dfda80 100644 --- a/tools/gfx/d3d12/render-d3d12.cpp +++ b/tools/gfx/d3d12/render-d3d12.cpp @@ -8,6 +8,7 @@ #include "../transient-resource-heap-base.h" #include "../simple-render-pass-layout.h" #include "../d3d/d3d-swapchain.h" +#include "../mutable-shader-object.h" #include "core/slang-blob.h" #include "core/slang-basic.h" #include "core/slang-chunked-list.h" @@ -117,6 +118,8 @@ public: ShaderObjectLayoutBase** outLayout) override; virtual Result createShaderObject(ShaderObjectLayoutBase* layout, IShaderObject** outObject) override; + virtual Result createMutableShaderObject( + ShaderObjectLayoutBase* layout, IShaderObject** outObject) override; virtual SLANG_NO_THROW Result SLANG_MCALL createProgram(const IShaderProgram::Desc& desc, IShaderProgram** outProgram) override; @@ -248,17 +251,8 @@ public: } }; - class SamplerStateImpl : public ISamplerState, public ComObject + class SamplerStateImpl : public SamplerStateBase { - public: - SLANG_COM_OBJECT_IUNKNOWN_ALL - - ISamplerState* getInterface(const Guid& guid) - { - if (guid == GfxGUID::IID_ISlangUnknown || guid == GfxGUID::IID_ISamplerState) - return static_cast(this); - return nullptr; - } public: D3D12Descriptor m_descriptor; Slang::RefPtr m_allocator; @@ -292,19 +286,8 @@ public: IFramebufferLayout::AttachmentLayout m_depthStencil; }; - class FramebufferImpl - : public IFramebuffer - , public ComObject + class FramebufferImpl : public FramebufferBase { - public: - SLANG_COM_OBJECT_IUNKNOWN_ALL - IFramebuffer* getInterface(const Guid& guid) - { - if (guid == GfxGUID::IID_ISlangUnknown || guid == GfxGUID::IID_IFramebuffer) - return static_cast(this); - return nullptr; - } - public: ShortList> renderTargetViews; RefPtr depthStencilView; @@ -381,16 +364,8 @@ public: }; #endif - class QueryPoolImpl : public IQueryPool, public ComObject + class QueryPoolImpl : public QueryPoolBase { - public: - SLANG_COM_OBJECT_IUNKNOWN_ALL - IQueryPool* getInterface(const Guid& guid) - { - if (guid == GfxGUID::IID_ISlangUnknown || guid == GfxGUID::IID_IQueryPool) - return static_cast(this); - return nullptr; - } public: Result init(const IQueryPool::Desc& desc, D3D12Device* device); @@ -604,10 +579,10 @@ public: } class TransientResourceHeapImpl - : public TransientResourceHeapBase + : public TransientResourceHeapBaseImpl { private: - typedef TransientResourceHeapBase Super; + typedef TransientResourceHeapBaseImpl Super; public: ComPtr m_commandAllocator; List> m_d3dCommandListPool; @@ -1280,6 +1255,7 @@ public: uint32_t getResourceSlotCount() { return m_ownCounts.resource; } uint32_t getSamplerSlotCount() { return m_ownCounts.sampler; } Index getSubObjectSlotCount() { return m_subObjectCount; } + Index getSubObjectCount() { return m_subObjectCount; } uint32_t getTotalResourceDescriptorCount() { return m_totalCounts.resource; } uint32_t getTotalSamplerDescriptorCount() { return m_totalCounts.sampler; } @@ -2012,9 +1988,9 @@ public: class ShaderObjectImpl : public ShaderObjectBaseImpl< - ShaderObjectImpl, - ShaderObjectLayoutImpl, - SimpleShaderObjectData> + ShaderObjectImpl, + ShaderObjectLayoutImpl, + SimpleShaderObjectData> { public: static Result create( @@ -2070,6 +2046,8 @@ public: memcpy(dest + offset, data, size); + m_isConstantBufferDirty = true; + return SLANG_OK; } @@ -2145,7 +2123,9 @@ public: m_layout = layout; - m_upToDateConstantBufferHeapVersion = 0; + m_constantBufferTransientHeap = nullptr; + m_constantBufferTransientHeapVersion = 0; + m_isConstantBufferDirty = true; // If the layout tells us that there is any uniform data, // then we will allocate a CPU memory buffer to hold that data @@ -2172,7 +2152,7 @@ public: // but does *not* include any descriptors that are managed // as part of sub-objects. // - if(auto resourceCount = layout->getResourceSlotCount()) + if (auto resourceCount = layout->getResourceSlotCount()) { m_descriptorSet.resourceTable.allocate(viewHeap, resourceCount); @@ -2182,7 +2162,7 @@ public: // m_boundResources.setCount(resourceCount); } - if(auto samplerCount = layout->getSamplerSlotCount()) + if (auto samplerCount = layout->getSamplerSlotCount()) { m_descriptorSet.samplerTable.allocate(samplerHeap, samplerCount); } @@ -2331,24 +2311,27 @@ public: return SLANG_OK; } + bool shouldAllocateConstantBuffer(TransientResourceHeapImpl* transientHeap) + { + return m_isConstantBufferDirty || m_constantBufferTransientHeap != transientHeap || + m_constantBufferTransientHeapVersion != transientHeap->getVersion(); + } + /// Ensure that the `m_ordinaryDataBuffer` has been created, if it is needed Result _ensureOrdinaryDataBufferCreatedIfNeeded( PipelineCommandEncoder* encoder, ShaderObjectLayoutImpl* specializedLayout) { - // If we have already created a buffer to hold ordinary data, then we should - // simply re-use that buffer rather than re-create it. - // - // TODO: Simply re-using the buffer without any kind of validation checks - // means that we are assuming that users cannot or will not perform any `set` - // operations on a shader object once an operation has requested this buffer - // be created. We need to enforce that rule if we want to rely on it. + // If data has been changed since last allocation/filling of constant buffer, + // we will need to allocate a new one. // - if (m_upToDateConstantBufferHeapVersion == - encoder->m_commandBuffer->m_transientHeap->getVersion()) + if (!shouldAllocateConstantBuffer(encoder->m_transientHeap)) { return SLANG_OK; } + m_isConstantBufferDirty = false; + m_constantBufferTransientHeap = encoder->m_transientHeap; + m_constantBufferTransientHeapVersion = encoder->m_transientHeap->getVersion(); // Computing the size of the ordinary data buffer is *not* just as simple // as using the size of the `m_ordinayData` array that we store. The reason @@ -2360,8 +2343,6 @@ public: m_constantBufferSize = specializedLayout->getTotalOrdinaryDataSize(); if (m_constantBufferSize == 0) { - m_upToDateConstantBufferHeapVersion = - encoder->m_commandBuffer->m_transientHeap->getVersion(); return SLANG_OK; } @@ -2385,11 +2366,6 @@ public: m_constantBufferSize, specializedLayout)); - // Update version tracker so that we don't redundantly alloc and fill in - // constant buffers for the same transient heap. - m_upToDateConstantBufferHeapVersion = - encoder->m_commandBuffer->m_transientHeap->getVersion(); - { // We also create and store a descriptor for our root constant buffer // into the descriptor table allocation that was reserved for them. @@ -2689,12 +2665,12 @@ public: size_t m_constantBufferOffset = 0; size_t m_constantBufferSize = 0; - /// The version number of the transient resource heap that contains up-to-date - /// constant buffer content for this shader object. If this is equal to the version number - /// of currently active transient heap, then the current set-up of constant buffer contents - /// as defined by the above `m_constantBuffer*` fields is valid and up-to-date so we can - /// use them directly. - uint64_t m_upToDateConstantBufferHeapVersion; + /// Dirty bit tracking whether the constant buffer needs to be updated. + bool m_isConstantBufferDirty = true; + /// The transient heap from which the constant buffer is allocated. + TransientResourceHeapImpl* m_constantBufferTransientHeap; + /// The version of the transient heap when the constant buffer is allocated. + uint64_t m_constantBufferTransientHeapVersion; /// Get the layout of this shader object with specialization arguments considered /// @@ -2734,6 +2710,9 @@ public: RefPtr m_specializedLayout; }; + class MutableShaderObjectImpl : public MutableShaderObject + {}; + class RootShaderObjectImpl : public ShaderObjectImpl { typedef ShaderObjectImpl Super; @@ -3455,7 +3434,6 @@ public: ResourceState src, ResourceState dst) override { - assert(!"Unimplemented"); } virtual SLANG_NO_THROW void SLANG_MCALL endEncoding() {} virtual SLANG_NO_THROW void SLANG_MCALL writeTimestamp(IQueryPool* pool, SlangInt index) override @@ -5380,6 +5358,19 @@ Result D3D12Device::createShaderObject( return SLANG_OK; } +Result D3D12Device::createMutableShaderObject( + ShaderObjectLayoutBase* layout, + IShaderObject** outObject) +{ + auto layoutImpl = static_cast(layout); + + RefPtr result = new MutableShaderObjectImpl(); + SLANG_RETURN_ON_FAIL(result->init(this, layoutImpl)); + returnComPtr(outObject, result); + + return SLANG_OK; +} + Result D3D12Device::createGraphicsPipelineState(const GraphicsPipelineStateDesc& inDesc, IPipelineState** outState) { GraphicsPipelineStateDesc desc = inDesc; diff --git a/tools/gfx/debug-layer.cpp b/tools/gfx/debug-layer.cpp index bab564f14..5f5b0505e 100644 --- a/tools/gfx/debug-layer.cpp +++ b/tools/gfx/debug-layer.cpp @@ -563,6 +563,26 @@ Result DebugDevice::createShaderObject( return result; } +Result DebugDevice::createMutableShaderObject( + slang::TypeReflection* type, + ShaderObjectContainerType containerType, + IShaderObject** outShaderObject) +{ + SLANG_GFX_API_FUNC; + + RefPtr outObject = new DebugShaderObject(); + auto typeName = type->getName(); + auto result = + baseObject->createMutableShaderObject(type, containerType, outObject->baseObject.writeRef()); + outObject->m_typeName = typeName; + outObject->m_device = this; + outObject->m_slangType = type; + if (SLANG_FAILED(result)) + return result; + returnComPtr(outShaderObject, outObject); + return result; +} + Result DebugDevice::createProgram(const IShaderProgram::Desc& desc, IShaderProgram** outProgram) { SLANG_GFX_API_FUNC; @@ -863,8 +883,10 @@ Result DebugComputeCommandEncoder::bindPipeline( SLANG_GFX_API_FUNC; auto innerState = static_cast(state)->baseObject; - auto result = - baseObject->bindPipeline(innerState, commandBuffer->rootObject.baseObject.writeRef()); + IShaderObject* innerRootObject = nullptr; + commandBuffer->rootObject.reset(); + auto result = baseObject->bindPipeline(innerState, &innerRootObject); + commandBuffer->rootObject.baseObject.attach(innerRootObject); *outRootShaderObject = &commandBuffer->rootObject; return result; } @@ -895,8 +917,10 @@ Result DebugRenderCommandEncoder::bindPipeline( SLANG_GFX_API_FUNC; auto innerState = static_cast(state)->baseObject; - auto result = - baseObject->bindPipeline(innerState, commandBuffer->rootObject.baseObject.writeRef()); + IShaderObject* innerRootObject = nullptr; + commandBuffer->rootObject.reset(); + auto result = baseObject->bindPipeline(innerState, &innerRootObject); + commandBuffer->rootObject.baseObject.attach(innerRootObject); *outRootShaderObject = &commandBuffer->rootObject; return result; } @@ -1142,7 +1166,10 @@ void DebugRayTracingCommandEncoder::bindPipeline( { SLANG_GFX_API_FUNC; auto innerPipeline = getInnerObj(state); - baseObject->bindPipeline(innerPipeline, commandBuffer->rootObject.baseObject.writeRef()); + IShaderObject* innerRootObject = nullptr; + commandBuffer->rootObject.reset(); + baseObject->bindPipeline(innerPipeline, &innerRootObject); + commandBuffer->rootObject.baseObject.attach(innerRootObject); *outRootObject = &commandBuffer->rootObject; } @@ -1393,10 +1420,21 @@ Result DebugShaderObject::setSpecializationArgs( const slang::SpecializationArg* args, uint32_t count) { - return baseObject->setSpecializationArgs(offset, args, count); } +Result DebugShaderObject::getCurrentVersion( + ITransientResourceHeap* transientHeap, IShaderObject** outObject) +{ + ComPtr innerObject; + SLANG_RETURN_ON_FAIL(baseObject->getCurrentVersion(getInnerObj(transientHeap), innerObject.writeRef())); + RefPtr debugShaderObject = new DebugShaderObject(); + debugShaderObject->baseObject = innerObject; + debugShaderObject->m_typeName = innerObject->getElementTypeLayout()->getName(); + returnComPtr(outObject, debugShaderObject); + return SLANG_OK; +} + DebugObjectBase::DebugObjectBase() { static uint64_t uidCounter = 0; @@ -1413,6 +1451,15 @@ Result DebugRootShaderObject::setSpecializationArgs( return baseObject->setSpecializationArgs(offset, args, count); } +void DebugRootShaderObject::reset() +{ + m_entryPoints.clear(); + m_objects.Clear(); + m_resources.Clear(); + m_samplers.Clear(); + baseObject.detach(); +} + Result DebugQueryPool::getResult(SlangInt index, SlangInt count, uint64_t* data) { SLANG_GFX_API_FUNC; diff --git a/tools/gfx/debug-layer.h b/tools/gfx/debug-layer.h index d25fbd453..aa56e5530 100644 --- a/tools/gfx/debug-layer.h +++ b/tools/gfx/debug-layer.h @@ -95,6 +95,10 @@ public: slang::TypeReflection* type, ShaderObjectContainerType container, IShaderObject** outObject) override; + virtual SLANG_NO_THROW Result SLANG_MCALL createMutableShaderObject( + slang::TypeReflection* type, + ShaderObjectContainerType container, + IShaderObject** outObject) override; virtual SLANG_NO_THROW Result SLANG_MCALL createProgram(const IShaderProgram::Desc& desc, IShaderProgram** outProgram) override; virtual SLANG_NO_THROW Result SLANG_MCALL createGraphicsPipelineState( @@ -189,6 +193,25 @@ public: ISamplerState* getInterface(const Slang::Guid& guid); }; +struct ShaderOffsetKey +{ + ShaderOffset offset; + bool operator==(ShaderOffsetKey other) + { + return offset.bindingArrayIndex == other.offset.bindingArrayIndex && + offset.bindingRangeIndex == other.offset.bindingRangeIndex && + offset.uniformOffset == other.offset.uniformOffset; + } + Slang::HashCode getHashCode() + { + return Slang::combineHash( + (Slang::HashCode)offset.uniformOffset, + Slang::combineHash( + (Slang::HashCode)offset.bindingArrayIndex, + (Slang::HashCode)offset.bindingRangeIndex)); + } +}; + class DebugShaderObject : public DebugObject { public: @@ -220,25 +243,10 @@ public: const slang::SpecializationArg* args, uint32_t count) override; + virtual SLANG_NO_THROW Result SLANG_MCALL getCurrentVersion( + ITransientResourceHeap* transientHeap, IShaderObject** outObject) override; + public: - struct ShaderOffsetKey - { - ShaderOffset offset; - bool operator==(ShaderOffsetKey other) - { - return offset.bindingArrayIndex == other.offset.bindingArrayIndex && - offset.bindingRangeIndex == other.offset.bindingRangeIndex && - offset.uniformOffset == other.offset.uniformOffset; - } - Slang::HashCode getHashCode() - { - return Slang::combineHash( - (Slang::HashCode)offset.uniformOffset, - Slang::combineHash( - (Slang::HashCode)offset.bindingArrayIndex, - (Slang::HashCode)offset.bindingRangeIndex)); - } - }; Slang::String m_typeName; slang::TypeReflection* m_slangType = nullptr; DebugDevice* m_device; @@ -257,6 +265,7 @@ public: ShaderOffset const& offset, const slang::SpecializationArg* args, uint32_t count) override; + void reset(); }; class DebugCommandBuffer; diff --git a/tools/gfx/immediate-renderer-base.cpp b/tools/gfx/immediate-renderer-base.cpp index eb5450746..8e2e6be0c 100644 --- a/tools/gfx/immediate-renderer-base.cpp +++ b/tools/gfx/immediate-renderer-base.cpp @@ -255,7 +255,6 @@ public: ResourceState src, ResourceState dst) override { - assert(!"Unimplemented"); } virtual SLANG_NO_THROW void SLANG_MCALL bufferBarrier( @@ -264,7 +263,6 @@ public: ResourceState src, ResourceState dst) override { - assert(!"Unimplemented"); } }; @@ -300,13 +298,13 @@ public: switch (name) { case CommandName::SetPipelineState: - m_renderer->setPipelineState(m_writer.getObject(cmd.operands[0])); + m_renderer->setPipelineState(m_writer.getObject(cmd.operands[0])); break; case CommandName::BindRootShaderObject: - m_renderer->bindRootShaderObject(m_writer.getObject(cmd.operands[0])); + m_renderer->bindRootShaderObject(m_writer.getObject(cmd.operands[0])); break; case CommandName::SetFramebuffer: - m_renderer->setFramebuffer(m_writer.getObject(cmd.operands[0])); + m_renderer->setFramebuffer(m_writer.getObject(cmd.operands[0])); break; case CommandName::ClearFrame: m_renderer->clearFrame( @@ -329,7 +327,7 @@ public: for (uint32_t i = 0; i < cmd.operands[1]; i++) { bufferResources.add( - m_writer.getObject(cmd.operands[2] + i)); + m_writer.getObject(cmd.operands[2] + i)); } m_renderer->setVertexBuffers( (UInt)cmd.operands[0], @@ -341,7 +339,7 @@ public: break; case CommandName::SetIndexBuffer: m_renderer->setIndexBuffer( - m_writer.getObject(cmd.operands[0]), + m_writer.getObject(cmd.operands[0]), (Format)cmd.operands[1], (UInt)cmd.operands[2]); break; @@ -361,21 +359,21 @@ public: break; case CommandName::UploadBufferData: m_renderer->uploadBufferData( - m_writer.getObject(cmd.operands[0]), + m_writer.getObject(cmd.operands[0]), cmd.operands[1], cmd.operands[2], m_writer.getData(cmd.operands[3])); break; case CommandName::CopyBuffer: m_renderer->copyBuffer( - m_writer.getObject(cmd.operands[0]), + m_writer.getObject(cmd.operands[0]), cmd.operands[1], - m_writer.getObject(cmd.operands[2]), + m_writer.getObject(cmd.operands[2]), cmd.operands[3], cmd.operands[4]); break; case CommandName::WriteTimestamp: - m_renderer->writeTimestamp(m_writer.getObject(cmd.operands[0]), (SlangInt)cmd.operands[1]); + m_renderer->writeTimestamp(m_writer.getObject(cmd.operands[0]), (SlangInt)cmd.operands[1]); break; default: assert(!"unknown command"); diff --git a/tools/gfx/mutable-shader-object.h b/tools/gfx/mutable-shader-object.h new file mode 100644 index 000000000..17c025580 --- /dev/null +++ b/tools/gfx/mutable-shader-object.h @@ -0,0 +1,215 @@ +#pragma once + +#include "slang-gfx.h" +#include "core/slang-basic.h" +#include "core/slang-com-object.h" +#include "renderer-shared.h" + +namespace gfx +{ + class ShaderObjectLayoutBase; + + template + class VersionedObjectPool + { + public: + struct ObjectVersion + { + Slang::RefPtr object; + Slang::RefPtr transientHeap; + uint64_t transientHeapVersion; + bool canRecycle() + { + return (transientHeap->getVersion() != transientHeapVersion); + } + }; + Slang::List objects; + SlangInt lastAllocationIndex = -1; + ObjectVersion& allocate(TransientResourceHeapBase* currentTransientHeap) + { + for (SlangInt i = 0; i < objects.getCount(); i++) + { + auto& object = objects[i]; + if (object.canRecycle()) + { + object.transientHeap = currentTransientHeap; + object.transientHeapVersion = currentTransientHeap->getVersion(); + lastAllocationIndex = i; + return object; + } + } + ObjectVersion v; + v.transientHeap = currentTransientHeap; + v.transientHeapVersion = currentTransientHeap->getVersion(); + objects.add(v); + lastAllocationIndex = objects.getCount() - 1; + return objects.getLast(); + } + ObjectVersion& getLastAllocation() { return objects[lastAllocationIndex]; } + }; + + class MutableShaderObjectData + { + public: + // Any "ordinary" / uniform data for this object + Slang::List m_ordinaryData; + + bool m_dirty = true; + + Slang::Index getCount() { return m_ordinaryData.getCount(); } + void setCount(Slang::Index count) { m_ordinaryData.setCount(count); } + char* getBuffer() { return m_ordinaryData.getBuffer(); } + void markDirty() { m_dirty = true; } + + // We don't actually create any GPU buffers here, since they will be handled + // by the immutable shader objects once the user calls `getCurrentVersion`. + ResourceViewBase* getResourceView( + RendererBase* device, + slang::TypeLayoutReflection* elementLayout, + slang::BindingType bindingType) + { + return nullptr; + } + }; + + template + class MutableShaderObject : public ShaderObjectBaseImpl< + TShaderObject, + TShaderObjectLayoutImpl, + MutableShaderObjectData> + { + typedef ShaderObjectBaseImpl< + TShaderObject, + TShaderObjectLayoutImpl, + MutableShaderObjectData> Super; + protected: + Slang::OrderedDictionary> m_resources; + Slang::OrderedDictionary> m_samplers; + Slang::OrderedHashSet m_objectOffsets; + VersionedObjectPool m_shaderObjectVersions; + bool m_dirty = true; + bool isDirty() + { + if (m_dirty) return true; + if (this->m_data.m_dirty) return true; + for (auto& object : this->m_objects) + { + if (object && object->isDirty()) + return true; + } + return false; + } + + void markDirty() + { + m_dirty = true; + } + public: + Result init(RendererBase* device, ShaderObjectLayoutBase* layout) + { + this->m_device = device; + auto layoutImpl = static_cast(layout); + this->m_layout = layoutImpl; + Slang::Index subObjectCount = layoutImpl->getSubObjectCount(); + this->m_objects.setCount(subObjectCount); + return SLANG_OK; + } + public: + virtual SLANG_NO_THROW Result SLANG_MCALL setData(ShaderOffset const& offset, void const* data, size_t size) override + { + if (!size) return SLANG_OK; + if (SlangInt(offset.uniformOffset + size) > this->m_data.getCount()) + this->m_data.setCount(offset.uniformOffset + size); + memcpy(this->m_data.getBuffer() + offset.uniformOffset, data, size); + this->m_data.markDirty(); + markDirty(); + return SLANG_OK; + } + + virtual SLANG_NO_THROW Result SLANG_MCALL + setObject(ShaderOffset const& offset, IShaderObject* object) override + { + Super::setObject(offset, object); + m_objectOffsets.Add(offset); + markDirty(); + return SLANG_OK; + } + + virtual SLANG_NO_THROW Result SLANG_MCALL setResource(ShaderOffset const& offset, IResourceView* resourceView) override + { + m_resources[offset] = static_cast(resourceView); + markDirty(); + return SLANG_OK; + } + + virtual SLANG_NO_THROW Result SLANG_MCALL setSampler(ShaderOffset const& offset, ISamplerState* sampler) override + { + m_samplers[offset] = static_cast(sampler); + markDirty(); + return SLANG_OK; + } + + virtual SLANG_NO_THROW Result SLANG_MCALL setCombinedTextureSampler(ShaderOffset const& offset, IResourceView* textureView, ISamplerState* sampler) override + { + m_samplers[offset] = static_cast(sampler); + m_resources[offset] = static_cast(textureView); + markDirty(); + return SLANG_OK; + } + + virtual SLANG_NO_THROW Result SLANG_MCALL getCurrentVersion( + ITransientResourceHeap* transientHeap, IShaderObject** outObject) override + { + if (!isDirty()) + { + returnComPtr(outObject, getLastAllocatedShaderObject()); + return SLANG_OK; + } + + Slang::RefPtr object = + allocateShaderObject(static_cast(transientHeap)); + SLANG_RETURN_ON_FAIL(object->setData(ShaderOffset(), this->m_data.getBuffer(), this->m_data.getCount())); + for (auto res : m_resources) + SLANG_RETURN_ON_FAIL(object->setResource(res.Key, res.Value)); + for (auto sampler : m_samplers) + SLANG_RETURN_ON_FAIL(object->setSampler(sampler.Key, sampler.Value)); + for (auto offset : m_objectOffsets) + { + if (offset.bindingRangeIndex < 0) + return SLANG_E_INVALID_ARG; + auto layout = this->getLayout(); + if (offset.bindingRangeIndex >= layout->getBindingRangeCount()) + return SLANG_E_INVALID_ARG; + auto bindingRange = layout->getBindingRange(offset.bindingRangeIndex); + + auto subObject = this->m_objects[bindingRange.subObjectIndex + offset.bindingArrayIndex]; + if (subObject) + { + ComPtr subObjectVersion; + SLANG_RETURN_ON_FAIL(subObject->getCurrentVersion(transientHeap, subObjectVersion.writeRef())); + SLANG_RETURN_ON_FAIL(object->setObject(offset, subObjectVersion)); + } + } + m_dirty = false; + this->m_data.m_dirty = false; + returnComPtr(outObject, object); + return SLANG_OK; + } + public: + Slang::RefPtr allocateShaderObject(TransientResourceHeapBase* transientHeap) + { + auto& version = m_shaderObjectVersions.allocate(transientHeap); + if (!version.object) + { + ComPtr shaderObject; + SLANG_RETURN_NULL_ON_FAIL(this->m_device->createShaderObject(this->m_layout, shaderObject.writeRef())); + version.object = static_cast(shaderObject.get()); + } + return version.object; + } + Slang::RefPtr getLastAllocatedShaderObject() + { + return m_shaderObjectVersions.getLastAllocation().object; + } + }; +} diff --git a/tools/gfx/open-gl/render-gl.cpp b/tools/gfx/open-gl/render-gl.cpp index 9ad24146d..a5d5ae368 100644 --- a/tools/gfx/open-gl/render-gl.cpp +++ b/tools/gfx/open-gl/render-gl.cpp @@ -4,6 +4,7 @@ #include "../nvapi/nvapi-util.h" #include "../immediate-renderer-base.h" +#include "../mutable-shader-object.h" #include "core/slang-basic.h" #include "core/slang-blob.h" @@ -140,6 +141,7 @@ public: slang::TypeLayoutReflection* typeLayout, ShaderObjectLayoutBase** outLayout) override; virtual Result createShaderObject(ShaderObjectLayoutBase* layout, IShaderObject** outObject) override; + virtual Result createMutableShaderObject(ShaderObjectLayoutBase* layout, IShaderObject** outObject) override; virtual Result createRootShaderObject(IShaderProgram* program, ShaderObjectBase** outObject) override; virtual void bindRootShaderObject(IShaderObject* shaderObject) override; @@ -283,16 +285,8 @@ public: GLuint m_handle; }; - class SamplerStateImpl : public ISamplerState, public ComObject + class SamplerStateImpl : public SamplerStateBase { - public: - SLANG_COM_OBJECT_IUNKNOWN_ALL - ISamplerState* getInterface(const Guid& guid) - { - if (guid == GfxGUID::IID_ISlangUnknown || guid == GfxGUID::IID_ISamplerState) - return static_cast(this); - return nullptr; - } public: GLuint m_samplerID; }; @@ -340,19 +334,8 @@ public: IFramebufferLayout::AttachmentLayout m_depthStencil; }; - class FramebufferImpl - : public IFramebuffer - , public ComObject + class FramebufferImpl : public FramebufferBase { - public: - SLANG_COM_OBJECT_IUNKNOWN_ALL - IFramebuffer* getInterface(const Guid& guid) - { - if (guid == GfxGUID::IID_ISlangUnknown || guid == GfxGUID::IID_IFramebuffer) - return static_cast(this); - return nullptr; - } - public: GLuint m_framebuffer; ShortList m_drawBuffers; @@ -1394,16 +1377,15 @@ public: RefPtr m_specializedLayout; }; + class MutableShaderObjectImpl : public MutableShaderObject + {}; + class RootShaderObjectImpl : public ShaderObjectImpl { typedef ShaderObjectImpl Super; - public: - // Override default reference counting behavior to disable lifetime management via ComPtr. - // Root objects are managed by command buffer and does not need to be freed by the user. - SLANG_NO_THROW uint32_t SLANG_MCALL addRef() override { return 1; } - SLANG_NO_THROW uint32_t SLANG_MCALL release() override { return 1; } - + virtual SLANG_NO_THROW uint32_t SLANG_MCALL addRef() override { return 1; } + virtual SLANG_NO_THROW uint32_t SLANG_MCALL release() override { return 1; } public: static Result create(IDevice* device, RootShaderObjectLayoutImpl* layout, RootShaderObjectImpl** outShaderObject) { @@ -2845,6 +2827,17 @@ Result GLDevice::createShaderObject(ShaderObjectLayoutBase* layout, IShaderObjec return SLANG_OK; } +Result GLDevice::createMutableShaderObject(ShaderObjectLayoutBase* layout, IShaderObject** outObject) +{ + auto layoutImpl = static_cast(layout); + + RefPtr result = new MutableShaderObjectImpl(); + SLANG_RETURN_ON_FAIL(result->init(this, layoutImpl)); + returnComPtr(outObject, result); + + return SLANG_OK; +} + Result GLDevice::createRootShaderObject(IShaderProgram* program, ShaderObjectBase** outObject) { auto programImpl = static_cast(program); diff --git a/tools/gfx/renderer-shared.cpp b/tools/gfx/renderer-shared.cpp index c5a0c826d..791cb1859 100644 --- a/tools/gfx/renderer-shared.cpp +++ b/tools/gfx/renderer-shared.cpp @@ -1,4 +1,5 @@ #include "renderer-shared.h" +#include "mutable-shader-object.h" #include "core/slang-io.h" #include "core/slang-token-reader.h" @@ -128,22 +129,22 @@ IResourceView* ResourceViewBase::getInterface(const Guid& guid) return nullptr; } -IAccelerationStructure* AccelerationStructureBase::getInterface(const Slang::Guid& guid) +ISamplerState* SamplerStateBase::getInterface(const Slang::Guid& guid) { - if (guid == GfxGUID::IID_ISlangUnknown || guid == GfxGUID::IID_IResourceView || - guid == GfxGUID::IID_IAccelerationStructure) - return static_cast(this); + if (guid == GfxGUID::IID_ISlangUnknown || guid == GfxGUID::IID_ISamplerState) + return static_cast(this); return nullptr; } -IShaderObject* ShaderObjectBase::getInterface(const Guid& guid) +IAccelerationStructure* AccelerationStructureBase::getInterface(const Slang::Guid& guid) { - if (guid == GfxGUID::IID_ISlangUnknown || guid == GfxGUID::IID_IShaderObject) - return static_cast(this); + if (guid == GfxGUID::IID_ISlangUnknown || guid == GfxGUID::IID_IResourceView || + guid == GfxGUID::IID_IAccelerationStructure) + return static_cast(this); return nullptr; } -bool ShaderObjectBase::_doesValueFitInExistentialPayload( +bool _doesValueFitInExistentialPayload( slang::TypeLayoutReflection* concreteTypeLayout, slang::TypeLayoutReflection* existentialTypeLayout) { @@ -223,6 +224,20 @@ IFramebufferLayout* FramebufferLayoutBase::getInterface(const Guid& guid) return nullptr; } +IFramebuffer* FramebufferBase::getInterface(const Guid& guid) +{ + if (guid == GfxGUID::IID_ISlangUnknown || guid == GfxGUID::IID_IFramebuffer) + return static_cast(this); + return nullptr; +} + +IQueryPool* QueryPoolBase::getInterface(const Guid& guid) +{ + if (guid == GfxGUID::IID_ISlangUnknown || guid == GfxGUID::IID_IQueryPool) + return static_cast(this); + return nullptr; +} + IPipelineState* gfx::PipelineStateBase::getInterface(const Guid& guid) { if (guid == GfxGUID::IID_ISlangUnknown || guid == GfxGUID::IID_IPipelineState) @@ -298,10 +313,20 @@ SLANG_NO_THROW Result SLANG_MCALL RendererBase::createShaderObject( IShaderObject** outObject) { RefPtr shaderObjectLayout; - SLANG_RETURN_FALSE_ON_FAIL(getShaderObjectLayout(type, container, shaderObjectLayout.writeRef())); + SLANG_RETURN_ON_FAIL(getShaderObjectLayout(type, container, shaderObjectLayout.writeRef())); return createShaderObject(shaderObjectLayout, outObject); } +SLANG_NO_THROW Result SLANG_MCALL RendererBase::createMutableShaderObject( + slang::TypeReflection* type, + ShaderObjectContainerType containerType, + IShaderObject** outObject) +{ + RefPtr shaderObjectLayout; + SLANG_RETURN_ON_FAIL(getShaderObjectLayout(type, containerType, shaderObjectLayout.writeRef())); + return createMutableShaderObject(shaderObjectLayout, outObject); +} + Result RendererBase::getAccelerationStructurePrebuildInfo( const IAccelerationStructure::BuildInputs& buildInputs, IAccelerationStructure::PrebuildInfo* outPrebuildInfo) @@ -452,7 +477,7 @@ Result ShaderObjectBase::_getSpecializedShaderObjectType(ExtendedShaderObjectTyp else { shaderObjectType.slangType = getRenderer()->slangContext.session->specializeType( - getElementTypeLayout()->getType(), + _getElementTypeLayout()->getType(), specializationArgs.components.getArrayView().getBuffer(), specializationArgs.getCount()); shaderObjectType.componentID = getRenderer()->shaderCache.getComponentId(shaderObjectType.slangType); } diff --git a/tools/gfx/renderer-shared.h b/tools/gfx/renderer-shared.h index 5df3ee92b..51db601c9 100644 --- a/tools/gfx/renderer-shared.h +++ b/tools/gfx/renderer-shared.h @@ -261,6 +261,13 @@ public: virtual SLANG_NO_THROW Desc* SLANG_MCALL getViewDesc() override { return &m_desc; } }; +class SamplerStateBase : public ISamplerState, public Slang::ComObject +{ +public: + SLANG_COM_OBJECT_IUNKNOWN_ALL + ISamplerState* getInterface(const Slang::Guid& guid); +}; + class AccelerationStructureBase : public IAccelerationStructure , public ResourceViewInternalBase @@ -423,8 +430,20 @@ public: slang::BindingType bindingType); }; +bool _doesValueFitInExistentialPayload( + slang::TypeLayoutReflection* concreteTypeLayout, + slang::TypeLayoutReflection* existentialFieldLayout); + class ShaderObjectBase : public IShaderObject, public Slang::ComObject { +public: + SLANG_COM_OBJECT_IUNKNOWN_ALL + IShaderObject* getInterface(const Slang::Guid& guid) + { + if (guid == GfxGUID::IID_ISlangUnknown || guid == GfxGUID::IID_IShaderObject) + return static_cast(this); + return nullptr; + } protected: // A strong reference to `IDevice` to make sure the weak device reference in // `ShaderObjectLayout`s are valid whenever they might be used. @@ -436,17 +455,14 @@ protected: // The specialized shader object type. ExtendedShaderObjectType shaderObjectType = { nullptr, kInvalidComponentID }; - static bool _doesValueFitInExistentialPayload( - slang::TypeLayoutReflection* concreteTypeLayout, - slang::TypeLayoutReflection* existentialFieldLayout); Result _getSpecializedShaderObjectType(ExtendedShaderObjectType* outType); - + slang::TypeLayoutReflection* _getElementTypeLayout() + { + return m_layout->getElementTypeLayout(); + } public: - SLANG_COM_OBJECT_IUNKNOWN_ALL - IShaderObject* getInterface(const Slang::Guid& guid); void breakStrongReferenceToDevice() { m_device.breakStrongReference(); } - public: ShaderComponentID getComponentID() { @@ -461,6 +477,15 @@ public: RendererBase* getRenderer() { return m_layout->getDevice(); } + ShaderObjectLayoutBase* getLayoutBase() { return m_layout; } + + /// Sets the RTTI ID and RTTI witness table fields of an existential value. + Result setExistentialHeader( + slang::TypeReflection* existentialType, + slang::TypeReflection* concreteType, + ShaderOffset offset); + +public: SLANG_NO_THROW UInt SLANG_MCALL getEntryPointCount() SLANG_OVERRIDE { return 0; } SLANG_NO_THROW Result SLANG_MCALL getEntryPoint(UInt index, IShaderObject** outEntryPoint) @@ -470,8 +495,6 @@ public: return SLANG_OK; } - ShaderObjectLayoutBase* getLayoutBase() { return m_layout; } - SLANG_NO_THROW slang::TypeLayoutReflection* SLANG_MCALL getElementTypeLayout() SLANG_OVERRIDE { return m_layout->getElementTypeLayout(); @@ -482,11 +505,12 @@ public: return m_layout->getContainerType(); } - /// Sets the RTTI ID and RTTI witness table fields of an existential value. - Result setExistentialHeader( - slang::TypeReflection* existentialType, - slang::TypeReflection* concreteType, - ShaderOffset offset); + virtual SLANG_NO_THROW Result SLANG_MCALL getCurrentVersion( + ITransientResourceHeap* transientHeap, IShaderObject** outObject) override + { + SLANG_UNUSED(outObject); + return SLANG_E_NOT_AVAILABLE; + } }; template @@ -707,12 +731,12 @@ public: { // If we are setting into a `StructuredBuffer` field, make sure we create and set // the StructuredBuffer resource as well. - setResource( - offset, - subObject->m_data.getResourceView( - getRenderer(), - subObject->getElementTypeLayout(), - bindingRange.bindingType)); + auto resourceView = subObject->m_data.getResourceView( + getRenderer(), + subObject->getElementTypeLayout(), + bindingRange.bindingType); + if (resourceView) + setResource(offset, resourceView); } break; } @@ -921,6 +945,24 @@ public: IFramebufferLayout* getInterface(const Slang::Guid& guid); }; +class FramebufferBase + : public IFramebuffer + , public Slang::ComObject +{ +public: + SLANG_COM_OBJECT_IUNKNOWN_ALL + IFramebuffer* getInterface(const Slang::Guid& guid); +}; + +class QueryPoolBase + : public IQueryPool + , public Slang::ComObject +{ +public: + SLANG_COM_OBJECT_IUNKNOWN_ALL + IQueryPool* getInterface(const Slang::Guid& guid); +}; + class PipelineStateBase : public IPipelineState , public Slang::ComObject @@ -1073,6 +1115,30 @@ protected: Slang::OrderedDictionary> specializedPipelines; }; +class TransientResourceHeapBase : public ITransientResourceHeap, public Slang::ComObject +{ +public: + uint64_t m_version = 0; + uint64_t getVersion() { return m_version; } + uint64_t& getVersionCounter() + { + static uint64_t version = 1; + return version; + } + TransientResourceHeapBase() + { + m_version = getVersionCounter()++; + } +public: + SLANG_COM_OBJECT_IUNKNOWN_ALL + ITransientResourceHeap* getInterface(const Slang::Guid& guid) + { + if (guid == GfxGUID::IID_ISlangUnknown || guid == GfxGUID::IID_ITransientResourceHeap) + return static_cast(this); + return nullptr; + } +}; + // Renderer implementation shared by all platforms. // Responsible for shader compilation, specialization and caching. class RendererBase : public IDevice, public Slang::ComObject @@ -1093,6 +1159,11 @@ public: ShaderObjectContainerType containerType, IShaderObject** outObject) SLANG_OVERRIDE; + virtual SLANG_NO_THROW Result SLANG_MCALL createMutableShaderObject( + slang::TypeReflection* type, + ShaderObjectContainerType containerType, + IShaderObject** outObject) SLANG_OVERRIDE; + // Provides a default implementation that returns SLANG_E_NOT_AVAILABLE for platforms // without ray tracing support. virtual SLANG_NO_THROW Result SLANG_MCALL getAccelerationStructurePrebuildInfo( @@ -1134,6 +1205,9 @@ public: ShaderObjectLayoutBase* layout, IShaderObject** outObject) = 0; + virtual Result createMutableShaderObject( + ShaderObjectLayoutBase* layout, + IShaderObject** outObject) = 0; protected: virtual SLANG_NO_THROW SlangResult SLANG_MCALL initialize(const Desc& desc); diff --git a/tools/gfx/simple-transient-resource-heap.h b/tools/gfx/simple-transient-resource-heap.h index 4340d49df..c6980f94c 100644 --- a/tools/gfx/simple-transient-resource-heap.h +++ b/tools/gfx/simple-transient-resource-heap.h @@ -9,19 +9,8 @@ namespace gfx { template -class SimpleTransientResourceHeap - : public ITransientResourceHeap - , public Slang::ComObject +class SimpleTransientResourceHeap : public TransientResourceHeapBase { -public: - SLANG_COM_OBJECT_IUNKNOWN_ALL - ITransientResourceHeap* getInterface(const Slang::Guid& guid) - { - if (guid == GfxGUID::IID_ISlangUnknown || guid == GfxGUID::IID_ITransientResourceHeap) - return static_cast(this); - return nullptr; - } - public: Slang::RefPtr m_device; Slang::ComPtr m_constantBuffer; @@ -49,6 +38,10 @@ public: return SLANG_OK; } - virtual SLANG_NO_THROW Result SLANG_MCALL synchronizeAndReset() override { return SLANG_OK; } + virtual SLANG_NO_THROW Result SLANG_MCALL synchronizeAndReset() override + { + ++getVersionCounter(); + return SLANG_OK; + } }; } diff --git a/tools/gfx/transient-resource-heap-base.h b/tools/gfx/transient-resource-heap-base.h index f11a91c14..27f5e1bb9 100644 --- a/tools/gfx/transient-resource-heap-base.h +++ b/tools/gfx/transient-resource-heap-base.h @@ -4,18 +4,9 @@ namespace gfx { template -class TransientResourceHeapBase - : public ITransientResourceHeap - , public Slang::ComObject +class TransientResourceHeapBaseImpl : public TransientResourceHeapBase { public: - SLANG_COM_OBJECT_IUNKNOWN_ALL - ITransientResourceHeap* getInterface(const Slang::Guid& guid) - { - if (guid == GfxGUID::IID_ISlangUnknown || guid == GfxGUID::IID_ITransientResourceHeap) - return static_cast(this); - return nullptr; - } void breakStrongReferenceToDevice() { m_device.breakStrongReference(); } public: @@ -24,13 +15,6 @@ public: Slang::Index m_constantBufferAllocCounter = 0; size_t m_constantBufferOffsetAllocCounter = 0; uint32_t m_alignment = 256; - uint64_t m_version; - uint64_t getVersion() { return m_version; } - uint64_t& getVersionCounter() - { - static uint64_t version = 1; - return version; - } Result init(const ITransientResourceHeap::Desc& desc, uint32_t alignment, TDevice* device) { diff --git a/tools/gfx/vulkan/render-vk.cpp b/tools/gfx/vulkan/render-vk.cpp index 2dd5d4041..e7ebf8040 100644 --- a/tools/gfx/vulkan/render-vk.cpp +++ b/tools/gfx/vulkan/render-vk.cpp @@ -4,6 +4,7 @@ //WORKING:#include "options.h" #include "../renderer-shared.h" #include "../transient-resource-heap-base.h" +#include "../mutable-shader-object.h" #include "core/slang-basic.h" #include "core/slang-blob.h" @@ -95,6 +96,8 @@ public: ShaderObjectLayoutBase** outLayout) override; virtual Result createShaderObject(ShaderObjectLayoutBase* layout, IShaderObject** outObject) override; + virtual Result createMutableShaderObject(ShaderObjectLayoutBase* layout, IShaderObject** outObject) + override; virtual SLANG_NO_THROW Result SLANG_MCALL createProgram(const IShaderProgram::Desc& desc, IShaderProgram** outProgram) override; @@ -140,6 +143,7 @@ public: ~VKDevice(); public: + class TransientResourceHeapImpl; class Buffer { @@ -243,16 +247,8 @@ public: } }; - class SamplerStateImpl : public ISamplerState, public ComObject + class SamplerStateImpl : public SamplerStateBase { - public: - SLANG_COM_OBJECT_IUNKNOWN_ALL - ISamplerState* getInterface(const Guid& guid) - { - if (guid == GfxGUID::IID_ISlangUnknown || guid == GfxGUID::IID_ISamplerState) - return static_cast(this); - return nullptr; - } public: VkSampler m_sampler; RefPtr m_device; @@ -562,19 +558,8 @@ public: } }; - class FramebufferImpl - : public IFramebuffer - , public ComObject + class FramebufferImpl : public FramebufferBase { - public: - SLANG_COM_OBJECT_IUNKNOWN_ALL - IFramebuffer* getInterface(const Guid& guid) - { - if (guid == GfxGUID::IID_ISlangUnknown || guid == GfxGUID::IID_IFramebuffer) - return static_cast(this); - return nullptr; - } - public: VkFramebuffer m_handle; ShortList> renderTargetViews; @@ -2322,6 +2307,8 @@ public: memcpy(dest + offset, data, size); + m_isConstantBufferDirty = true; + return SLANG_OK; } @@ -2388,7 +2375,9 @@ public: { m_layout = layout; - m_upToDateConstantBufferHeapVersion = 0; + m_constantBufferTransientHeap = nullptr; + m_constantBufferTransientHeapVersion = 0; + m_isConstantBufferDirty = true; // If the layout tells us that there is any uniform data, // then we will allocate a CPU memory buffer to hold that data @@ -2812,30 +2801,31 @@ public: } } + bool shouldAllocateConstantBuffer(TransientResourceHeapImpl* transientHeap) + { + return m_isConstantBufferDirty || m_constantBufferTransientHeap != transientHeap || + m_constantBufferTransientHeapVersion != transientHeap->getVersion(); + } + /// Ensure that the `m_ordinaryDataBuffer` has been created, if it is needed Result _ensureOrdinaryDataBufferCreatedIfNeeded( PipelineCommandEncoder* encoder, ShaderObjectLayoutImpl* specializedLayout) { - // If we have already created a buffer to hold ordinary data, then we should - // simply re-use that buffer rather than re-create it. - // - // TODO: Simply re-using the buffer without any kind of validation checks - // means that we are assuming that users cannot or will not perform any `set` - // operations on a shader object once an operation has requested this buffer - // be created. We need to enforce that rule if we want to rely on it. + // If data has been changed since last allocation/filling of constant buffer, + // we will need to allocate a new one. // - if (m_upToDateConstantBufferHeapVersion == - encoder->m_commandBuffer->m_transientHeap->getVersion()) + if (!shouldAllocateConstantBuffer(encoder->m_commandBuffer->m_transientHeap)) { return SLANG_OK; } + m_isConstantBufferDirty = false; + m_constantBufferTransientHeap = encoder->m_commandBuffer->m_transientHeap; + m_constantBufferTransientHeapVersion = encoder->m_commandBuffer->m_transientHeap->getVersion(); m_constantBufferSize = specializedLayout->getTotalOrdinaryDataSize(); if (m_constantBufferSize == 0) { - m_upToDateConstantBufferHeapVersion = - encoder->m_commandBuffer->m_transientHeap->getVersion(); return SLANG_OK; } @@ -2858,10 +2848,6 @@ public: m_constantBufferSize, specializedLayout)); - // Update version tracker so that we don't redundantly alloc and fill in - // constant buffers for the same transient heap. - m_upToDateConstantBufferHeapVersion = - encoder->m_commandBuffer->m_transientHeap->getVersion(); return SLANG_OK; } @@ -3213,9 +3199,6 @@ public: List m_combinedTextureSamplers; - // The version number of the transient resource heap that contains up-to-date - // constant buffer content for this shader object. - uint64_t m_upToDateConstantBufferHeapVersion; // The transient constant buffer that holds the GPU copy of the constant data, // weak referenced. IBufferResource* m_constantBuffer = nullptr; @@ -3223,6 +3206,13 @@ public: size_t m_constantBufferOffset = 0; size_t m_constantBufferSize = 0; + /// Dirty bit tracking whether the constant buffer needs to be updated. + bool m_isConstantBufferDirty = true; + /// The transient heap from which the constant buffer is allocated. + VKDevice::TransientResourceHeapImpl* m_constantBufferTransientHeap; + /// The version of the transient heap when the constant buffer is allocated. + uint64_t m_constantBufferTransientHeapVersion; + /// Get the layout of this shader object with specialization arguments considered /// /// This operation should only be called after the shader object has been @@ -3261,6 +3251,9 @@ public: RefPtr m_specializedLayout; }; + class MutableShaderObjectImpl : public MutableShaderObject + {}; + class EntryPointShaderObject : public ShaderObjectImpl { typedef ShaderObjectImpl Super; @@ -3541,8 +3534,6 @@ public: List> m_entryPoints; }; - class TransientResourceHeapImpl; - class CommandBufferImpl : public ICommandBuffer , public ComObject @@ -4141,7 +4132,7 @@ public: VkPipelineStageFlagBits dstStage = calcPipelineStageFlags(dst, false); auto& vkApi = m_commandBuffer->m_renderer->m_api; - vkApi.vkCmdPipelineBarrier(m_commandBuffer->m_commandBuffer, srcStage, dstStage, 0, 0, nullptr, 0, nullptr, count, barriers.getBuffer()); + vkApi.vkCmdPipelineBarrier(m_commandBuffer->m_commandBuffer, srcStage, dstStage, 0, 0, nullptr, 0, nullptr, (uint32_t)count, barriers.getBuffer()); } virtual SLANG_NO_THROW void SLANG_MCALL bufferBarrier( size_t count, @@ -4150,7 +4141,7 @@ public: ResourceState dst) { List barriers; - barriers.setCount(count); + barriers.reserve(count); for (size_t i = 0; i < count; i++) { @@ -4171,7 +4162,7 @@ public: VkPipelineStageFlagBits dstStage = calcPipelineStageFlags(dst, false); auto& vkApi = m_commandBuffer->m_renderer->m_api; - vkApi.vkCmdPipelineBarrier(m_commandBuffer->m_commandBuffer, srcStage, dstStage, 0, 0, nullptr, count, barriers.getBuffer(), 0, nullptr); + vkApi.vkCmdPipelineBarrier(m_commandBuffer->m_commandBuffer, srcStage, dstStage, 0, 0, nullptr, (uint32_t)count, barriers.getBuffer(), 0, nullptr); } virtual SLANG_NO_THROW void SLANG_MCALL endEncoding() override { @@ -4661,10 +4652,10 @@ public: }; class TransientResourceHeapImpl - : public TransientResourceHeapBase + : public TransientResourceHeapBaseImpl { private: - typedef TransientResourceHeapBase Super; + typedef TransientResourceHeapBaseImpl Super; public: VkCommandPool m_commandPool; @@ -4708,18 +4699,8 @@ public: virtual SLANG_NO_THROW Result SLANG_MCALL synchronizeAndReset() override; }; - class QueryPoolImpl - : public IQueryPool - , public ComObject + class QueryPoolImpl : public QueryPoolBase { - public: - SLANG_COM_OBJECT_IUNKNOWN_ALL - IQueryPool* getInterface(const Guid& guid) - { - if (guid == GfxGUID::IID_ISlangUnknown || guid == GfxGUID::IID_IQueryPool) - return static_cast(this); - return nullptr; - } public: Result init(const IQueryPool::Desc& desc, VKDevice* device) { @@ -6177,9 +6158,9 @@ static VkBufferUsageFlagBits _calcBufferUsageFlags(ResourceState state) return VkBufferUsageFlagBits(0); } case ResourceState::UnorderedAccess: - return VK_BUFFER_USAGE_STORAGE_TEXEL_BUFFER_BIT; + return (VkBufferUsageFlagBits)(VK_BUFFER_USAGE_STORAGE_TEXEL_BUFFER_BIT | VK_BUFFER_USAGE_STORAGE_BUFFER_BIT); case ResourceState::ShaderResource: - return VK_BUFFER_USAGE_STORAGE_BUFFER_BIT; + return (VkBufferUsageFlagBits)(VK_BUFFER_USAGE_UNIFORM_TEXEL_BUFFER_BIT | VK_BUFFER_USAGE_STORAGE_BUFFER_BIT); case ResourceState::CopySource: return VK_BUFFER_USAGE_TRANSFER_SRC_BIT; case ResourceState::CopyDestination: @@ -6302,7 +6283,7 @@ void VKDevice::_transitionImageLayout(VkImage image, VkFormat format, const Text barrier.subresourceRange.baseMipLevel = 0; barrier.subresourceRange.levelCount = desc.numMipLevels; barrier.subresourceRange.baseArrayLayer = 0; - barrier.subresourceRange.layerCount = 1; + barrier.subresourceRange.layerCount = VK_REMAINING_ARRAY_LAYERS; VkPipelineStageFlags sourceStage; VkPipelineStageFlags destinationStage; @@ -6425,7 +6406,6 @@ Result VKDevice::createTextureResource(const ITextureResource::Desc& descIn, con // Create the image { VkImageCreateInfo imageInfo = {VK_STRUCTURE_TYPE_IMAGE_CREATE_INFO}; - switch (desc.type) { case IResource::Type::Texture1D: @@ -6444,6 +6424,7 @@ Result VKDevice::createTextureResource(const ITextureResource::Desc& descIn, con { imageInfo.imageType = VK_IMAGE_TYPE_2D; imageInfo.extent = VkExtent3D{ uint32_t(descIn.size.width), uint32_t(descIn.size.height), 1 }; + imageInfo.flags = VK_IMAGE_CREATE_CUBE_COMPATIBLE_BIT; break; } case IResource::Type::Texture3D: @@ -6470,9 +6451,8 @@ Result VKDevice::createTextureResource(const ITextureResource::Desc& descIn, con imageInfo.tiling = VK_IMAGE_TILING_OPTIMAL; imageInfo.usage = _calcImageUsageFlags(desc.allowedStates, desc.cpuAccessFlags, initData); imageInfo.sharingMode = VK_SHARING_MODE_EXCLUSIVE; - + imageInfo.samples = VK_SAMPLE_COUNT_1_BIT; - imageInfo.flags = 0; // Optional SLANG_VK_RETURN_ON_FAIL(m_api.vkCreateImage(m_device, &imageInfo, nullptr, &texture->m_image)); } @@ -6661,7 +6641,11 @@ Result VKDevice::createBufferResource(const IBufferResource::Desc& descIn, const { usage |= VK_BUFFER_USAGE_SHADER_DEVICE_ADDRESS_BIT; } - + if (desc.allowedStates.contains(ResourceState::ShaderResource) && + m_api.m_extendedFeatures.accelerationStructureFeatures.accelerationStructure) + { + usage |= VK_BUFFER_USAGE_ACCELERATION_STRUCTURE_BUILD_INPUT_READ_ONLY_BIT_KHR; + } if (initData) { usage |= VK_BUFFER_USAGE_TRANSFER_DST_BIT; @@ -6861,19 +6845,20 @@ Result VKDevice::createTextureView(ITextureResource* texture, IResourceView::Des createInfo.format = resourceImpl->m_vkformat; createInfo.image = resourceImpl->m_image; createInfo.components = VkComponentMapping{ VK_COMPONENT_SWIZZLE_R, VK_COMPONENT_SWIZZLE_G,VK_COMPONENT_SWIZZLE_B,VK_COMPONENT_SWIZZLE_A }; + bool isArray = resourceImpl->getDesc()->arraySize != 0; switch (resourceImpl->getType()) { case IResource::Type::Texture1D: - createInfo.viewType = VK_IMAGE_VIEW_TYPE_1D; + createInfo.viewType = isArray ? VK_IMAGE_VIEW_TYPE_1D_ARRAY : VK_IMAGE_VIEW_TYPE_1D; break; case IResource::Type::Texture2D: - createInfo.viewType = VK_IMAGE_VIEW_TYPE_2D; + createInfo.viewType = isArray ? VK_IMAGE_VIEW_TYPE_2D_ARRAY : VK_IMAGE_VIEW_TYPE_2D; break; case IResource::Type::Texture3D: createInfo.viewType = VK_IMAGE_VIEW_TYPE_3D; break; case IResource::Type::TextureCube: - createInfo.viewType = VK_IMAGE_VIEW_TYPE_CUBE; + createInfo.viewType = isArray ? VK_IMAGE_VIEW_TYPE_CUBE_ARRAY : VK_IMAGE_VIEW_TYPE_CUBE; break; default: SLANG_UNIMPLEMENTED_X("Unknown Texture type."); @@ -7144,6 +7129,19 @@ Result VKDevice::createShaderObject(ShaderObjectLayoutBase* layout, IShaderObjec return SLANG_OK; } +Result VKDevice::createMutableShaderObject( + ShaderObjectLayoutBase* layout, + IShaderObject** outObject) +{ + auto layoutImpl = static_cast(layout); + + RefPtr result = new MutableShaderObjectImpl(); + SLANG_RETURN_ON_FAIL(result->init(this, layoutImpl)); + returnComPtr(outObject, result); + + return SLANG_OK; +} + Result VKDevice::createGraphicsPipelineState(const GraphicsPipelineStateDesc& inDesc, IPipelineState** outState) { GraphicsPipelineStateDesc desc = inDesc; diff --git a/tools/slang-test/options.cpp b/tools/slang-test/options.cpp index 27b759a0e..fa7f332b0 100644 --- a/tools/slang-test/options.cpp +++ b/tools/slang-test/options.cpp @@ -266,6 +266,10 @@ static bool _isSubCommand(const char* arg) return res; } } + else if (strcmp(arg, "-skip-api-detection") == 0) + { + optionsOut->skipApiDetection = true; + } else { stdError.print("unknown option '%s'\n", arg); diff --git a/tools/slang-test/options.h b/tools/slang-test/options.h index a8c2e3852..eacbc7acb 100644 --- a/tools/slang-test/options.h +++ b/tools/slang-test/options.h @@ -61,6 +61,9 @@ struct Options // force generation of baselines for HLSL tests bool generateHLSLBaselines = false; + // Whether to skip the step of creating test devices to check if an API is actually available. + bool skipApiDetection = false; + // Dump expected/actual output on failures, for debugging. // This is especially intended for use in continuous // integration builds. diff --git a/tools/slang-test/slang-test-main.cpp b/tools/slang-test/slang-test-main.cpp index 6fb270c35..8f7c7e35b 100644 --- a/tools/slang-test/slang-test-main.cpp +++ b/tools/slang-test/slang-test-main.cpp @@ -897,6 +897,11 @@ static RenderApiFlags _getAvailableRenderApiFlags(TestContext* context) // See if it's possible the api is available if (RenderApiUtil::calcHasApi(apiType)) { + if (context->options.skipApiDetection) + { + availableRenderApiFlags |= RenderApiFlags(1) << int(apiType); + continue; + } // Try starting up the device CommandLine cmdLine; cmdLine.setExecutablePath(Path::combine(context->options.binDir, String("render-test") + ProcessUtil::getExecutableSuffix())); @@ -908,7 +913,6 @@ static RenderApiFlags _getAvailableRenderApiFlags(TestContext* context) StringBuilder builder; builder << "-" << RenderApiUtil::getApiName(apiType); cmdLine.addArg(builder); - // Run the render-test tool and see if the device could startup ExecuteResult exeRes; if (SLANG_SUCCEEDED(spawnAndWaitSharedLibrary(context, "device-startup", cmdLine, exeRes)) @@ -3337,6 +3341,8 @@ static void _disableCPPBackends(TestContext* context) for (auto passThru : cppPassThrus) { context->availableBackendFlags &= ~(PassThroughFlags(1) << int(passThru)); + context->availableRenderApiFlags &= ~(RenderApiFlag::CPU); + context->options.enabledApis &= ~(RenderApiFlag::CPU); } } diff --git a/tools/slang-test/test-reporter.cpp b/tools/slang-test/test-reporter.cpp index 84bcaa32c..8000fd2a5 100644 --- a/tools/slang-test/test-reporter.cpp +++ b/tools/slang-test/test-reporter.cpp @@ -338,6 +338,7 @@ void TestReporter::_addResult(const TestInfo& info) _appendTime(info.executionTime, buffer); } printf("%s test: '%S' %s\n", resultString, info.name.toWString().begin(), buffer.getBuffer()); + fflush(stdout); }; switch (m_outputMode) @@ -498,7 +499,7 @@ void TestReporter::message(TestMessageType type, const String& message) { fputs(message.getBuffer(), stderr); } - + fflush(stderr); // Just dump out if can dump out return; } @@ -515,6 +516,7 @@ void TestReporter::message(TestMessageType type, const String& message) { fputs(message.getBuffer(), stderr); } + fflush(stderr); } if (m_currentMessage.getLength() > 0) -- cgit v1.2.3