From f65d756bff8d4c5cbc15bd0322a2ae8e6b896a21 Mon Sep 17 00:00:00 2001 From: Ellie Hermaszewska Date: Tue, 29 Oct 2024 14:49:26 +0800 Subject: format * format * Minor test fixes * enable checking cpp format in ci --- tools/gfx-unit-test/shader-cache-tests.cpp | 1857 ++++++++++++++-------------- 1 file changed, 944 insertions(+), 913 deletions(-) (limited to 'tools/gfx-unit-test/shader-cache-tests.cpp') diff --git a/tools/gfx-unit-test/shader-cache-tests.cpp b/tools/gfx-unit-test/shader-cache-tests.cpp index 4477c4f56..8ee8d9015 100644 --- a/tools/gfx-unit-test/shader-cache-tests.cpp +++ b/tools/gfx-unit-test/shader-cache-tests.cpp @@ -1,44 +1,43 @@ -#include "tools/unit-test/slang-unit-test.h" - -#include "slang-gfx.h" +#include "gfx-test-texture-util.h" #include "gfx-test-util.h" -#include "tools/gfx-util/shader-cursor.h" +#include "slang-gfx.h" #include "source/core/slang-basic.h" -#include "source/core/slang-string-util.h" -#include "source/core/slang-io.h" #include "source/core/slang-file-system.h" +#include "source/core/slang-io.h" #include "source/core/slang-process.h" -#include "gfx-test-texture-util.h" +#include "source/core/slang-string-util.h" +#include "tools/gfx-util/shader-cursor.h" +#include "tools/unit-test/slang-unit-test.h" using namespace gfx; using namespace Slang; namespace gfx_test { - // Base class for shader cache tests. - // Slang currently does not allow reloading shaders from modified sources. - // Because of this, the tests recreate a GFX device for each test step, - // allowing to modify shader sources in between. - struct ShaderCacheTest - { - UnitTestContext* context; - Slang::RenderApiFlag::Enum api; +// Base class for shader cache tests. +// Slang currently does not allow reloading shaders from modified sources. +// Because of this, the tests recreate a GFX device for each test step, +// allowing to modify shader sources in between. +struct ShaderCacheTest +{ + UnitTestContext* context; + Slang::RenderApiFlag::Enum api; - String testDirectory; - String cacheDirectory; + String testDirectory; + String cacheDirectory; - ComPtr diskFileSystem; + ComPtr diskFileSystem; - IDevice::ShaderCacheDesc shaderCacheDesc = {}; + IDevice::ShaderCacheDesc shaderCacheDesc = {}; - ComPtr device; - ComPtr shaderCache; - ComPtr pipelineState; - ComPtr bufferResource; - ComPtr bufferView; + ComPtr device; + ComPtr shaderCache; + ComPtr pipelineState; + ComPtr bufferResource; + ComPtr bufferView; - String computeShaderA = String( - R"( + String computeShaderA = String( + R"( [shader("compute")] [numthreads(4, 1, 1)] void main( @@ -48,10 +47,10 @@ namespace gfx_test var input = buffer[sv_dispatchThreadID.x]; buffer[sv_dispatchThreadID.x] = input + 1.0f; } - )"); + )"); - String computeShaderB = String( - R"( + String computeShaderB = String( + R"( [shader("compute")] [numthreads(4, 1, 1)] void main( @@ -63,8 +62,8 @@ namespace gfx_test } )"); - String computeShaderC = String( - R"( + String computeShaderC = String( + R"( [shader("compute")] [numthreads(4, 1, 1)] void main( @@ -77,359 +76,383 @@ namespace gfx_test )"); - void removeDirectory(const String& directory) - { - auto osFileSystem = OSFileSystem::getMutableSingleton(); - - struct Context - { - ISlangMutableFileSystem *fileSystem; - const String& directory; - } context { osFileSystem, directory }; - - osFileSystem->enumeratePathContents( - directory.getBuffer(), - [](SlangPathType pathType, const char* fileName, void* userData) - { - struct Context* context = static_cast(userData); - if (pathType == SlangPathType::SLANG_PATH_TYPE_FILE) - { - String path = Path::simplify(context->directory + "/" + fileName); - context->fileSystem->remove(path.getBuffer()); - } - }, - &context); - - osFileSystem->remove(directory.getBuffer()); - } - - void writeShader(const String& source, const String& fileName) - { - diskFileSystem->saveFile(fileName.getBuffer(), source.getBuffer(), source.getLength()); - } - - void init(UnitTestContext* context, Slang::RenderApiFlag::Enum api) - { - this->context = context; - this->api = api; - testDirectory = Path::simplify(Path::getParentDirectory(Path::getExecutablePath()) + "/shader-cache-test" + String(Process::getId())); - cacheDirectory = Path::simplify(testDirectory + "/cache" + String(Process::getId())); - - // Cleanup if there are stale files from a previously aborted test. - removeDirectory(cacheDirectory); - removeDirectory(testDirectory); - - Path::createDirectory(testDirectory); - diskFileSystem = new RelativeFileSystem(OSFileSystem::getMutableSingleton(), testDirectory); - shaderCacheDesc.shaderCachePath = cacheDirectory.getBuffer(); - } - - void cleanup() - { - removeDirectory(cacheDirectory); - removeDirectory(testDirectory); - } + void removeDirectory(const String& directory) + { + auto osFileSystem = OSFileSystem::getMutableSingleton(); - template - void runStep(Func func) + struct Context { - List additionalSearchPaths; - additionalSearchPaths.add(testDirectory.getBuffer()); + ISlangMutableFileSystem* fileSystem; + const String& directory; + } context{osFileSystem, directory}; - runTestImpl( - [this, func] (IDevice* device, UnitTestContext* ctx) + osFileSystem->enumeratePathContents( + directory.getBuffer(), + [](SlangPathType pathType, const char* fileName, void* userData) + { + struct Context* context = static_cast(userData); + if (pathType == SlangPathType::SLANG_PATH_TYPE_FILE) { - this->device = device; - SLANG_CHECK_ABORT(SLANG_SUCCEEDED( - device->queryInterface(SLANG_UUID_IShaderCache, (void**)this->shaderCache.writeRef()))); - func(); - this->device = nullptr; - this->shaderCache = nullptr; - }, - context, api, additionalSearchPaths, shaderCacheDesc); - } - - void createComputeResources() - { - const int numberCount = 4; - float initialData[] = { 0.0f, 1.0f, 2.0f, 3.0f }; - IBufferResource::Desc bufferDesc = {}; - bufferDesc.sizeInBytes = numberCount * sizeof(float); - bufferDesc.format = Format::Unknown; - bufferDesc.elementSize = sizeof(float); - bufferDesc.allowedStates = ResourceStateSet( - ResourceState::ShaderResource, - ResourceState::UnorderedAccess, - ResourceState::CopyDestination, - ResourceState::CopySource); - bufferDesc.defaultState = ResourceState::UnorderedAccess; - bufferDesc.memoryType = MemoryType::DeviceLocal; - - GFX_CHECK_CALL_ABORT(device->createBufferResource( - bufferDesc, - (void*)initialData, - bufferResource.writeRef())); - - IResourceView::Desc viewDesc = {}; - viewDesc.type = IResourceView::Type::UnorderedAccess; - viewDesc.format = Format::Unknown; - GFX_CHECK_CALL_ABORT( - device->createBufferView(bufferResource, nullptr, viewDesc, bufferView.writeRef())); - } - - void freeComputeResources() - { - bufferResource = nullptr; - bufferView = nullptr; - pipelineState = nullptr; - } - - void createComputePipeline(const char* moduleName, const char* entryPointName) - { - ComPtr shaderProgram; - slang::ProgramLayout* slangReflection; - GFX_CHECK_CALL_ABORT(loadComputeProgram(device, shaderProgram, moduleName, entryPointName, slangReflection)); - - ComputePipelineStateDesc pipelineDesc = {}; - pipelineDesc.program = shaderProgram.get(); - GFX_CHECK_CALL_ABORT( - device->createComputePipelineState(pipelineDesc, pipelineState.writeRef())); - } - - void createComputePipeline(Slang::String shaderSource) - { - ComPtr shaderProgram; - GFX_CHECK_CALL_ABORT(loadComputeProgramFromSource(device, shaderProgram, shaderSource)); - - ComputePipelineStateDesc pipelineDesc = {}; - pipelineDesc.program = shaderProgram.get(); - GFX_CHECK_CALL_ABORT( - device->createComputePipelineState(pipelineDesc, pipelineState.writeRef())); - } + String path = Path::simplify(context->directory + "/" + fileName); + context->fileSystem->remove(path.getBuffer()); + } + }, + &context); - void dispatchComputePipeline() - { - ComPtr transientHeap; - ITransientResourceHeap::Desc transientHeapDesc = {}; - transientHeapDesc.constantBufferSize = 4096; - GFX_CHECK_CALL_ABORT( - device->createTransientResourceHeap(transientHeapDesc, transientHeap.writeRef())); + osFileSystem->remove(directory.getBuffer()); + } - ICommandQueue::Desc queueDesc = { ICommandQueue::QueueType::Graphics }; - auto queue = device->createCommandQueue(queueDesc); + void writeShader(const String& source, const String& fileName) + { + diskFileSystem->saveFile(fileName.getBuffer(), source.getBuffer(), source.getLength()); + } - auto commandBuffer = transientHeap->createCommandBuffer(); - auto encoder = commandBuffer->encodeComputeCommands(); + void init(UnitTestContext* context, Slang::RenderApiFlag::Enum api) + { + this->context = context; + this->api = api; + testDirectory = Path::simplify( + Path::getParentDirectory(Path::getExecutablePath()) + "/shader-cache-test" + + String(Process::getId())); + cacheDirectory = Path::simplify(testDirectory + "/cache" + String(Process::getId())); + + // Cleanup if there are stale files from a previously aborted test. + removeDirectory(cacheDirectory); + removeDirectory(testDirectory); + + Path::createDirectory(testDirectory); + diskFileSystem = new RelativeFileSystem(OSFileSystem::getMutableSingleton(), testDirectory); + shaderCacheDesc.shaderCachePath = cacheDirectory.getBuffer(); + } - auto rootObject = encoder->bindPipeline(pipelineState); + void cleanup() + { + removeDirectory(cacheDirectory); + removeDirectory(testDirectory); + } - // Bind buffer view to the entry point. - ShaderCursor entryPointCursor(rootObject->getEntryPoint(0)); - entryPointCursor.getPath("buffer").setResource(bufferView); + template + void runStep(Func func) + { + List additionalSearchPaths; + additionalSearchPaths.add(testDirectory.getBuffer()); - encoder->dispatchCompute(4, 1, 1); - encoder->endEncoding(); - commandBuffer->close(); - queue->executeCommandBuffer(commandBuffer); - queue->waitOnHost(); - } + runTestImpl( + [this, func](IDevice* device, UnitTestContext* ctx) + { + this->device = device; + SLANG_CHECK_ABORT(SLANG_SUCCEEDED(device->queryInterface( + SLANG_UUID_IShaderCache, + (void**)this->shaderCache.writeRef()))); + func(); + this->device = nullptr; + this->shaderCache = nullptr; + }, + context, + api, + additionalSearchPaths, + shaderCacheDesc); + } - bool checkOutput(const List& expectedOutput) - { - ComPtr bufferBlob; - device->readBufferResource(bufferResource, 0, 4 * sizeof(float), bufferBlob.writeRef()); - SLANG_CHECK_ABORT(bufferBlob && bufferBlob->getBufferSize() == expectedOutput.getCount() * sizeof(float)); - return ::memcmp(bufferBlob->getBufferPointer(), expectedOutput.getBuffer(), bufferBlob->getBufferSize()) == 0; - } + void createComputeResources() + { + const int numberCount = 4; + float initialData[] = {0.0f, 1.0f, 2.0f, 3.0f}; + IBufferResource::Desc bufferDesc = {}; + bufferDesc.sizeInBytes = numberCount * sizeof(float); + bufferDesc.format = Format::Unknown; + bufferDesc.elementSize = sizeof(float); + bufferDesc.allowedStates = ResourceStateSet( + ResourceState::ShaderResource, + ResourceState::UnorderedAccess, + ResourceState::CopyDestination, + ResourceState::CopySource); + bufferDesc.defaultState = ResourceState::UnorderedAccess; + bufferDesc.memoryType = MemoryType::DeviceLocal; + + GFX_CHECK_CALL_ABORT(device->createBufferResource( + bufferDesc, + (void*)initialData, + bufferResource.writeRef())); + + IResourceView::Desc viewDesc = {}; + viewDesc.type = IResourceView::Type::UnorderedAccess; + viewDesc.format = Format::Unknown; + GFX_CHECK_CALL_ABORT( + device->createBufferView(bufferResource, nullptr, viewDesc, bufferView.writeRef())); + } - bool runComputePipeline(const char* moduleName, const char* entryPointName, const List& expectedOutput) - { - createComputeResources(); - createComputePipeline(moduleName, entryPointName); - dispatchComputePipeline(); - bool hasExpectedOutput = checkOutput(expectedOutput); - SLANG_CHECK(hasExpectedOutput); - freeComputeResources(); - return hasExpectedOutput; - } - - bool runComputePipeline(Slang::String shaderSource, const List& expectedOutput) - { - createComputeResources(); - createComputePipeline(shaderSource); - dispatchComputePipeline(); - bool hasExpectedOutput = checkOutput(expectedOutput); - SLANG_CHECK(hasExpectedOutput); - freeComputeResources(); - return hasExpectedOutput; - } - - ShaderCacheStats getStats() - { - SLANG_ASSERT(shaderCache); - ShaderCacheStats stats; - shaderCache->getShaderCacheStats(&stats); - return stats; - } + void freeComputeResources() + { + bufferResource = nullptr; + bufferView = nullptr; + pipelineState = nullptr; + } - void run(UnitTestContext* context, Slang::RenderApiFlag::Enum api) - { - init(context, api); - runTests(); - cleanup(); - } + void createComputePipeline(const char* moduleName, const char* entryPointName) + { + ComPtr shaderProgram; + slang::ProgramLayout* slangReflection; + GFX_CHECK_CALL_ABORT( + loadComputeProgram(device, shaderProgram, moduleName, entryPointName, slangReflection)); - virtual void runTests() = 0; - }; + ComputePipelineStateDesc pipelineDesc = {}; + pipelineDesc.program = shaderProgram.get(); + GFX_CHECK_CALL_ABORT( + device->createComputePipelineState(pipelineDesc, pipelineState.writeRef())); + } - // Basic shader cache test using 3 different shader files stored on disk. - struct ShaderCacheSourceFile : ShaderCacheTest + void createComputePipeline(Slang::String shaderSource) { - void runTests() - { - // Write shader source files. - writeShader(computeShaderA, "shader-cache-tmp-a.slang"); - writeShader(computeShaderB, "shader-cache-tmp-b.slang"); - writeShader(computeShaderC, "shader-cache-tmp-c.slang"); - - // Cache is cold and we expect 3 misses. - runStep( - [this]() - { - SLANG_CHECK(runComputePipeline("shader-cache-tmp-a", "main", { 1.f, 2.f, 3.f, 4.f })); - SLANG_CHECK(runComputePipeline("shader-cache-tmp-b", "main", { 2.f, 3.f, 4.f, 5.f })); - SLANG_CHECK(runComputePipeline("shader-cache-tmp-c", "main", { 3.f, 4.f, 5.f, 6.f })); - - SLANG_CHECK(getStats().missCount == 3); - SLANG_CHECK(getStats().hitCount == 0); - SLANG_CHECK(getStats().entryCount == 3); - } - ); + ComPtr shaderProgram; + GFX_CHECK_CALL_ABORT(loadComputeProgramFromSource(device, shaderProgram, shaderSource)); - // Cache is hot and we expect 3 hits. - runStep( - [this]() - { - SLANG_CHECK(runComputePipeline("shader-cache-tmp-a", "main", { 1.f, 2.f, 3.f, 4.f })); - SLANG_CHECK(runComputePipeline("shader-cache-tmp-b", "main", { 2.f, 3.f, 4.f, 5.f })); - SLANG_CHECK(runComputePipeline("shader-cache-tmp-c", "main", { 3.f, 4.f, 5.f, 6.f })); + ComputePipelineStateDesc pipelineDesc = {}; + pipelineDesc.program = shaderProgram.get(); + GFX_CHECK_CALL_ABORT( + device->createComputePipelineState(pipelineDesc, pipelineState.writeRef())); + } - SLANG_CHECK(getStats().missCount == 0); - SLANG_CHECK(getStats().hitCount == 3); - SLANG_CHECK(getStats().entryCount == 3); - } - ); + void dispatchComputePipeline() + { + ComPtr transientHeap; + ITransientResourceHeap::Desc transientHeapDesc = {}; + transientHeapDesc.constantBufferSize = 4096; + GFX_CHECK_CALL_ABORT( + device->createTransientResourceHeap(transientHeapDesc, transientHeap.writeRef())); - // Write shader source files, all rotated by one. - writeShader(computeShaderA, "shader-cache-tmp-b.slang"); - writeShader(computeShaderB, "shader-cache-tmp-c.slang"); - writeShader(computeShaderC, "shader-cache-tmp-a.slang"); + ICommandQueue::Desc queueDesc = {ICommandQueue::QueueType::Graphics}; + auto queue = device->createCommandQueue(queueDesc); - // Cache is cold again and we expect 3 misses. - runStep( - [this]() - { - SLANG_CHECK(runComputePipeline("shader-cache-tmp-b", "main", { 1.f, 2.f, 3.f, 4.f })); - SLANG_CHECK(runComputePipeline("shader-cache-tmp-c", "main", { 2.f, 3.f, 4.f, 5.f })); - SLANG_CHECK(runComputePipeline("shader-cache-tmp-a", "main", { 3.f, 4.f, 5.f, 6.f })); + auto commandBuffer = transientHeap->createCommandBuffer(); + auto encoder = commandBuffer->encodeComputeCommands(); - SLANG_CHECK(getStats().missCount == 3); - SLANG_CHECK(getStats().hitCount == 0); - SLANG_CHECK(getStats().entryCount == 6); - } - ); + auto rootObject = encoder->bindPipeline(pipelineState); - // Cache is hot again and we expect 3 hits. - runStep( - [this]() - { - SLANG_CHECK(runComputePipeline("shader-cache-tmp-b", "main", { 1.f, 2.f, 3.f, 4.f })); - SLANG_CHECK(runComputePipeline("shader-cache-tmp-c", "main", { 2.f, 3.f, 4.f, 5.f })); - SLANG_CHECK(runComputePipeline("shader-cache-tmp-a", "main", { 3.f, 4.f, 5.f, 6.f })); + // Bind buffer view to the entry point. + ShaderCursor entryPointCursor(rootObject->getEntryPoint(0)); + entryPointCursor.getPath("buffer").setResource(bufferView); - SLANG_CHECK(getStats().missCount == 0); - SLANG_CHECK(getStats().hitCount == 3); - SLANG_CHECK(getStats().entryCount == 6); - } - ); - } - }; + encoder->dispatchCompute(4, 1, 1); + encoder->endEncoding(); + commandBuffer->close(); + queue->executeCommandBuffer(commandBuffer); + queue->waitOnHost(); + } - // Test caching of shaders that are compiled from source strings instead of files. - struct ShaderCacheTestSourceString : ShaderCacheTest + bool checkOutput(const List& expectedOutput) { - void runTests() - { - // Cache is cold and we expect 3 misses. - runStep( - [this]() - { - SLANG_CHECK(runComputePipeline(computeShaderA, { 1.f, 2.f, 3.f, 4.f })); - SLANG_CHECK(runComputePipeline(computeShaderB, { 2.f, 3.f, 4.f, 5.f })); - SLANG_CHECK(runComputePipeline(computeShaderC, { 3.f, 4.f, 5.f, 6.f })); + ComPtr bufferBlob; + device->readBufferResource(bufferResource, 0, 4 * sizeof(float), bufferBlob.writeRef()); + SLANG_CHECK_ABORT( + bufferBlob && bufferBlob->getBufferSize() == expectedOutput.getCount() * sizeof(float)); + return ::memcmp( + bufferBlob->getBufferPointer(), + expectedOutput.getBuffer(), + bufferBlob->getBufferSize()) == 0; + } - SLANG_CHECK(getStats().missCount == 3); - SLANG_CHECK(getStats().hitCount == 0); - SLANG_CHECK(getStats().entryCount == 3); - } - ); + bool runComputePipeline( + const char* moduleName, + const char* entryPointName, + const List& expectedOutput) + { + createComputeResources(); + createComputePipeline(moduleName, entryPointName); + dispatchComputePipeline(); + bool hasExpectedOutput = checkOutput(expectedOutput); + SLANG_CHECK(hasExpectedOutput); + freeComputeResources(); + return hasExpectedOutput; + } - // Cache is hot and we expect 3 hits. - runStep( - [this]() - { - SLANG_CHECK(runComputePipeline(computeShaderA, { 1.f, 2.f, 3.f, 4.f })); - SLANG_CHECK(runComputePipeline(computeShaderB, { 2.f, 3.f, 4.f, 5.f })); - SLANG_CHECK(runComputePipeline(computeShaderC, { 3.f, 4.f, 5.f, 6.f })); + bool runComputePipeline(Slang::String shaderSource, const List& expectedOutput) + { + createComputeResources(); + createComputePipeline(shaderSource); + dispatchComputePipeline(); + bool hasExpectedOutput = checkOutput(expectedOutput); + SLANG_CHECK(hasExpectedOutput); + freeComputeResources(); + return hasExpectedOutput; + } - SLANG_CHECK(getStats().missCount == 0); - SLANG_CHECK(getStats().hitCount == 3); - SLANG_CHECK(getStats().entryCount == 3); - } - ); - } - }; + ShaderCacheStats getStats() + { + SLANG_ASSERT(shaderCache); + ShaderCacheStats stats; + shaderCache->getShaderCacheStats(&stats); + return stats; + } - // Test one shader file on disk with multiple entry points. - struct ShaderCacheTestEntryPoint : ShaderCacheTest + void run(UnitTestContext* context, Slang::RenderApiFlag::Enum api) { - void runTests() - { - // Cache is cold and we expect 3 misses, one for each entry point. - runStep( - [this]() - { - SLANG_CHECK(runComputePipeline("shader-cache-multiple-entry-points", "computeA", { 1.f, 2.f, 3.f, 4.f })); - SLANG_CHECK(runComputePipeline("shader-cache-multiple-entry-points", "computeB", { 2.f, 3.f, 4.f, 5.f })); - SLANG_CHECK(runComputePipeline("shader-cache-multiple-entry-points", "computeC", { 3.f, 4.f, 5.f, 6.f })); + init(context, api); + runTests(); + cleanup(); + } - SLANG_CHECK(getStats().missCount == 3); - SLANG_CHECK(getStats().hitCount == 0); - SLANG_CHECK(getStats().entryCount == 3); - } - ); + virtual void runTests() = 0; +}; - // Cache is hot and we expect 3 hits. - runStep( - [this]() - { - SLANG_CHECK(runComputePipeline("shader-cache-multiple-entry-points", "computeA", { 1.f, 2.f, 3.f, 4.f })); - SLANG_CHECK(runComputePipeline("shader-cache-multiple-entry-points", "computeB", { 2.f, 3.f, 4.f, 5.f })); - SLANG_CHECK(runComputePipeline("shader-cache-multiple-entry-points", "computeC", { 3.f, 4.f, 5.f, 6.f })); +// Basic shader cache test using 3 different shader files stored on disk. +struct ShaderCacheSourceFile : ShaderCacheTest +{ + void runTests() + { + // Write shader source files. + writeShader(computeShaderA, "shader-cache-tmp-a.slang"); + writeShader(computeShaderB, "shader-cache-tmp-b.slang"); + writeShader(computeShaderC, "shader-cache-tmp-c.slang"); + + // Cache is cold and we expect 3 misses. + runStep( + [this]() + { + SLANG_CHECK(runComputePipeline("shader-cache-tmp-a", "main", {1.f, 2.f, 3.f, 4.f})); + SLANG_CHECK(runComputePipeline("shader-cache-tmp-b", "main", {2.f, 3.f, 4.f, 5.f})); + SLANG_CHECK(runComputePipeline("shader-cache-tmp-c", "main", {3.f, 4.f, 5.f, 6.f})); + + SLANG_CHECK(getStats().missCount == 3); + SLANG_CHECK(getStats().hitCount == 0); + SLANG_CHECK(getStats().entryCount == 3); + }); + + // Cache is hot and we expect 3 hits. + runStep( + [this]() + { + SLANG_CHECK(runComputePipeline("shader-cache-tmp-a", "main", {1.f, 2.f, 3.f, 4.f})); + SLANG_CHECK(runComputePipeline("shader-cache-tmp-b", "main", {2.f, 3.f, 4.f, 5.f})); + SLANG_CHECK(runComputePipeline("shader-cache-tmp-c", "main", {3.f, 4.f, 5.f, 6.f})); + + SLANG_CHECK(getStats().missCount == 0); + SLANG_CHECK(getStats().hitCount == 3); + SLANG_CHECK(getStats().entryCount == 3); + }); + + // Write shader source files, all rotated by one. + writeShader(computeShaderA, "shader-cache-tmp-b.slang"); + writeShader(computeShaderB, "shader-cache-tmp-c.slang"); + writeShader(computeShaderC, "shader-cache-tmp-a.slang"); + + // Cache is cold again and we expect 3 misses. + runStep( + [this]() + { + SLANG_CHECK(runComputePipeline("shader-cache-tmp-b", "main", {1.f, 2.f, 3.f, 4.f})); + SLANG_CHECK(runComputePipeline("shader-cache-tmp-c", "main", {2.f, 3.f, 4.f, 5.f})); + SLANG_CHECK(runComputePipeline("shader-cache-tmp-a", "main", {3.f, 4.f, 5.f, 6.f})); + + SLANG_CHECK(getStats().missCount == 3); + SLANG_CHECK(getStats().hitCount == 0); + SLANG_CHECK(getStats().entryCount == 6); + }); + + // Cache is hot again and we expect 3 hits. + runStep( + [this]() + { + SLANG_CHECK(runComputePipeline("shader-cache-tmp-b", "main", {1.f, 2.f, 3.f, 4.f})); + SLANG_CHECK(runComputePipeline("shader-cache-tmp-c", "main", {2.f, 3.f, 4.f, 5.f})); + SLANG_CHECK(runComputePipeline("shader-cache-tmp-a", "main", {3.f, 4.f, 5.f, 6.f})); + + SLANG_CHECK(getStats().missCount == 0); + SLANG_CHECK(getStats().hitCount == 3); + SLANG_CHECK(getStats().entryCount == 6); + }); + } +}; - SLANG_CHECK(getStats().missCount == 0); - SLANG_CHECK(getStats().hitCount == 3); - SLANG_CHECK(getStats().entryCount == 3); - } - ); - } - }; +// Test caching of shaders that are compiled from source strings instead of files. +struct ShaderCacheTestSourceString : ShaderCacheTest +{ + void runTests() + { + // Cache is cold and we expect 3 misses. + runStep( + [this]() + { + SLANG_CHECK(runComputePipeline(computeShaderA, {1.f, 2.f, 3.f, 4.f})); + SLANG_CHECK(runComputePipeline(computeShaderB, {2.f, 3.f, 4.f, 5.f})); + SLANG_CHECK(runComputePipeline(computeShaderC, {3.f, 4.f, 5.f, 6.f})); + + SLANG_CHECK(getStats().missCount == 3); + SLANG_CHECK(getStats().hitCount == 0); + SLANG_CHECK(getStats().entryCount == 3); + }); + + // Cache is hot and we expect 3 hits. + runStep( + [this]() + { + SLANG_CHECK(runComputePipeline(computeShaderA, {1.f, 2.f, 3.f, 4.f})); + SLANG_CHECK(runComputePipeline(computeShaderB, {2.f, 3.f, 4.f, 5.f})); + SLANG_CHECK(runComputePipeline(computeShaderC, {3.f, 4.f, 5.f, 6.f})); + + SLANG_CHECK(getStats().missCount == 0); + SLANG_CHECK(getStats().hitCount == 3); + SLANG_CHECK(getStats().entryCount == 3); + }); + } +}; - // Test cache invalidation due to an import/include file being changed on disk. - struct ShaderCacheTestImportInclude : ShaderCacheTest +// Test one shader file on disk with multiple entry points. +struct ShaderCacheTestEntryPoint : ShaderCacheTest +{ + void runTests() { - String importedContentsA = String( - R"( + // Cache is cold and we expect 3 misses, one for each entry point. + runStep( + [this]() + { + SLANG_CHECK(runComputePipeline( + "shader-cache-multiple-entry-points", + "computeA", + {1.f, 2.f, 3.f, 4.f})); + SLANG_CHECK(runComputePipeline( + "shader-cache-multiple-entry-points", + "computeB", + {2.f, 3.f, 4.f, 5.f})); + SLANG_CHECK(runComputePipeline( + "shader-cache-multiple-entry-points", + "computeC", + {3.f, 4.f, 5.f, 6.f})); + + SLANG_CHECK(getStats().missCount == 3); + SLANG_CHECK(getStats().hitCount == 0); + SLANG_CHECK(getStats().entryCount == 3); + }); + + // Cache is hot and we expect 3 hits. + runStep( + [this]() + { + SLANG_CHECK(runComputePipeline( + "shader-cache-multiple-entry-points", + "computeA", + {1.f, 2.f, 3.f, 4.f})); + SLANG_CHECK(runComputePipeline( + "shader-cache-multiple-entry-points", + "computeB", + {2.f, 3.f, 4.f, 5.f})); + SLANG_CHECK(runComputePipeline( + "shader-cache-multiple-entry-points", + "computeC", + {3.f, 4.f, 5.f, 6.f})); + + SLANG_CHECK(getStats().missCount == 0); + SLANG_CHECK(getStats().hitCount == 3); + SLANG_CHECK(getStats().entryCount == 3); + }); + } +}; + +// Test cache invalidation due to an import/include file being changed on disk. +struct ShaderCacheTestImportInclude : ShaderCacheTest +{ + String importedContentsA = String( + R"( public void processElement(RWStructuredBuffer buffer, uint index) { var input = buffer[index]; @@ -437,8 +460,8 @@ namespace gfx_test } )"); - String importedContentsB = String( - R"( + String importedContentsB = String( + R"( public void processElement(RWStructuredBuffer buffer, uint index) { var input = buffer[index]; @@ -446,8 +469,8 @@ namespace gfx_test } )"); - String importFile = String( - R"( + String importFile = String( + R"( import shader_cache_tmp_imported; [shader("compute")] @@ -460,8 +483,8 @@ namespace gfx_test } )"); - String includeFile = String( - R"( + String includeFile = String( + R"( #include "shader-cache-tmp-imported.slang" [shader("compute")] @@ -473,627 +496,635 @@ namespace gfx_test processElement(buffer, sv_dispatchThreadID.x); })"); - void runTests() - { - // Write shader source files. - writeShader(importedContentsA, "shader-cache-tmp-imported.slang"); - writeShader(importFile, "shader-cache-tmp-import.slang"); - writeShader(includeFile, "shader-cache-tmp-include.slang"); - - // Cache is cold and we expect 2 misses. - runStep( - [this]() - { - SLANG_CHECK(runComputePipeline("shader-cache-tmp-import", "main", { 1.f, 2.f, 3.f, 4.f })); - SLANG_CHECK(runComputePipeline("shader-cache-tmp-include", "main", { 1.f, 2.f, 3.f, 4.f })); - - SLANG_CHECK(getStats().missCount == 2); - SLANG_CHECK(getStats().hitCount == 0); - SLANG_CHECK(getStats().entryCount == 2); - } - ); - - // Cache is hot and we expect 2 hits. - runStep( - [this]() - { - SLANG_CHECK(runComputePipeline("shader-cache-tmp-import", "main", { 1.f, 2.f, 3.f, 4.f })); - SLANG_CHECK(runComputePipeline("shader-cache-tmp-include", "main", { 1.f, 2.f, 3.f, 4.f })); - - SLANG_CHECK(getStats().missCount == 0); - SLANG_CHECK(getStats().hitCount == 2); - SLANG_CHECK(getStats().entryCount == 2); - } - ); - - // Change content of imported/included shader file. - writeShader(importedContentsB, "shader-cache-tmp-imported.slang"); - - // Cache is cold and we expect 2 misses. - runStep( - [this]() - { - SLANG_CHECK(runComputePipeline("shader-cache-tmp-import", "main", { 2.f, 3.f, 4.f, 5.f })); - SLANG_CHECK(runComputePipeline("shader-cache-tmp-include", "main", { 2.f, 3.f, 4.f, 5.f })); - - SLANG_CHECK(getStats().missCount == 2); - SLANG_CHECK(getStats().hitCount == 0); - SLANG_CHECK(getStats().entryCount == 4); - } - ); - - // Cache is hot and we expect 2 hits. - runStep( - [this]() - { - SLANG_CHECK(runComputePipeline("shader-cache-tmp-import", "main", { 2.f, 3.f, 4.f, 5.f })); - SLANG_CHECK(runComputePipeline("shader-cache-tmp-include", "main", { 2.f, 3.f, 4.f, 5.f })); - - SLANG_CHECK(getStats().missCount == 0); - SLANG_CHECK(getStats().hitCount == 2); - SLANG_CHECK(getStats().entryCount == 4); - } - ); - } - }; - - // One shader featuring multiple kinds of shader objects that can be bound. - struct ShaderCacheTestSpecialization : ShaderCacheTest + void runTests() { - slang::ProgramLayout* slangReflection; - - void createComputePipeline() - { - ComPtr shaderProgram; - - GFX_CHECK_CALL_ABORT( - loadComputeProgram(device, shaderProgram, "shader-cache-specialization", "computeMain", slangReflection)); - - ComputePipelineStateDesc pipelineDesc = {}; - pipelineDesc.program = shaderProgram.get(); - GFX_CHECK_CALL_ABORT( - device->createComputePipelineState(pipelineDesc, pipelineState.writeRef())); - } - - void dispatchComputePipeline(const char* transformerTypeName) - { - Slang::ComPtr transientHeap; - ITransientResourceHeap::Desc transientHeapDesc = {}; - transientHeapDesc.constantBufferSize = 4096; - GFX_CHECK_CALL_ABORT( - device->createTransientResourceHeap(transientHeapDesc, transientHeap.writeRef())); - - ICommandQueue::Desc queueDesc = { ICommandQueue::QueueType::Graphics }; - auto queue = device->createCommandQueue(queueDesc); - - auto commandBuffer = transientHeap->createCommandBuffer(); - auto encoder = commandBuffer->encodeComputeCommands(); - - auto rootObject = encoder->bindPipeline(pipelineState); + // Write shader source files. + writeShader(importedContentsA, "shader-cache-tmp-imported.slang"); + writeShader(importFile, "shader-cache-tmp-import.slang"); + writeShader(includeFile, "shader-cache-tmp-include.slang"); + + // Cache is cold and we expect 2 misses. + runStep( + [this]() + { + SLANG_CHECK( + runComputePipeline("shader-cache-tmp-import", "main", {1.f, 2.f, 3.f, 4.f})); + SLANG_CHECK( + runComputePipeline("shader-cache-tmp-include", "main", {1.f, 2.f, 3.f, 4.f})); + + SLANG_CHECK(getStats().missCount == 2); + SLANG_CHECK(getStats().hitCount == 0); + SLANG_CHECK(getStats().entryCount == 2); + }); + + // Cache is hot and we expect 2 hits. + runStep( + [this]() + { + SLANG_CHECK( + runComputePipeline("shader-cache-tmp-import", "main", {1.f, 2.f, 3.f, 4.f})); + SLANG_CHECK( + runComputePipeline("shader-cache-tmp-include", "main", {1.f, 2.f, 3.f, 4.f})); + + SLANG_CHECK(getStats().missCount == 0); + SLANG_CHECK(getStats().hitCount == 2); + SLANG_CHECK(getStats().entryCount == 2); + }); + + // Change content of imported/included shader file. + writeShader(importedContentsB, "shader-cache-tmp-imported.slang"); + + // Cache is cold and we expect 2 misses. + runStep( + [this]() + { + SLANG_CHECK( + runComputePipeline("shader-cache-tmp-import", "main", {2.f, 3.f, 4.f, 5.f})); + SLANG_CHECK( + runComputePipeline("shader-cache-tmp-include", "main", {2.f, 3.f, 4.f, 5.f})); + + SLANG_CHECK(getStats().missCount == 2); + SLANG_CHECK(getStats().hitCount == 0); + SLANG_CHECK(getStats().entryCount == 4); + }); + + // Cache is hot and we expect 2 hits. + runStep( + [this]() + { + SLANG_CHECK( + runComputePipeline("shader-cache-tmp-import", "main", {2.f, 3.f, 4.f, 5.f})); + SLANG_CHECK( + runComputePipeline("shader-cache-tmp-include", "main", {2.f, 3.f, 4.f, 5.f})); + + SLANG_CHECK(getStats().missCount == 0); + SLANG_CHECK(getStats().hitCount == 2); + SLANG_CHECK(getStats().entryCount == 4); + }); + } +}; - Slang::ComPtr transformer; - slang::TypeReflection* transformerType = slangReflection->findTypeByName(transformerTypeName); - GFX_CHECK_CALL_ABORT(device->createShaderObject( - transformerType, ShaderObjectContainerType::None, transformer.writeRef())); +// One shader featuring multiple kinds of shader objects that can be bound. +struct ShaderCacheTestSpecialization : ShaderCacheTest +{ + slang::ProgramLayout* slangReflection; - float c = 5.f; - ShaderCursor(transformer).getPath("c").setData(&c, sizeof(float)); + void createComputePipeline() + { + ComPtr shaderProgram; + + GFX_CHECK_CALL_ABORT(loadComputeProgram( + device, + shaderProgram, + "shader-cache-specialization", + "computeMain", + slangReflection)); + + ComputePipelineStateDesc pipelineDesc = {}; + pipelineDesc.program = shaderProgram.get(); + GFX_CHECK_CALL_ABORT( + device->createComputePipelineState(pipelineDesc, pipelineState.writeRef())); + } - ShaderCursor entryPointCursor(rootObject->getEntryPoint(0)); - entryPointCursor.getPath("buffer").setResource(bufferView); - entryPointCursor.getPath("transformer").setObject(transformer); + void dispatchComputePipeline(const char* transformerTypeName) + { + Slang::ComPtr transientHeap; + ITransientResourceHeap::Desc transientHeapDesc = {}; + transientHeapDesc.constantBufferSize = 4096; + GFX_CHECK_CALL_ABORT( + device->createTransientResourceHeap(transientHeapDesc, transientHeap.writeRef())); + + ICommandQueue::Desc queueDesc = {ICommandQueue::QueueType::Graphics}; + auto queue = device->createCommandQueue(queueDesc); + + auto commandBuffer = transientHeap->createCommandBuffer(); + auto encoder = commandBuffer->encodeComputeCommands(); + + auto rootObject = encoder->bindPipeline(pipelineState); + + Slang::ComPtr transformer; + slang::TypeReflection* transformerType = + slangReflection->findTypeByName(transformerTypeName); + GFX_CHECK_CALL_ABORT(device->createShaderObject( + transformerType, + ShaderObjectContainerType::None, + transformer.writeRef())); + + float c = 5.f; + ShaderCursor(transformer).getPath("c").setData(&c, sizeof(float)); + + ShaderCursor entryPointCursor(rootObject->getEntryPoint(0)); + entryPointCursor.getPath("buffer").setResource(bufferView); + entryPointCursor.getPath("transformer").setObject(transformer); + + encoder->dispatchCompute(1, 1, 1); + encoder->endEncoding(); + commandBuffer->close(); + queue->executeCommandBuffer(commandBuffer); + queue->waitOnHost(); + } - encoder->dispatchCompute(1, 1, 1); - encoder->endEncoding(); - commandBuffer->close(); - queue->executeCommandBuffer(commandBuffer); - queue->waitOnHost(); - } + bool runComputePipeline(const char* transformerTypeName, const List& expectedOutput) + { + createComputeResources(); + createComputePipeline(); + dispatchComputePipeline(transformerTypeName); + bool hasExpectedOutput = checkOutput(expectedOutput); + SLANG_CHECK(hasExpectedOutput); + freeComputeResources(); + return hasExpectedOutput; + } - bool runComputePipeline(const char* transformerTypeName, const List& expectedOutput) - { - createComputeResources(); - createComputePipeline(); - dispatchComputePipeline(transformerTypeName); - bool hasExpectedOutput = checkOutput(expectedOutput); - SLANG_CHECK(hasExpectedOutput); - freeComputeResources(); - return hasExpectedOutput; - } - - void runTests() - { - // Cache is cold and we expect 2 misses. - runStep( - [this]() - { - SLANG_CHECK(runComputePipeline("AddTransformer", { 5.f, 6.f, 7.f, 8.f })); - SLANG_CHECK(runComputePipeline("MulTransformer", { 0.f, 5.f, 10.f, 15.f })); + void runTests() + { + // Cache is cold and we expect 2 misses. + runStep( + [this]() + { + SLANG_CHECK(runComputePipeline("AddTransformer", {5.f, 6.f, 7.f, 8.f})); + SLANG_CHECK(runComputePipeline("MulTransformer", {0.f, 5.f, 10.f, 15.f})); - SLANG_CHECK(getStats().missCount == 2); - SLANG_CHECK(getStats().hitCount == 0); - SLANG_CHECK(getStats().entryCount == 2); - } - ); + SLANG_CHECK(getStats().missCount == 2); + SLANG_CHECK(getStats().hitCount == 0); + SLANG_CHECK(getStats().entryCount == 2); + }); - // Cache is hot and we expect 2 hits. - runStep( - [this]() - { - SLANG_CHECK(runComputePipeline("AddTransformer", { 5.f, 6.f, 7.f, 8.f })); - SLANG_CHECK(runComputePipeline("MulTransformer", { 0.f, 5.f, 10.f, 15.f })); + // Cache is hot and we expect 2 hits. + runStep( + [this]() + { + SLANG_CHECK(runComputePipeline("AddTransformer", {5.f, 6.f, 7.f, 8.f})); + SLANG_CHECK(runComputePipeline("MulTransformer", {0.f, 5.f, 10.f, 15.f})); - SLANG_CHECK(getStats().missCount == 0); - SLANG_CHECK(getStats().hitCount == 2); - SLANG_CHECK(getStats().entryCount == 2); - } - ); - } - }; + SLANG_CHECK(getStats().missCount == 0); + SLANG_CHECK(getStats().hitCount == 2); + SLANG_CHECK(getStats().entryCount == 2); + }); + } +}; - struct ShaderCacheTestEviction : ShaderCacheTest +struct ShaderCacheTestEviction : ShaderCacheTest +{ + void runTests() { - void runTests() - { - shaderCacheDesc.maxEntryCount = 2; + shaderCacheDesc.maxEntryCount = 2; - // Load shader A & B. Cache is cold and we expect 2 misses. - runStep( - [this]() - { - SLANG_CHECK(runComputePipeline(computeShaderA, { 1.f, 2.f, 3.f, 4.f })); - SLANG_CHECK(runComputePipeline(computeShaderB, { 2.f, 3.f, 4.f, 5.f })); - - SLANG_CHECK(getStats().missCount == 2); - SLANG_CHECK(getStats().hitCount == 0); - SLANG_CHECK(getStats().entryCount == 2); - } - ); - - // Load shader A & B. Cache is hot and we expect 2 hits. - runStep( - [this]() - { - SLANG_CHECK(runComputePipeline(computeShaderA, { 1.f, 2.f, 3.f, 4.f })); - SLANG_CHECK(runComputePipeline(computeShaderB, { 2.f, 3.f, 4.f, 5.f })); + // Load shader A & B. Cache is cold and we expect 2 misses. + runStep( + [this]() + { + SLANG_CHECK(runComputePipeline(computeShaderA, {1.f, 2.f, 3.f, 4.f})); + SLANG_CHECK(runComputePipeline(computeShaderB, {2.f, 3.f, 4.f, 5.f})); - SLANG_CHECK(getStats().missCount == 0); - SLANG_CHECK(getStats().hitCount == 2); - SLANG_CHECK(getStats().entryCount == 2); - } - ); + SLANG_CHECK(getStats().missCount == 2); + SLANG_CHECK(getStats().hitCount == 0); + SLANG_CHECK(getStats().entryCount == 2); + }); - // Load shader C. Cache is cold and we expect 1 miss. - // This will evict the least frequently used entry (shader A). - // We expect 2 entries in the cache (shader B & C). - runStep( - [this]() - { - SLANG_CHECK(runComputePipeline(computeShaderC, { 3.f, 4.f, 5.f, 6.f })); + // Load shader A & B. Cache is hot and we expect 2 hits. + runStep( + [this]() + { + SLANG_CHECK(runComputePipeline(computeShaderA, {1.f, 2.f, 3.f, 4.f})); + SLANG_CHECK(runComputePipeline(computeShaderB, {2.f, 3.f, 4.f, 5.f})); + + SLANG_CHECK(getStats().missCount == 0); + SLANG_CHECK(getStats().hitCount == 2); + SLANG_CHECK(getStats().entryCount == 2); + }); + + // Load shader C. Cache is cold and we expect 1 miss. + // This will evict the least frequently used entry (shader A). + // We expect 2 entries in the cache (shader B & C). + runStep( + [this]() + { + SLANG_CHECK(runComputePipeline(computeShaderC, {3.f, 4.f, 5.f, 6.f})); - SLANG_CHECK(getStats().missCount == 1); - SLANG_CHECK(getStats().hitCount == 0); - SLANG_CHECK(getStats().entryCount == 2); - } - ); + SLANG_CHECK(getStats().missCount == 1); + SLANG_CHECK(getStats().hitCount == 0); + SLANG_CHECK(getStats().entryCount == 2); + }); - // Load shader C. Cache is hot and we expect 1 hit. - runStep( - [this]() - { - SLANG_CHECK(runComputePipeline(computeShaderC, { 3.f, 4.f, 5.f, 6.f })); + // Load shader C. Cache is hot and we expect 1 hit. + runStep( + [this]() + { + SLANG_CHECK(runComputePipeline(computeShaderC, {3.f, 4.f, 5.f, 6.f})); - SLANG_CHECK(getStats().missCount == 0); - SLANG_CHECK(getStats().hitCount == 1); - SLANG_CHECK(getStats().entryCount == 2); - } - ); + SLANG_CHECK(getStats().missCount == 0); + SLANG_CHECK(getStats().hitCount == 1); + SLANG_CHECK(getStats().entryCount == 2); + }); - // Load shader B. Cache is hot and we expect 1 hit. - runStep( - [this]() - { - SLANG_CHECK(runComputePipeline(computeShaderB, { 2.f, 3.f, 4.f, 5.f })); + // Load shader B. Cache is hot and we expect 1 hit. + runStep( + [this]() + { + SLANG_CHECK(runComputePipeline(computeShaderB, {2.f, 3.f, 4.f, 5.f})); - SLANG_CHECK(getStats().missCount == 0); - SLANG_CHECK(getStats().hitCount == 1); - SLANG_CHECK(getStats().entryCount == 2); - } - ); + SLANG_CHECK(getStats().missCount == 0); + SLANG_CHECK(getStats().hitCount == 1); + SLANG_CHECK(getStats().entryCount == 2); + }); - // Load shader A. Cache is cold and we expect 1 miss. - runStep( - [this]() - { - SLANG_CHECK(runComputePipeline(computeShaderA, { 1.f, 2.f, 3.f, 4.f })); + // Load shader A. Cache is cold and we expect 1 miss. + runStep( + [this]() + { + SLANG_CHECK(runComputePipeline(computeShaderA, {1.f, 2.f, 3.f, 4.f})); - SLANG_CHECK(getStats().missCount == 1); - SLANG_CHECK(getStats().hitCount == 0); - SLANG_CHECK(getStats().entryCount == 2); - } - ); - } - }; + SLANG_CHECK(getStats().missCount == 1); + SLANG_CHECK(getStats().hitCount == 0); + SLANG_CHECK(getStats().entryCount == 2); + }); + } +}; - // Similar to ShaderCacheTestEntryPoint but with a source file containing a vertex and fragment shader. - struct ShaderCacheTestGraphics : ShaderCacheTest +// Similar to ShaderCacheTestEntryPoint but with a source file containing a vertex and fragment +// shader. +struct ShaderCacheTestGraphics : ShaderCacheTest +{ + struct Vertex { - struct Vertex - { - float position[3]; - }; - - static const int kWidth = 256; - static const int kHeight = 256; - static const Format format = Format::R32G32B32A32_FLOAT; - - ComPtr vertexBuffer; - ComPtr colorBuffer; - ComPtr inputLayout; - ComPtr framebufferLayout; - ComPtr renderPass; - ComPtr framebuffer; - - ComPtr createVertexBuffer(IDevice* device) - { - const Vertex vertices[] = { - { 0, 0, 0.5 }, - { 1, 0, 0.5 }, - { 0, 1, 0.5 }, - }; - - IBufferResource::Desc vertexBufferDesc; - vertexBufferDesc.type = IResource::Type::Buffer; - vertexBufferDesc.sizeInBytes = sizeof(vertices); - vertexBufferDesc.defaultState = ResourceState::VertexBuffer; - vertexBufferDesc.allowedStates = ResourceState::VertexBuffer; - ComPtr vertexBuffer = device->createBufferResource(vertexBufferDesc, vertices); - SLANG_CHECK_ABORT(vertexBuffer != nullptr); - return vertexBuffer; - } - - ComPtr createColorBuffer(IDevice* device) - { - gfx::ITextureResource::Desc colorBufferDesc; - colorBufferDesc.type = IResource::Type::Texture2D; - colorBufferDesc.size.width = kWidth; - colorBufferDesc.size.height = kHeight; - colorBufferDesc.size.depth = 1; - colorBufferDesc.numMipLevels = 1; - colorBufferDesc.format = format; - colorBufferDesc.defaultState = ResourceState::RenderTarget; - colorBufferDesc.allowedStates = { ResourceState::RenderTarget, ResourceState::CopySource }; - ComPtr colorBuffer = device->createTextureResource(colorBufferDesc, nullptr); - SLANG_CHECK_ABORT(colorBuffer != nullptr); - return colorBuffer; - } - - void createGraphicsResources() - { - VertexStreamDesc vertexStreams[] = { - { sizeof(Vertex), InputSlotClass::PerVertex, 0 }, - }; - - InputElementDesc inputElements[] = { - // Vertex buffer data - { "POSITION", 0, Format::R32G32B32_FLOAT, offsetof(Vertex, position), 0 }, - }; - IInputLayout::Desc inputLayoutDesc = {}; - inputLayoutDesc.inputElementCount = SLANG_COUNT_OF(inputElements); - inputLayoutDesc.inputElements = inputElements; - inputLayoutDesc.vertexStreamCount = SLANG_COUNT_OF(vertexStreams); - inputLayoutDesc.vertexStreams = vertexStreams; - inputLayout = device->createInputLayout(inputLayoutDesc); - SLANG_CHECK_ABORT(inputLayout != nullptr); - - vertexBuffer = createVertexBuffer(device); - colorBuffer = createColorBuffer(device); - - IFramebufferLayout::TargetLayout targetLayout; - targetLayout.format = format; - targetLayout.sampleCount = 1; - - IFramebufferLayout::Desc framebufferLayoutDesc; - framebufferLayoutDesc.renderTargetCount = 1; - framebufferLayoutDesc.renderTargets = &targetLayout; - framebufferLayout = device->createFramebufferLayout(framebufferLayoutDesc); - SLANG_CHECK_ABORT(framebufferLayout != nullptr); - - IRenderPassLayout::Desc renderPassDesc = {}; - renderPassDesc.framebufferLayout = framebufferLayout; - renderPassDesc.renderTargetCount = 1; - IRenderPassLayout::TargetAccessDesc renderTargetAccess = {}; - renderTargetAccess.loadOp = IRenderPassLayout::TargetLoadOp::Clear; - renderTargetAccess.storeOp = IRenderPassLayout::TargetStoreOp::Store; - renderTargetAccess.initialState = ResourceState::RenderTarget; - renderTargetAccess.finalState = ResourceState::CopySource; - renderPassDesc.renderTargetAccess = &renderTargetAccess; - GFX_CHECK_CALL_ABORT(device->createRenderPassLayout(renderPassDesc, renderPass.writeRef())); - - gfx::IResourceView::Desc colorBufferViewDesc; - memset(&colorBufferViewDesc, 0, sizeof(colorBufferViewDesc)); - colorBufferViewDesc.format = format; - colorBufferViewDesc.renderTarget.shape = gfx::IResource::Type::Texture2D; - colorBufferViewDesc.type = gfx::IResourceView::Type::RenderTarget; - auto rtv = device->createTextureView(colorBuffer, colorBufferViewDesc); - - gfx::IFramebuffer::Desc framebufferDesc; - framebufferDesc.renderTargetCount = 1; - framebufferDesc.depthStencilView = nullptr; - framebufferDesc.renderTargetViews = rtv.readRef(); - framebufferDesc.layout = framebufferLayout; - GFX_CHECK_CALL_ABORT(device->createFramebuffer(framebufferDesc, framebuffer.writeRef())); - } - - void freeGraphicsResources() - { - inputLayout = nullptr; - framebufferLayout = nullptr; - renderPass = nullptr; - framebuffer = nullptr; - vertexBuffer = nullptr; - colorBuffer = nullptr; - pipelineState = nullptr; - } - - void createGraphicsPipeline() - { - ComPtr shaderProgram; - slang::ProgramLayout* slangReflection; - GFX_CHECK_CALL_ABORT( - loadGraphicsProgram(device, shaderProgram, "shader-cache-graphics", "vertexMain", "fragmentMain", slangReflection)); - - GraphicsPipelineStateDesc pipelineDesc = {}; - pipelineDesc.program = shaderProgram.get(); - pipelineDesc.inputLayout = inputLayout; - pipelineDesc.framebufferLayout = framebufferLayout; - pipelineDesc.depthStencil.depthTestEnable = false; - pipelineDesc.depthStencil.depthWriteEnable = false; - GFX_CHECK_CALL_ABORT( - device->createGraphicsPipelineState(pipelineDesc, pipelineState.writeRef())); - } - - void dispatchGraphicsPipeline() - { - ComPtr transientHeap; - ITransientResourceHeap::Desc transientHeapDesc = {}; - transientHeapDesc.constantBufferSize = 4096; - GFX_CHECK_CALL_ABORT( - device->createTransientResourceHeap(transientHeapDesc, transientHeap.writeRef())); - - ICommandQueue::Desc queueDesc = { ICommandQueue::QueueType::Graphics }; - auto queue = device->createCommandQueue(queueDesc); - auto commandBuffer = transientHeap->createCommandBuffer(); - - auto encoder = commandBuffer->encodeRenderCommands(renderPass, framebuffer); - auto rootObject = encoder->bindPipeline(pipelineState); - - gfx::Viewport viewport = {}; - viewport.maxZ = 1.0f; - viewport.extentX = (float)kWidth; - viewport.extentY = (float)kHeight; - encoder->setViewportAndScissor(viewport); - - encoder->setVertexBuffer(0, vertexBuffer); - encoder->setPrimitiveTopology(PrimitiveTopology::TriangleList); - - encoder->draw(3); - encoder->endEncoding(); - commandBuffer->close(); - queue->executeCommandBuffer(commandBuffer); - queue->waitOnHost(); - } - - void runGraphicsPipeline() - { - createGraphicsResources(); - createGraphicsPipeline(); - dispatchGraphicsPipeline(); - freeGraphicsResources(); - } - - void runTests() - { - // Cache is cold and we expect 2 misses (2 entry points). - runStep( - [this]() - { - runGraphicsPipeline(); - - SLANG_CHECK(getStats().missCount == 2); - SLANG_CHECK(getStats().hitCount == 0); - SLANG_CHECK(getStats().entryCount == 2); - } - ); - - // Cache is hot and we expect 2 hits. - runStep( - [this]() - { - runGraphicsPipeline(); - - SLANG_CHECK(getStats().missCount == 0); - SLANG_CHECK(getStats().hitCount == 2); - SLANG_CHECK(getStats().entryCount == 2); - } - ); - } + float position[3]; }; - // Similar to ShaderCacheTestGraphics but with two separate shader files for the vertex and fragment shaders. - struct ShaderCacheTestGraphicsSplit : ShaderCacheTestGraphics - { - void createGraphicsPipeline() - { - ComPtr slangSession; - GFX_CHECK_CALL_ABORT(device->getSlangSession(slangSession.writeRef())); - slang::IModule* vertexModule = slangSession->loadModule("shader-cache-graphics-vertex"); - SLANG_CHECK_ABORT(vertexModule); - slang::IModule* fragmentModule = slangSession->loadModule("shader-cache-graphics-fragment"); - SLANG_CHECK_ABORT(fragmentModule); - - ComPtr vertexEntryPoint; - GFX_CHECK_CALL_ABORT( - vertexModule->findEntryPointByName("main", vertexEntryPoint.writeRef())); - - ComPtr fragmentEntryPoint; - GFX_CHECK_CALL_ABORT( - fragmentModule->findEntryPointByName("main", fragmentEntryPoint.writeRef())); - - Slang::List componentTypes; - componentTypes.add(vertexModule); - componentTypes.add(fragmentModule); - - Slang::ComPtr composedProgram; - GFX_CHECK_CALL_ABORT( - slangSession->createCompositeComponentType( - componentTypes.getBuffer(), - componentTypes.getCount(), - composedProgram.writeRef())); - - slang::ProgramLayout* slangReflection = composedProgram->getLayout(); - - Slang::List entryPoints; - entryPoints.add(vertexEntryPoint); - entryPoints.add(fragmentEntryPoint); - - gfx::IShaderProgram::Desc programDesc = {}; - programDesc.slangGlobalScope = composedProgram.get(); - programDesc.linkingStyle = gfx::IShaderProgram::LinkingStyle::SeparateEntryPointCompilation; - programDesc.entryPointCount = 2; - programDesc.slangEntryPoints = entryPoints.getBuffer(); - - ComPtr shaderProgram = device->createProgram(programDesc); - - GraphicsPipelineStateDesc pipelineDesc = {}; - pipelineDesc.program = shaderProgram.get(); - pipelineDesc.inputLayout = inputLayout; - pipelineDesc.framebufferLayout = framebufferLayout; - pipelineDesc.depthStencil.depthTestEnable = false; - pipelineDesc.depthStencil.depthWriteEnable = false; - GFX_CHECK_CALL_ABORT( - device->createGraphicsPipelineState(pipelineDesc, pipelineState.writeRef())); - } - - void runGraphicsPipeline() - { - createGraphicsResources(); - createGraphicsPipeline(); - dispatchGraphicsPipeline(); - freeGraphicsResources(); - } - - void runTests() - { - // Cache is cold and we expect 2 misses (2 entry points). - runStep( - [this]() - { - runGraphicsPipeline(); + static const int kWidth = 256; + static const int kHeight = 256; + static const Format format = Format::R32G32B32A32_FLOAT; - SLANG_CHECK(getStats().missCount == 2); - SLANG_CHECK(getStats().hitCount == 0); - SLANG_CHECK(getStats().entryCount == 2); - } - ); - - // Cache is hot and we expect 2 hits. - runStep( - [this]() - { - runGraphicsPipeline(); - - SLANG_CHECK(getStats().missCount == 0); - SLANG_CHECK(getStats().hitCount == 2); - SLANG_CHECK(getStats().entryCount == 2); - } - ); } - }; + ComPtr vertexBuffer; + ComPtr colorBuffer; + ComPtr inputLayout; + ComPtr framebufferLayout; + ComPtr renderPass; + ComPtr framebuffer; - template - void runTest(UnitTestContext* context, Slang::RenderApiFlag::Enum api) + ComPtr createVertexBuffer(IDevice* device) { - T test; - test.run(context, api); - } + const Vertex vertices[] = { + {0, 0, 0.5}, + {1, 0, 0.5}, + {0, 1, 0.5}, + }; - SLANG_UNIT_TEST(shaderCacheSourceFileD3D12) - { - runTest(unitTestContext, Slang::RenderApiFlag::D3D12); + IBufferResource::Desc vertexBufferDesc; + vertexBufferDesc.type = IResource::Type::Buffer; + vertexBufferDesc.sizeInBytes = sizeof(vertices); + vertexBufferDesc.defaultState = ResourceState::VertexBuffer; + vertexBufferDesc.allowedStates = ResourceState::VertexBuffer; + ComPtr vertexBuffer = + device->createBufferResource(vertexBufferDesc, vertices); + SLANG_CHECK_ABORT(vertexBuffer != nullptr); + return vertexBuffer; } - SLANG_UNIT_TEST(shaderCacheSourceFileVulkan) + ComPtr createColorBuffer(IDevice* device) { - runTest(unitTestContext, Slang::RenderApiFlag::Vulkan); + gfx::ITextureResource::Desc colorBufferDesc; + colorBufferDesc.type = IResource::Type::Texture2D; + colorBufferDesc.size.width = kWidth; + colorBufferDesc.size.height = kHeight; + colorBufferDesc.size.depth = 1; + colorBufferDesc.numMipLevels = 1; + colorBufferDesc.format = format; + colorBufferDesc.defaultState = ResourceState::RenderTarget; + colorBufferDesc.allowedStates = {ResourceState::RenderTarget, ResourceState::CopySource}; + ComPtr colorBuffer = + device->createTextureResource(colorBufferDesc, nullptr); + SLANG_CHECK_ABORT(colorBuffer != nullptr); + return colorBuffer; } - SLANG_UNIT_TEST(shaderCacheSourceStringD3D12) + void createGraphicsResources() { - runTest(unitTestContext, Slang::RenderApiFlag::D3D12); - } + VertexStreamDesc vertexStreams[] = { + {sizeof(Vertex), InputSlotClass::PerVertex, 0}, + }; - SLANG_UNIT_TEST(shaderCacheSourceStringVulkan) - { - runTest(unitTestContext, Slang::RenderApiFlag::Vulkan); + InputElementDesc inputElements[] = { + // Vertex buffer data + {"POSITION", 0, Format::R32G32B32_FLOAT, offsetof(Vertex, position), 0}, + }; + IInputLayout::Desc inputLayoutDesc = {}; + inputLayoutDesc.inputElementCount = SLANG_COUNT_OF(inputElements); + inputLayoutDesc.inputElements = inputElements; + inputLayoutDesc.vertexStreamCount = SLANG_COUNT_OF(vertexStreams); + inputLayoutDesc.vertexStreams = vertexStreams; + inputLayout = device->createInputLayout(inputLayoutDesc); + SLANG_CHECK_ABORT(inputLayout != nullptr); + + vertexBuffer = createVertexBuffer(device); + colorBuffer = createColorBuffer(device); + + IFramebufferLayout::TargetLayout targetLayout; + targetLayout.format = format; + targetLayout.sampleCount = 1; + + IFramebufferLayout::Desc framebufferLayoutDesc; + framebufferLayoutDesc.renderTargetCount = 1; + framebufferLayoutDesc.renderTargets = &targetLayout; + framebufferLayout = device->createFramebufferLayout(framebufferLayoutDesc); + SLANG_CHECK_ABORT(framebufferLayout != nullptr); + + IRenderPassLayout::Desc renderPassDesc = {}; + renderPassDesc.framebufferLayout = framebufferLayout; + renderPassDesc.renderTargetCount = 1; + IRenderPassLayout::TargetAccessDesc renderTargetAccess = {}; + renderTargetAccess.loadOp = IRenderPassLayout::TargetLoadOp::Clear; + renderTargetAccess.storeOp = IRenderPassLayout::TargetStoreOp::Store; + renderTargetAccess.initialState = ResourceState::RenderTarget; + renderTargetAccess.finalState = ResourceState::CopySource; + renderPassDesc.renderTargetAccess = &renderTargetAccess; + GFX_CHECK_CALL_ABORT(device->createRenderPassLayout(renderPassDesc, renderPass.writeRef())); + + gfx::IResourceView::Desc colorBufferViewDesc; + memset(&colorBufferViewDesc, 0, sizeof(colorBufferViewDesc)); + colorBufferViewDesc.format = format; + colorBufferViewDesc.renderTarget.shape = gfx::IResource::Type::Texture2D; + colorBufferViewDesc.type = gfx::IResourceView::Type::RenderTarget; + auto rtv = device->createTextureView(colorBuffer, colorBufferViewDesc); + + gfx::IFramebuffer::Desc framebufferDesc; + framebufferDesc.renderTargetCount = 1; + framebufferDesc.depthStencilView = nullptr; + framebufferDesc.renderTargetViews = rtv.readRef(); + framebufferDesc.layout = framebufferLayout; + GFX_CHECK_CALL_ABORT(device->createFramebuffer(framebufferDesc, framebuffer.writeRef())); } - SLANG_UNIT_TEST(shaderCacheEntryPointD3D12) + void freeGraphicsResources() { - runTest(unitTestContext, Slang::RenderApiFlag::D3D12); + inputLayout = nullptr; + framebufferLayout = nullptr; + renderPass = nullptr; + framebuffer = nullptr; + vertexBuffer = nullptr; + colorBuffer = nullptr; + pipelineState = nullptr; } - SLANG_UNIT_TEST(shaderCacheEntryPointVulkan) + void createGraphicsPipeline() { - runTest(unitTestContext, Slang::RenderApiFlag::Vulkan); + ComPtr shaderProgram; + slang::ProgramLayout* slangReflection; + GFX_CHECK_CALL_ABORT(loadGraphicsProgram( + device, + shaderProgram, + "shader-cache-graphics", + "vertexMain", + "fragmentMain", + slangReflection)); + + GraphicsPipelineStateDesc pipelineDesc = {}; + pipelineDesc.program = shaderProgram.get(); + pipelineDesc.inputLayout = inputLayout; + pipelineDesc.framebufferLayout = framebufferLayout; + pipelineDesc.depthStencil.depthTestEnable = false; + pipelineDesc.depthStencil.depthWriteEnable = false; + GFX_CHECK_CALL_ABORT( + device->createGraphicsPipelineState(pipelineDesc, pipelineState.writeRef())); } - SLANG_UNIT_TEST(shaderCacheImportIncludeD3D12) + void dispatchGraphicsPipeline() { - runTest(unitTestContext, Slang::RenderApiFlag::D3D12); + ComPtr transientHeap; + ITransientResourceHeap::Desc transientHeapDesc = {}; + transientHeapDesc.constantBufferSize = 4096; + GFX_CHECK_CALL_ABORT( + device->createTransientResourceHeap(transientHeapDesc, transientHeap.writeRef())); + + ICommandQueue::Desc queueDesc = {ICommandQueue::QueueType::Graphics}; + auto queue = device->createCommandQueue(queueDesc); + auto commandBuffer = transientHeap->createCommandBuffer(); + + auto encoder = commandBuffer->encodeRenderCommands(renderPass, framebuffer); + auto rootObject = encoder->bindPipeline(pipelineState); + + gfx::Viewport viewport = {}; + viewport.maxZ = 1.0f; + viewport.extentX = (float)kWidth; + viewport.extentY = (float)kHeight; + encoder->setViewportAndScissor(viewport); + + encoder->setVertexBuffer(0, vertexBuffer); + encoder->setPrimitiveTopology(PrimitiveTopology::TriangleList); + + encoder->draw(3); + encoder->endEncoding(); + commandBuffer->close(); + queue->executeCommandBuffer(commandBuffer); + queue->waitOnHost(); } - SLANG_UNIT_TEST(shaderCacheImportIncludeVulkan) + void runGraphicsPipeline() { - runTest(unitTestContext, Slang::RenderApiFlag::Vulkan); + createGraphicsResources(); + createGraphicsPipeline(); + dispatchGraphicsPipeline(); + freeGraphicsResources(); } - SLANG_UNIT_TEST(shaderCacheSpecializationD3D12) + void runTests() { - runTest(unitTestContext, Slang::RenderApiFlag::D3D12); - } + // Cache is cold and we expect 2 misses (2 entry points). + runStep( + [this]() + { + runGraphicsPipeline(); - SLANG_UNIT_TEST(shaderCacheSpecializationVulkan) - { - runTest(unitTestContext, Slang::RenderApiFlag::Vulkan); - } + SLANG_CHECK(getStats().missCount == 2); + SLANG_CHECK(getStats().hitCount == 0); + SLANG_CHECK(getStats().entryCount == 2); + }); - SLANG_UNIT_TEST(shaderCacheEvictionD3D12) - { - runTest(unitTestContext, Slang::RenderApiFlag::D3D12); - } + // Cache is hot and we expect 2 hits. + runStep( + [this]() + { + runGraphicsPipeline(); - SLANG_UNIT_TEST(shaderCacheEvictionVulkan) - { - runTest(unitTestContext, Slang::RenderApiFlag::Vulkan); + SLANG_CHECK(getStats().missCount == 0); + SLANG_CHECK(getStats().hitCount == 2); + SLANG_CHECK(getStats().entryCount == 2); + }); } +}; - SLANG_UNIT_TEST(shaderCacheGraphicsD3D12) +// Similar to ShaderCacheTestGraphics but with two separate shader files for the vertex and fragment +// shaders. +struct ShaderCacheTestGraphicsSplit : ShaderCacheTestGraphics +{ + void createGraphicsPipeline() { - runTest(unitTestContext, Slang::RenderApiFlag::D3D12); + ComPtr slangSession; + GFX_CHECK_CALL_ABORT(device->getSlangSession(slangSession.writeRef())); + slang::IModule* vertexModule = slangSession->loadModule("shader-cache-graphics-vertex"); + SLANG_CHECK_ABORT(vertexModule); + slang::IModule* fragmentModule = slangSession->loadModule("shader-cache-graphics-fragment"); + SLANG_CHECK_ABORT(fragmentModule); + + ComPtr vertexEntryPoint; + GFX_CHECK_CALL_ABORT( + vertexModule->findEntryPointByName("main", vertexEntryPoint.writeRef())); + + ComPtr fragmentEntryPoint; + GFX_CHECK_CALL_ABORT( + fragmentModule->findEntryPointByName("main", fragmentEntryPoint.writeRef())); + + Slang::List componentTypes; + componentTypes.add(vertexModule); + componentTypes.add(fragmentModule); + + Slang::ComPtr composedProgram; + GFX_CHECK_CALL_ABORT(slangSession->createCompositeComponentType( + componentTypes.getBuffer(), + componentTypes.getCount(), + composedProgram.writeRef())); + + slang::ProgramLayout* slangReflection = composedProgram->getLayout(); + + Slang::List entryPoints; + entryPoints.add(vertexEntryPoint); + entryPoints.add(fragmentEntryPoint); + + gfx::IShaderProgram::Desc programDesc = {}; + programDesc.slangGlobalScope = composedProgram.get(); + programDesc.linkingStyle = gfx::IShaderProgram::LinkingStyle::SeparateEntryPointCompilation; + programDesc.entryPointCount = 2; + programDesc.slangEntryPoints = entryPoints.getBuffer(); + + ComPtr shaderProgram = device->createProgram(programDesc); + + GraphicsPipelineStateDesc pipelineDesc = {}; + pipelineDesc.program = shaderProgram.get(); + pipelineDesc.inputLayout = inputLayout; + pipelineDesc.framebufferLayout = framebufferLayout; + pipelineDesc.depthStencil.depthTestEnable = false; + pipelineDesc.depthStencil.depthWriteEnable = false; + GFX_CHECK_CALL_ABORT( + device->createGraphicsPipelineState(pipelineDesc, pipelineState.writeRef())); } - SLANG_UNIT_TEST(shaderCacheGraphicsVulkan) + void runGraphicsPipeline() { - runTest(unitTestContext, Slang::RenderApiFlag::Vulkan); + createGraphicsResources(); + createGraphicsPipeline(); + dispatchGraphicsPipeline(); + freeGraphicsResources(); } - SLANG_UNIT_TEST(shaderCacheGraphicsSplitD3D12) + void runTests() { - runTest(unitTestContext, Slang::RenderApiFlag::D3D12); - } + // Cache is cold and we expect 2 misses (2 entry points). + runStep( + [this]() + { + runGraphicsPipeline(); - SLANG_UNIT_TEST(shaderCacheGraphicsSplitVulkan) - { - runTest(unitTestContext, Slang::RenderApiFlag::Vulkan); + SLANG_CHECK(getStats().missCount == 2); + SLANG_CHECK(getStats().hitCount == 0); + SLANG_CHECK(getStats().entryCount == 2); + }); + + // Cache is hot and we expect 2 hits. + runStep( + [this]() + { + runGraphicsPipeline(); + + SLANG_CHECK(getStats().missCount == 0); + SLANG_CHECK(getStats().hitCount == 2); + SLANG_CHECK(getStats().entryCount == 2); + }); } +}; + +template +void runTest(UnitTestContext* context, Slang::RenderApiFlag::Enum api) +{ + T test; + test.run(context, api); +} + +SLANG_UNIT_TEST(shaderCacheSourceFileD3D12) +{ + runTest(unitTestContext, Slang::RenderApiFlag::D3D12); +} + +SLANG_UNIT_TEST(shaderCacheSourceFileVulkan) +{ + runTest(unitTestContext, Slang::RenderApiFlag::Vulkan); +} + +SLANG_UNIT_TEST(shaderCacheSourceStringD3D12) +{ + runTest(unitTestContext, Slang::RenderApiFlag::D3D12); +} + +SLANG_UNIT_TEST(shaderCacheSourceStringVulkan) +{ + runTest(unitTestContext, Slang::RenderApiFlag::Vulkan); +} + +SLANG_UNIT_TEST(shaderCacheEntryPointD3D12) +{ + runTest(unitTestContext, Slang::RenderApiFlag::D3D12); +} + +SLANG_UNIT_TEST(shaderCacheEntryPointVulkan) +{ + runTest(unitTestContext, Slang::RenderApiFlag::Vulkan); +} + +SLANG_UNIT_TEST(shaderCacheImportIncludeD3D12) +{ + runTest(unitTestContext, Slang::RenderApiFlag::D3D12); +} + +SLANG_UNIT_TEST(shaderCacheImportIncludeVulkan) +{ + runTest(unitTestContext, Slang::RenderApiFlag::Vulkan); +} + +SLANG_UNIT_TEST(shaderCacheSpecializationD3D12) +{ + runTest(unitTestContext, Slang::RenderApiFlag::D3D12); +} + +SLANG_UNIT_TEST(shaderCacheSpecializationVulkan) +{ + runTest(unitTestContext, Slang::RenderApiFlag::Vulkan); +} + +SLANG_UNIT_TEST(shaderCacheEvictionD3D12) +{ + runTest(unitTestContext, Slang::RenderApiFlag::D3D12); +} + +SLANG_UNIT_TEST(shaderCacheEvictionVulkan) +{ + runTest(unitTestContext, Slang::RenderApiFlag::Vulkan); +} + +SLANG_UNIT_TEST(shaderCacheGraphicsD3D12) +{ + runTest(unitTestContext, Slang::RenderApiFlag::D3D12); +} + +SLANG_UNIT_TEST(shaderCacheGraphicsVulkan) +{ + runTest(unitTestContext, Slang::RenderApiFlag::Vulkan); +} + +SLANG_UNIT_TEST(shaderCacheGraphicsSplitD3D12) +{ + runTest(unitTestContext, Slang::RenderApiFlag::D3D12); +} + +SLANG_UNIT_TEST(shaderCacheGraphicsSplitVulkan) +{ + runTest(unitTestContext, Slang::RenderApiFlag::Vulkan); } +} // namespace gfx_test -- cgit v1.2.3