diff options
| author | kaizhangNV <149626564+kaizhangNV@users.noreply.github.com> | 2024-08-22 12:18:44 -0500 |
|---|---|---|
| committer | GitHub <noreply@github.com> | 2024-08-22 10:18:44 -0700 |
| commit | b5bb82411cc6101b66283f7d0abca7ceb58aa2f6 (patch) | |
| tree | 192a522513da30b1a92e6c319693aa541729bea9 | |
| parent | a3fbd8f060317332eae951d4a376e830d058469e (diff) | |
Feature/capture unit test (#4898)
* record/replay: Add tests
Modify the hello-world example to generate the hash code for the
entry point spirv code, so that we can compare it with replaying
the example.
Add the test script to run the example and compare the hash code
with replaying it.
* Check nullptr for out Diagnostics
We need to check whether the output Diagnostics is a nullptr,
because it's allowed.
* Fix the double free pointers
* Add triangle example as the new test for record-replay
Change the example base to add the offline rendering path
because we don't want to display anything when we're in the
test mode.
This change involves introducing a TestBase that will parse
the command line option. It will decide whether we are in
the test mode.
Disable all the swapchain and windows related creation, instead
we will only create one single framebuffer for the render target.
* Address comments
TODO:
In the follow up patches, I will add more tests and integrate the test flow into slang-unit-test.
24 files changed, 384 insertions, 135 deletions
diff --git a/examples/example-base/example-base.cpp b/examples/example-base/example-base.cpp index 82d474f06..fb11105f5 100644 --- a/examples/example-base/example-base.cpp +++ b/examples/example-base/example-base.cpp @@ -17,19 +17,6 @@ Slang::Result WindowedAppBase::initializeBase( int height, DeviceType deviceType) { - // Create a window for our application to render into. - // - platform::WindowDesc windowDesc; - windowDesc.title = title; - windowDesc.width = width; - windowDesc.height = height; - windowWidth = width; - windowHeight = height; - windowDesc.style = platform::WindowStyle::Default; - gWindow = platform::Application::createWindow(windowDesc); - gWindow->events.mainLoop = [this]() { mainLoop(); }; - gWindow->events.sizeChanged = Slang::Action<>(this, &WindowedAppBase::windowSizeChanged); - // Initialize the rendering layer. #ifdef _DEBUG // Enable debug layer in debug config. @@ -41,26 +28,44 @@ Slang::Result WindowedAppBase::initializeBase( if (SLANG_FAILED(res)) return res; - auto deviceInfo = gDevice->getDeviceInfo(); - Slang::StringBuilder titleSb; - titleSb << title << " (" << deviceInfo.apiName << ": " << deviceInfo.adapterName << ")"; - gWindow->setText(titleSb.getBuffer()); - ICommandQueue::Desc queueDesc = {}; queueDesc.type = ICommandQueue::QueueType::Graphics; gQueue = gDevice->createCommandQueue(queueDesc); - // Create swapchain and framebuffers. - gfx::ISwapchain::Desc swapchainDesc = {}; - swapchainDesc.format = gfx::Format::R8G8B8A8_UNORM; - swapchainDesc.width = width; - swapchainDesc.height = height; - swapchainDesc.imageCount = kSwapchainImageCount; - swapchainDesc.queue = gQueue; - gfx::WindowHandle windowHandle = gWindow->getNativeHandle().convert<gfx::WindowHandle>(); - gSwapchain = gDevice->createSwapchain(swapchainDesc, windowHandle); - - IFramebufferLayout::TargetLayout renderTargetLayout = {gSwapchain->getDesc().format, 1}; + windowWidth = width; + windowHeight = height; + // Do not create swapchain and windows in test mode, because there won't be any display. + if (!isTestMode()) + { + // Create a window for our application to render into. + // + platform::WindowDesc windowDesc; + windowDesc.title = title; + windowDesc.width = width; + windowDesc.height = height; + windowDesc.style = platform::WindowStyle::Default; + gWindow = platform::Application::createWindow(windowDesc); + gWindow->events.mainLoop = [this]() { mainLoop(); }; + gWindow->events.sizeChanged = Slang::Action<>(this, &WindowedAppBase::windowSizeChanged); + + auto deviceInfo = gDevice->getDeviceInfo(); + Slang::StringBuilder titleSb; + titleSb << title << " (" << deviceInfo.apiName << ": " << deviceInfo.adapterName << ")"; + gWindow->setText(titleSb.getBuffer()); + + // Create swapchain and framebuffers. + gfx::ISwapchain::Desc swapchainDesc = {}; + swapchainDesc.format = gfx::Format::R8G8B8A8_UNORM; + swapchainDesc.width = width; + swapchainDesc.height = height; + swapchainDesc.imageCount = kSwapchainImageCount; + swapchainDesc.queue = gQueue; + gfx::WindowHandle windowHandle = gWindow->getNativeHandle().convert<gfx::WindowHandle>(); + gSwapchain = gDevice->createSwapchain(swapchainDesc, windowHandle); + createSwapchainFramebuffers(); + } + + IFramebufferLayout::TargetLayout renderTargetLayout = {gfx::Format::R8G8B8A8_UNORM, 1}; IFramebufferLayout::TargetLayout depthLayout = {gfx::Format::D32_FLOAT, 1}; IFramebufferLayout::Desc framebufferLayoutDesc; framebufferLayoutDesc.renderTargetCount = 1; @@ -69,7 +74,10 @@ Slang::Result WindowedAppBase::initializeBase( SLANG_RETURN_ON_FAIL( gDevice->createFramebufferLayout(framebufferLayoutDesc, gFramebufferLayout.writeRef())); - createSwapchainFramebuffers(); + if (isTestMode()) + { + createOfflineFramebuffers(); + } for (uint32_t i = 0; i < kSwapchainImageCount; i++) { @@ -102,23 +110,27 @@ Slang::Result WindowedAppBase::initializeBase( void WindowedAppBase::mainLoop() { int frameBufferIndex = gSwapchain->acquireNextImage(); - if (frameBufferIndex == -1) - return; gTransientHeaps[frameBufferIndex]->synchronizeAndReset(); renderFrame(frameBufferIndex); gTransientHeaps[frameBufferIndex]->finish(); } -void WindowedAppBase::createSwapchainFramebuffers() +void WindowedAppBase::offlineRender() { - gFramebuffers.clear(); - for (uint32_t i = 0; i < kSwapchainImageCount; i++) + gTransientHeaps[0]->synchronizeAndReset(); + renderFrame(0); + gTransientHeaps[0]->finish(); +} + +void WindowedAppBase::createFramebuffers(uint32_t width, uint32_t height, gfx::Format colorFormat, uint32_t frameBufferCount) +{ + for (uint32_t i = 0; i < frameBufferCount; i++) { gfx::ITextureResource::Desc depthBufferDesc; depthBufferDesc.type = IResource::Type::Texture2D; - depthBufferDesc.size.width = gSwapchain->getDesc().width; - depthBufferDesc.size.height = gSwapchain->getDesc().height; + depthBufferDesc.size.width = width; + depthBufferDesc.size.height = height; depthBufferDesc.size.depth = 1; depthBufferDesc.format = gfx::Format::D32_FLOAT; depthBufferDesc.defaultState = ResourceState::DepthWrite; @@ -127,12 +139,28 @@ void WindowedAppBase::createSwapchainFramebuffers() depthBufferDesc.optimalClearValue = &depthClearValue; ComPtr<gfx::ITextureResource> depthBufferResource = gDevice->createTextureResource(depthBufferDesc, nullptr); + ComPtr<gfx::ITextureResource> colorBuffer; - gSwapchain->getImage(i, colorBuffer.writeRef()); + if (isTestMode()) + { + gfx::ITextureResource::Desc colorBufferDesc; + colorBufferDesc.type = IResource::Type::Texture2D; + colorBufferDesc.size.width = width; + colorBufferDesc.size.height = height; + colorBufferDesc.size.depth = 1; + colorBufferDesc.format = colorFormat; + colorBufferDesc.defaultState = ResourceState::RenderTarget; + colorBufferDesc.allowedStates = ResourceStateSet(ResourceState::RenderTarget, ResourceState::CopyDestination); + colorBuffer = gDevice->createTextureResource(colorBufferDesc, nullptr); + } + else + { + gSwapchain->getImage(i, colorBuffer.writeRef()); + } gfx::IResourceView::Desc colorBufferViewDesc; memset(&colorBufferViewDesc, 0, sizeof(colorBufferViewDesc)); - colorBufferViewDesc.format = gSwapchain->getDesc().format; + colorBufferViewDesc.format = colorFormat; colorBufferViewDesc.renderTarget.shape = gfx::IResource::Type::Texture2D; colorBufferViewDesc.type = gfx::IResourceView::Type::RenderTarget; ComPtr<gfx::IResourceView> rtv = @@ -152,10 +180,24 @@ void WindowedAppBase::createSwapchainFramebuffers() framebufferDesc.renderTargetViews = rtv.readRef(); framebufferDesc.layout = gFramebufferLayout; ComPtr<gfx::IFramebuffer> frameBuffer = gDevice->createFramebuffer(framebufferDesc); + gFramebuffers.add(frameBuffer); } } +void WindowedAppBase::createOfflineFramebuffers() +{ + gFramebuffers.clear(); + createFramebuffers(windowWidth, windowHeight, gfx::Format::R8G8B8A8_UNORM, 1); +} + +void WindowedAppBase::createSwapchainFramebuffers() +{ + gFramebuffers.clear(); + createFramebuffers(gSwapchain->getDesc().width, gSwapchain->getDesc().height, + gSwapchain->getDesc().format, kSwapchainImageCount); +} + ComPtr<gfx::IResourceView> WindowedAppBase::createTextureFromFile(String fileName, int& textureWidth, int& textureHeight) { int channelsInFile = 0; diff --git a/examples/example-base/example-base.h b/examples/example-base/example-base.h index 8943323da..b97728e17 100644 --- a/examples/example-base/example-base.h +++ b/examples/example-base/example-base.h @@ -4,12 +4,13 @@ #include "tools/platform/window.h" #include "source/core/slang-basic.h" #include "source/core/slang-io.h" +#include "test-base.h" #ifdef _WIN32 void _Win32OutputDebugString(const char* str); #endif -struct WindowedAppBase +struct WindowedAppBase : public TestBase { protected: static const int kSwapchainImageCount = 2; @@ -32,8 +33,13 @@ protected: int width, int height, gfx::DeviceType deviceType = gfx::DeviceType::Default); + + void createFramebuffers(uint32_t width, uint32_t height, gfx::Format colorFormat, uint32_t frameBufferCount); void createSwapchainFramebuffers(); + void createOfflineFramebuffers(); + void mainLoop(); + Slang::ComPtr<gfx::IResourceView> createTextureFromFile(Slang::String fileName, int& textureWidth, int& textureHeight); virtual void windowSizeChanged(); @@ -42,6 +48,7 @@ protected: public: platform::Window* getWindow() { return gWindow.Ptr(); } virtual void finalize() { gQueue->waitOnHost(); } + void offlineRender(); }; struct ExampleResources { @@ -103,18 +110,26 @@ inline void diagnoseIfNeeded(slang::IBlob* diagnosticsBlob) void initDebugCallback(); template<typename TApp> -int innerMain() +int innerMain(int argc, char** argv) { initDebugCallback(); TApp app; + app.parseOption(argc, argv); if (SLANG_FAILED(app.initialize())) { return -1; } - platform::Application::run(app.getWindow()); + if (!app.isTestMode()) + { + platform::Application::run(app.getWindow()); + } + else + { + app.offlineRender(); + } app.finalize(); return 0; diff --git a/examples/example-base/test-base.cpp b/examples/example-base/test-base.cpp new file mode 100644 index 000000000..5c727aabe --- /dev/null +++ b/examples/example-base/test-base.cpp @@ -0,0 +1,50 @@ +#include "test-base.h" + +#ifdef _WIN32 +#include <windows.h> +#include <shellapi.h> +#endif + +int TestBase::parseOption(int argc, char** argv) +{ + // We only make the parse in a very loose way for only extracting the test option. +#ifdef _WIN32 + wchar_t** szArglist; + szArglist = CommandLineToArgvW(GetCommandLineW(), &argc); +#endif + + for (int i = 0; i < argc; i++) + { +#ifdef _WIN32 + if (wcscmp(szArglist[i], L"--test-mode") == 0) +#else + if (strcmp(argv[i], "--test-mode") == 0) +#endif + { + m_isTestMode = true; + } + } + return 0; +} + +void TestBase::printEntrypointHashes(int entryPointCount, int targetCount, ComPtr<slang::IComponentType>& composedProgram) +{ + for (int targetIndex = 0; targetIndex < targetCount; targetIndex++) + { + for (int entryPointIndex = 0; entryPointIndex < entryPointCount; entryPointIndex++) + { + ComPtr<slang::IBlob> entryPointHashBlob; + composedProgram->getEntryPointHash(entryPointIndex, targetIndex, entryPointHashBlob.writeRef()); + + Slang::StringBuilder strBuilder; + strBuilder << "entrypoint: "<< entryPointIndex << ", target: " << targetIndex << ", hash: "; + + uint8_t* buffer = (uint8_t*)entryPointHashBlob->getBufferPointer(); + for (size_t i = 0; i < entryPointHashBlob->getBufferSize(); i++) + { + strBuilder<<Slang::StringUtil::makeStringWithFormat("%.2X", buffer[i]); + } + fprintf(stdout, "%s\n", strBuilder.begin()); + } + } +} diff --git a/examples/example-base/test-base.h b/examples/example-base/test-base.h new file mode 100644 index 000000000..22cd70d09 --- /dev/null +++ b/examples/example-base/test-base.h @@ -0,0 +1,21 @@ +#pragma once + +#include "slang.h" +#include "slang-com-ptr.h" +#include "source/core/slang-string-util.h" + +using Slang::ComPtr; + +class TestBase { + +public: + // Parses command line options. This example only has one option for testing purpose. + int parseOption(int argc, char** argv); + + void printEntrypointHashes(int entryPointCount, int targetCount, ComPtr<slang::IComponentType>& composedProgram); + + bool isTestMode() const { return m_isTestMode; } + +private: + bool m_isTestMode = false; +}; diff --git a/examples/hello-world/main.cpp b/examples/hello-world/main.cpp index 53285ae51..87d440901 100644 --- a/examples/hello-world/main.cpp +++ b/examples/hello-world/main.cpp @@ -12,12 +12,14 @@ #include "vulkan-api.h" #include "examples/example-base/example-base.h" +#include "examples/example-base/test-base.h" +#include "source/core/slang-string-util.h" using Slang::ComPtr; static const ExampleResources resourceBase("hello-world"); -struct HelloWorldExample +struct HelloWorldExample : public TestBase { // The Vulkan functions pointers result from loading the vulkan library. VulkanAPI vkAPI; @@ -66,10 +68,11 @@ struct HelloWorldExample }; -int main() +int main(int argc, char* argv[]) { initDebugCallback(); HelloWorldExample example; + example.parseOption(argc, argv); return example.run(); } @@ -204,6 +207,11 @@ int HelloWorldExample::createComputePipelineFromShader() 0, 0, spirvCode.writeRef(), diagnosticsBlob.writeRef()); diagnoseIfNeeded(diagnosticsBlob); RETURN_ON_FAIL(result); + + if (isTestMode()) + { + printEntrypointHashes(1, 1, composedProgram); + } } // The following steps are all Vulkan API calls to create a pipeline. diff --git a/examples/triangle/main.cpp b/examples/triangle/main.cpp index 93ae1bd2e..88c55c416 100644 --- a/examples/triangle/main.cpp +++ b/examples/triangle/main.cpp @@ -187,6 +187,11 @@ gfx::Result loadShaderProgram( programDesc.slangGlobalScope = linkedProgram; SLANG_RETURN_ON_FAIL(device->createProgram(programDesc, outProgram)); + if (isTestMode()) + { + printEntrypointHashes(entryPointCount, 1, linkedProgram); + } + return SLANG_OK; } @@ -387,9 +392,12 @@ virtual void renderFrame(int frameBufferIndex) override commandBuffer->close(); gQueue->executeCommandBuffer(commandBuffer); - // With that, we are done drawing for one frame, and ready for the next. - // - gSwapchain->present(); + if (!isTestMode()) + { + // With that, we are done drawing for one frame, and ready for the next. + // + gSwapchain->present(); + } } }; diff --git a/source/slang-record-replay/record/slang-composite-component-type.cpp b/source/slang-record-replay/record/slang-composite-component-type.cpp index e3c339adb..29ac5da8d 100644 --- a/source/slang-record-replay/record/slang-composite-component-type.cpp +++ b/source/slang-record-replay/record/slang-composite-component-type.cpp @@ -15,11 +15,6 @@ namespace SlangRecord slangRecordLog(LogLevel::Verbose, "%s: %p\n", __PRETTY_FUNCTION__, componentType); } - CompositeComponentTypeRecorder::~CompositeComponentTypeRecorder() - { - m_actualCompositeComponentType->release(); - } - ISlangUnknown* CompositeComponentTypeRecorder::getInterface(const Guid& guid) { if (guid == IComponentType::getTypeGuid()) @@ -65,7 +60,7 @@ namespace SlangRecord slang::ProgramLayout* programLayout = m_actualCompositeComponentType->getLayout(targetIndex, outDiagnostics); { - recorder->recordAddress(*outDiagnostics); + recorder->recordAddress(outDiagnostics ? *outDiagnostics : nullptr); recorder->recordAddress(programLayout); m_recordManager->apendOutput(); } @@ -101,7 +96,7 @@ namespace SlangRecord { recorder->recordAddress(*outCode); - recorder->recordAddress(*outDiagnostics); + recorder->recordAddress(outDiagnostics ? *outDiagnostics : nullptr); m_recordManager->apendOutput(); } @@ -126,7 +121,7 @@ namespace SlangRecord { recorder->recordAddress(*outCode); - recorder->recordAddress(*outDiagnostics); + recorder->recordAddress(outDiagnostics ? *outDiagnostics : nullptr); m_recordManager->apendOutput(); } @@ -201,7 +196,7 @@ namespace SlangRecord { recorder->recordAddress(*outSpecializedComponentType); - recorder->recordAddress(*outDiagnostics); + recorder->recordAddress(outDiagnostics ? *outDiagnostics : nullptr); m_recordManager->apendOutput(); } @@ -224,7 +219,7 @@ namespace SlangRecord { recorder->recordAddress(*outLinkedComponentType); - recorder->recordAddress(*outDiagnostics); + recorder->recordAddress(outDiagnostics ? *outDiagnostics : nullptr); m_recordManager->apendOutput(); } @@ -251,7 +246,7 @@ namespace SlangRecord { recorder->recordAddress(*outSharedLibrary); - recorder->recordAddress(*outDiagnostics); + recorder->recordAddress(outDiagnostics ? *outDiagnostics : nullptr); m_recordManager->apendOutput(); } @@ -300,7 +295,7 @@ namespace SlangRecord { recorder->recordAddress(*outLinkedComponentType); - recorder->recordAddress(*outDiagnostics); + recorder->recordAddress(outDiagnostics ? *outDiagnostics : nullptr); m_recordManager->apendOutput(); } diff --git a/source/slang-record-replay/record/slang-composite-component-type.h b/source/slang-record-replay/record/slang-composite-component-type.h index 758a59434..2374bcda2 100644 --- a/source/slang-record-replay/record/slang-composite-component-type.h +++ b/source/slang-record-replay/record/slang-composite-component-type.h @@ -19,7 +19,6 @@ namespace SlangRecord ISlangUnknown* getInterface(const Guid& guid); explicit CompositeComponentTypeRecorder(slang::IComponentType* componentType, RecordManager* recordManager); - ~CompositeComponentTypeRecorder(); // Interfaces for `IComponentType` virtual SLANG_NO_THROW slang::ISession* SLANG_MCALL getSession() override; diff --git a/source/slang-record-replay/record/slang-entrypoint.cpp b/source/slang-record-replay/record/slang-entrypoint.cpp index e5c76a5af..7d43700e2 100644 --- a/source/slang-record-replay/record/slang-entrypoint.cpp +++ b/source/slang-record-replay/record/slang-entrypoint.cpp @@ -14,11 +14,6 @@ namespace SlangRecord slangRecordLog(LogLevel::Verbose, "%s: %p\n", __PRETTY_FUNCTION__, entryPoint); } - EntryPointRecorder::~EntryPointRecorder() - { - m_actualEntryPoint->release(); - } - ISlangUnknown* EntryPointRecorder::getInterface(const Guid& guid) { if(guid == EntryPointRecorder::getTypeGuid()) @@ -63,7 +58,7 @@ namespace SlangRecord slang::ProgramLayout* programLayout = m_actualEntryPoint->getLayout(targetIndex, outDiagnostics); { - recorder->recordAddress(*outDiagnostics); + recorder->recordAddress(outDiagnostics ? *outDiagnostics : nullptr); recorder->recordAddress(programLayout); m_recordManager->apendOutput(); } @@ -99,7 +94,7 @@ namespace SlangRecord { recorder->recordAddress(*outCode); - recorder->recordAddress(*outDiagnostics); + recorder->recordAddress(outDiagnostics ? *outDiagnostics : nullptr); m_recordManager->apendOutput(); } @@ -124,7 +119,7 @@ namespace SlangRecord { recorder->recordAddress(*outCode); - recorder->recordAddress(*outDiagnostics); + recorder->recordAddress(outDiagnostics ? *outDiagnostics : nullptr); m_recordManager->apendOutput(); } @@ -199,7 +194,7 @@ namespace SlangRecord { recorder->recordAddress(*outSpecializedComponentType); - recorder->recordAddress(*outDiagnostics); + recorder->recordAddress(outDiagnostics ? *outDiagnostics : nullptr); m_recordManager->apendOutput(); } @@ -222,7 +217,7 @@ namespace SlangRecord { recorder->recordAddress(*outLinkedComponentType); - recorder->recordAddress(*outDiagnostics); + recorder->recordAddress(outDiagnostics ? *outDiagnostics : nullptr); m_recordManager->apendOutput(); } @@ -249,7 +244,7 @@ namespace SlangRecord { recorder->recordAddress(*outSharedLibrary); - recorder->recordAddress(*outDiagnostics); + recorder->recordAddress(outDiagnostics ? *outDiagnostics : nullptr); m_recordManager->apendOutput(); } @@ -298,7 +293,7 @@ namespace SlangRecord { recorder->recordAddress(*outLinkedComponentType); - recorder->recordAddress(*outDiagnostics); + recorder->recordAddress(outDiagnostics ? *outDiagnostics : nullptr); m_recordManager->apendOutput(); } diff --git a/source/slang-record-replay/record/slang-entrypoint.h b/source/slang-record-replay/record/slang-entrypoint.h index 17c6fab6f..60c1bf369 100644 --- a/source/slang-record-replay/record/slang-entrypoint.h +++ b/source/slang-record-replay/record/slang-entrypoint.h @@ -21,7 +21,6 @@ namespace SlangRecord ISlangUnknown* getInterface(const Guid& guid); explicit EntryPointRecorder(slang::IEntryPoint* entryPoint, RecordManager* recordManager); - ~EntryPointRecorder(); // Interfaces for `IComponentType` virtual SLANG_NO_THROW slang::ISession* SLANG_MCALL getSession() override; diff --git a/source/slang-record-replay/record/slang-filesystem.cpp b/source/slang-record-replay/record/slang-filesystem.cpp index 310fecd78..87a7182fc 100644 --- a/source/slang-record-replay/record/slang-filesystem.cpp +++ b/source/slang-record-replay/record/slang-filesystem.cpp @@ -18,11 +18,6 @@ namespace SlangRecord slangRecordLog(LogLevel::Verbose, "%s: %p\n", __PRETTY_FUNCTION__, m_actualFileSystem.get()); } - FileSystemRecorder::~FileSystemRecorder() - { - m_actualFileSystem->release(); - } - void* FileSystemRecorder::castAs(const Slang::Guid& guid) { return getInterface(guid); diff --git a/source/slang-record-replay/record/slang-filesystem.h b/source/slang-record-replay/record/slang-filesystem.h index ff7004fa1..6f73e479c 100644 --- a/source/slang-record-replay/record/slang-filesystem.h +++ b/source/slang-record-replay/record/slang-filesystem.h @@ -18,7 +18,6 @@ namespace SlangRecord { public: explicit FileSystemRecorder(ISlangFileSystemExt* fileSystem, RecordManager* recordManager); - ~FileSystemRecorder(); // ISlangUnknown SLANG_REF_OBJECT_IUNKNOWN_ALL diff --git a/source/slang-record-replay/record/slang-global-session.cpp b/source/slang-record-replay/record/slang-global-session.cpp index f8fe6c65b..c5350f760 100644 --- a/source/slang-record-replay/record/slang-global-session.cpp +++ b/source/slang-record-replay/record/slang-global-session.cpp @@ -29,11 +29,6 @@ namespace SlangRecord m_recordManager->apendOutput(); } - GlobalSessionRecorder::~GlobalSessionRecorder() - { - m_actualGlobalSession->release(); - } - SLANG_NO_THROW SlangResult SLANG_MCALL GlobalSessionRecorder::queryInterface(SlangUUID const& uuid, void** outObject) { if (uuid == Session::getTypeGuid()) diff --git a/source/slang-record-replay/record/slang-global-session.h b/source/slang-record-replay/record/slang-global-session.h index 3fd584ef4..4242f4bff 100644 --- a/source/slang-record-replay/record/slang-global-session.h +++ b/source/slang-record-replay/record/slang-global-session.h @@ -15,7 +15,6 @@ namespace SlangRecord { public: explicit GlobalSessionRecorder(slang::IGlobalSession* session); - virtual ~GlobalSessionRecorder(); SLANG_REF_OBJECT_IUNKNOWN_ADD_REF SLANG_REF_OBJECT_IUNKNOWN_RELEASE diff --git a/source/slang-record-replay/record/slang-module.cpp b/source/slang-record-replay/record/slang-module.cpp index 42dcd9eea..b116fa99b 100644 --- a/source/slang-record-replay/record/slang-module.cpp +++ b/source/slang-record-replay/record/slang-module.cpp @@ -14,11 +14,6 @@ namespace SlangRecord slangRecordLog(LogLevel::Verbose, "%s: %p\n", __PRETTY_FUNCTION__, module); } - ModuleRecorder::~ModuleRecorder() - { - m_actualModule->release(); - } - ISlangUnknown* ModuleRecorder::getInterface(const Guid& guid) { if(guid == ModuleRecorder::getTypeGuid()) @@ -185,7 +180,7 @@ namespace SlangRecord { recorder->recordAddress(*outEntryPoint); - recorder->recordAddress(*outDiagnostics); + recorder->recordAddress(outDiagnostics ? *outDiagnostics : nullptr); m_recordManager->apendOutput(); } @@ -249,7 +244,7 @@ namespace SlangRecord slang::ProgramLayout* programLayout = m_actualModule->getLayout(targetIndex, outDiagnostics); { - recorder->recordAddress(*outDiagnostics); + recorder->recordAddress(outDiagnostics ? *outDiagnostics : nullptr); recorder->recordAddress(programLayout); m_recordManager->apendOutput(); } @@ -285,7 +280,7 @@ namespace SlangRecord { recorder->recordAddress(*outCode); - recorder->recordAddress(*outDiagnostics); + recorder->recordAddress(outDiagnostics ? *outDiagnostics : nullptr); m_recordManager->apendOutput(); } @@ -310,7 +305,7 @@ namespace SlangRecord { recorder->recordAddress(*outCode); - recorder->recordAddress(*outDiagnostics); + recorder->recordAddress(outDiagnostics ? *outDiagnostics : nullptr); m_recordManager->apendOutput(); } @@ -386,7 +381,7 @@ namespace SlangRecord { recorder->recordAddress(*outSpecializedComponentType); - recorder->recordAddress(*outDiagnostics); + recorder->recordAddress(outDiagnostics ? *outDiagnostics : nullptr); m_recordManager->apendOutput(); } @@ -409,7 +404,7 @@ namespace SlangRecord { recorder->recordAddress(*outLinkedComponentType); - recorder->recordAddress(*outDiagnostics); + recorder->recordAddress(outDiagnostics ? *outDiagnostics : nullptr); m_recordManager->apendOutput(); } @@ -436,7 +431,7 @@ namespace SlangRecord { recorder->recordAddress(*outSharedLibrary); - recorder->recordAddress(*outDiagnostics); + recorder->recordAddress(outDiagnostics ? *outDiagnostics : nullptr); m_recordManager->apendOutput(); } @@ -485,7 +480,7 @@ namespace SlangRecord { recorder->recordAddress(*outLinkedComponentType); - recorder->recordAddress(*outDiagnostics); + recorder->recordAddress(outDiagnostics ? *outDiagnostics : nullptr); m_recordManager->apendOutput(); } diff --git a/source/slang-record-replay/record/slang-module.h b/source/slang-record-replay/record/slang-module.h index 22339f1cd..1b1cc5de9 100644 --- a/source/slang-record-replay/record/slang-module.h +++ b/source/slang-record-replay/record/slang-module.h @@ -21,7 +21,6 @@ namespace SlangRecord ISlangUnknown* getInterface(const Guid& guid); explicit ModuleRecorder(slang::IModule* module, RecordManager* recordManager); - ~ModuleRecorder(); // Interfaces for `IModule` virtual SLANG_NO_THROW SlangResult SLANG_MCALL findEntryPointByName( diff --git a/source/slang-record-replay/record/slang-session.cpp b/source/slang-record-replay/record/slang-session.cpp index 7c0ccd2e1..3eb944082 100644 --- a/source/slang-record-replay/record/slang-session.cpp +++ b/source/slang-record-replay/record/slang-session.cpp @@ -17,11 +17,6 @@ namespace SlangRecord slangRecordLog(LogLevel::Verbose, "%s: %p\n", "SessionRecorder create:", session); } - SessionRecorder::~SessionRecorder() - { - m_actualSession->release(); - } - ISlangUnknown* SessionRecorder::getInterface(const Guid& guid) { if(guid == ISlangUnknown::getTypeGuid() || guid == ISession::getTypeGuid()) @@ -54,7 +49,7 @@ namespace SlangRecord slang::IModule* pModule = m_actualSession->loadModule(moduleName, outDiagnostics); { - recorder->recordAddress(*outDiagnostics); + recorder->recordAddress(outDiagnostics ? *outDiagnostics : nullptr); recorder->recordAddress(pModule); m_recordManager->apendOutput(); } @@ -83,7 +78,7 @@ namespace SlangRecord slang::IModule* pModule = m_actualSession->loadModuleFromIRBlob(moduleName, path, source, outDiagnostics); { - recorder->recordAddress(*outDiagnostics); + recorder->recordAddress(outDiagnostics ? *outDiagnostics : nullptr); recorder->recordAddress(pModule); m_recordManager->apendOutput(); } @@ -112,7 +107,7 @@ namespace SlangRecord slang::IModule* pModule = m_actualSession->loadModuleFromSource(moduleName, path, source, outDiagnostics); { - recorder->recordAddress(*outDiagnostics); + recorder->recordAddress(outDiagnostics ? *outDiagnostics : nullptr); recorder->recordAddress(pModule); m_recordManager->apendOutput(); } @@ -142,7 +137,7 @@ namespace SlangRecord { // TODO: Not sure if we need to record the diagnostics blob. - recorder->recordAddress(*outDiagnostics); + recorder->recordAddress(outDiagnostics ? *outDiagnostics : nullptr); recorder->recordAddress(pModule); m_recordManager->apendOutput(); } @@ -179,7 +174,7 @@ namespace SlangRecord { recorder->recordAddress(*outCompositeComponentType); - recorder->recordAddress(*outDiagnostics); + recorder->recordAddress(outDiagnostics ? *outDiagnostics : nullptr); m_recordManager->apendOutput(); } @@ -213,7 +208,7 @@ namespace SlangRecord slang::TypeReflection* pTypeReflection = m_actualSession->specializeType(type, specializationArgs, specializationArgCount, outDiagnostics); { - recorder->recordAddress(*outDiagnostics); + recorder->recordAddress(outDiagnostics ? *outDiagnostics : nullptr); recorder->recordAddress(pTypeReflection); m_recordManager->apendOutput(); } @@ -241,7 +236,7 @@ namespace SlangRecord slang::TypeLayoutReflection* pTypeLayoutReflection = m_actualSession->getTypeLayout(type, targetIndex, rules, outDiagnostics); { - recorder->recordAddress(*outDiagnostics); + recorder->recordAddress(outDiagnostics ? *outDiagnostics : nullptr); recorder->recordAddress(pTypeLayoutReflection); m_recordManager->apendOutput(); } @@ -267,7 +262,7 @@ namespace SlangRecord slang::TypeReflection* pTypeReflection = m_actualSession->getContainerType(elementType, containerType, outDiagnostics); { - recorder->recordAddress(*outDiagnostics); + recorder->recordAddress(outDiagnostics ? *outDiagnostics : nullptr); recorder->recordAddress(pTypeReflection); m_recordManager->apendOutput(); } @@ -386,7 +381,7 @@ namespace SlangRecord { recorder->recordAddress(*outConformance); - recorder->recordAddress(*outDiagnostics); + recorder->recordAddress(outDiagnostics ? *outDiagnostics : nullptr); m_recordManager->apendOutput(); } diff --git a/source/slang-record-replay/record/slang-session.h b/source/slang-record-replay/record/slang-session.h index ca06e25a0..bc56003a9 100644 --- a/source/slang-record-replay/record/slang-session.h +++ b/source/slang-record-replay/record/slang-session.h @@ -20,7 +20,6 @@ namespace SlangRecord ISlangUnknown* getInterface(const Guid& guid); explicit SessionRecorder(slang::ISession* session, RecordManager* recordManager); - ~SessionRecorder(); SLANG_NO_THROW slang::IGlobalSession* SLANG_MCALL getGlobalSession() override; SLANG_NO_THROW slang::IModule* SLANG_MCALL loadModule( diff --git a/source/slang-record-replay/record/slang-type-conformance.cpp b/source/slang-record-replay/record/slang-type-conformance.cpp index 91f108786..56433f700 100644 --- a/source/slang-record-replay/record/slang-type-conformance.cpp +++ b/source/slang-record-replay/record/slang-type-conformance.cpp @@ -14,11 +14,6 @@ namespace SlangRecord slangRecordLog(LogLevel::Verbose, "%s: %p\n", __PRETTY_FUNCTION__, typeConformance); } - TypeConformanceRecorder::~TypeConformanceRecorder() - { - m_actualTypeConformance->release(); - } - ISlangUnknown* TypeConformanceRecorder::getInterface(const Guid& guid) { if (guid == TypeConformanceRecorder::getTypeGuid()) @@ -67,7 +62,7 @@ namespace SlangRecord slang::ProgramLayout* programLayout = m_actualTypeConformance->getLayout(targetIndex, outDiagnostics); { - recorder->recordAddress(*outDiagnostics); + recorder->recordAddress(outDiagnostics ? *outDiagnostics : nullptr); recorder->recordAddress(programLayout); m_recordManager->apendOutput(); } @@ -102,7 +97,7 @@ namespace SlangRecord { recorder->recordAddress(*outCode); - recorder->recordAddress(*outDiagnostics); + recorder->recordAddress(outDiagnostics ? *outDiagnostics : nullptr); m_recordManager->apendOutput(); } @@ -127,7 +122,7 @@ namespace SlangRecord { recorder->recordAddress(*outCode); - recorder->recordAddress(*outDiagnostics); + recorder->recordAddress(outDiagnostics ? *outDiagnostics : nullptr); m_recordManager->apendOutput(); } @@ -202,7 +197,7 @@ namespace SlangRecord { recorder->recordAddress(*outSpecializedComponentType); - recorder->recordAddress(*outDiagnostics); + recorder->recordAddress(outDiagnostics ? *outDiagnostics : nullptr); m_recordManager->apendOutput(); } @@ -225,7 +220,7 @@ namespace SlangRecord { recorder->recordAddress(*outLinkedComponentType); - recorder->recordAddress(*outDiagnostics); + recorder->recordAddress(outDiagnostics ? *outDiagnostics : nullptr); m_recordManager->apendOutput(); } @@ -252,7 +247,7 @@ namespace SlangRecord { recorder->recordAddress(*outSharedLibrary); - recorder->recordAddress(*outDiagnostics); + recorder->recordAddress(outDiagnostics ? *outDiagnostics : nullptr); m_recordManager->apendOutput(); } @@ -301,7 +296,7 @@ namespace SlangRecord { recorder->recordAddress(*outLinkedComponentType); - recorder->recordAddress(*outDiagnostics); + recorder->recordAddress(outDiagnostics ? *outDiagnostics : nullptr); m_recordManager->apendOutput(); } diff --git a/source/slang-record-replay/record/slang-type-conformance.h b/source/slang-record-replay/record/slang-type-conformance.h index 7bcae0d15..7beabcca5 100644 --- a/source/slang-record-replay/record/slang-type-conformance.h +++ b/source/slang-record-replay/record/slang-type-conformance.h @@ -21,7 +21,6 @@ namespace SlangRecord ISlangUnknown* getInterface(const Guid& guid); explicit TypeConformanceRecorder(slang::ITypeConformance* typeConformance, RecordManager* recordManager); - ~TypeConformanceRecorder(); // Interfaces for `IComponentType` virtual SLANG_NO_THROW slang::ISession* SLANG_MCALL getSession() override; diff --git a/source/slang-record-replay/replay/json-consumer.cpp b/source/slang-record-replay/replay/json-consumer.cpp index 0faf5267b..456988bdb 100644 --- a/source/slang-record-replay/replay/json-consumer.cpp +++ b/source/slang-record-replay/replay/json-consumer.cpp @@ -448,7 +448,7 @@ namespace SlangRecord _writePair(builder, indent, "name", Slang::StringUtil::makeStringWithFormat("\"%s\"", desc.preprocessorMacros[i].name != nullptr ? desc.preprocessorMacros[i].name : "nullptr")); - _writePair(builder, indent, "value", Slang::StringUtil::makeStringWithFormat("\"%s\"", + _writePairNoComma(builder, indent, "value", Slang::StringUtil::makeStringWithFormat("\"%s\"", desc.preprocessorMacros[i].value != nullptr ? desc.preprocessorMacros[i].value : "nullptr")); } } diff --git a/source/slang-record-replay/replay/replay-consumer.cpp b/source/slang-record-replay/replay/replay-consumer.cpp index cb1117bc0..7710b50e9 100644 --- a/source/slang-record-replay/replay/replay-consumer.cpp +++ b/source/slang-record-replay/replay/replay-consumer.cpp @@ -106,7 +106,6 @@ namespace SlangRecord SlangResult CommonInterfaceReplayer::getEntryPointHash(ObjectID objectId, SlangInt entryPointIndex, SlangInt targetIndex, ObjectID outHashId) { InputObjectSanityCheck(objectId); - OutputObjectSanityCheck(outHashId); SlangResult res = SLANG_OK; slang::IComponentType* pObj = getObjectPointer(objectId); @@ -115,16 +114,17 @@ namespace SlangRecord if (outHash) { - m_objectMap.add(outHashId, outHash); if (outHash->getBufferSize()) { uint8_t* buffer = (uint8_t*)outHash->getBufferPointer(); Slang::StringBuilder strBuilder; + strBuilder << "entrypoint: "<< entryPointIndex << ", target: " << targetIndex << ", hash: "; + for (size_t i = 0; i < outHash->getBufferSize(); i++) { - strBuilder<<Slang::StringUtil::makeStringWithFormat("%.2X ", buffer[i]); + strBuilder<<Slang::StringUtil::makeStringWithFormat("%.2X", buffer[i]); } - slangRecordLog(LogLevel::Verbose, "getEntryPointHash: %s\n", strBuilder.begin()); + slangRecordLog(LogLevel::Verbose, "%s\n", strBuilder.begin()); } else { diff --git a/test-record-replay.sh b/test-record-replay.sh new file mode 100755 index 000000000..2faabe954 --- /dev/null +++ b/test-record-replay.sh @@ -0,0 +1,147 @@ +#!/usr/bin/env bash + +RED='\033[0;31m' +Green='\033[0;32m' +NC='\033[0m' +matchPattern="entrypoint: [0-9]+, target: [0-9]+, hash: [0-9a-fA-F]+" + +getHash() +{ + matchedLine=$1 + local -n outputVar=$2 + + entrypointIdx=$(echo $matchedLine | grep -oE "entrypoint: [0-9]+" | grep -oE "[0-9]+") + targetIdx=$(echo $matchedLine | grep -oE "target: [0-9]+" | grep -oE "[0-9]+") + hashCode=$(echo $matchedLine | grep -oE "hash: .*" | grep -oE ": [0-9a-fA-F]+" | grep -oE "[0-9a-fA-F]+") + + outputVar="$entrypointIdx-$targetIdx-$hashCode" +} + +log() +{ + msg=$1 + color=$2 + printf "${color}$1${NC}\n" +} + +parseStandardOutput() +{ + local -n resultArray=$1 + lines=$2 + + for line in "${lines[@]}" + do + matchLine=$(echo $line | grep -oE "$matchPattern") + + if [ -n "$matchLine" ]; then + result="" + getHash "$matchLine" result + + if [ -n "$result" ]; then + resultArray+=("$result") + fi + fi + done +} + +resultCheck() +{ + local -n inExpectedResults=$1 + local -n inReplayResults=$2 + local -n outFailedResults=$3 + + found="" + for expectedResult in ${inExpectedResults[@]}; do + + for replayResult in ${inReplayResults[@]}; do + if [ "$replayResult" == "$expectedResult" ]; then + found="1" + fi + done + + if [ -z "$found" ]; then + echo "$expectedResult is not Found in replay" + outFailedResults+=("$expectedResult") + else + echo "$expectedResult is Found in replay" + fi + done +} + +# TODO: Add more test commands here in this array +testCommands=("./build/Debug/bin/hello-world" "./build/Debug/bin/triangle") + +# Enable hash code generation for the test such that +# we can have something to compare with replaying the test +argsToEnableHashCode="--test-mode" + +declare -A testStats + +for ((i = 0; i < ${#testCommands[@]}; i++)) +do + testCommand=${testCommands[$i]} + echo "Start running test: $testCommand" + + # Run the test executable + export SLANG_RECORD_LAYER=1 + mapfile -t lines < <(${testCommand} ${argsToEnableHashCode}) + unset SLANG_RECORD_LAYER + + # parse the output from stdout + expectedResults=() + parseStandardOutput expectedResults "${lines[@]}" + + echo "Expected Results: ${expectedResults[@]}" + if [ ${#expectedResults[@]} -eq 0 ]; then + log "No expected results found" $RED + rm -rf ./slang-record/* + continue + fi + + # Replay the record file + export SLANG_RECORD_LOG_LEVEL=3 + replayTestCommand="./build/Debug/bin/slang-replay ./slang-record/*.cap" + echo "Start replaying the test ..." + mapfile -t lines < <(${replayTestCommand}) + unset SLANG_RECORD_LOG_LEVEL + + # parse the output from stdout + replayResults=() + parseStandardOutput replayResults "${lines[@]}" + + echo "Replay Results: ${replayResults[@]}" + if [ ${#replayResults[@]} -eq 0 ]; then + log "No replay results found" $RED + rm -rf ./slang-record/* + continue + fi + + # Check the results + failedResults=() + resultCheck expectedResults replayResults failedResults + + rm -rf ./slang-record/* + + if [ ${#failedResults[@]} -eq 0 ]; then + testStats[$testCommand]="PASSED" + else + testStats[$testCommand]="FAILED" + fi + printf "\n" +done + +for testName in "${!testStats[@]}" +do + if [ "${testStats[$testName]}" == "PASSED" ]; then + log "$testName: PASSED" $Green + else + log "$testName: FAILED" $RED + fi +done + +# Notify the CI if any of the tests failed +if [ ${#testStats[@]} -eq 0 ]; then + exit 0 +else + exit 1 +fi diff --git a/tools/platform/window.h b/tools/platform/window.h index efbe139a7..29327def3 100644 --- a/tools/platform/window.h +++ b/tools/platform/window.h @@ -242,7 +242,7 @@ public: int /*showCommand*/) \ { \ platform::Application::init(); \ - auto result = APPLICATION_ENTRY(); \ + auto result = APPLICATION_ENTRY(0, nullptr); \ platform::Application::dispose(); \ GFX_DUMP_LEAK \ return result; \ @@ -251,10 +251,10 @@ public: #else #define PLATFORM_UI_MAIN(APPLICATION_ENTRY) \ - int main() \ + int main(int argc, char** argv) \ { \ platform::Application::init(); \ - auto rs = APPLICATION_ENTRY(); \ + auto rs = APPLICATION_ENTRY(argc, argv); \ platform::Application::dispose(); \ return rs; \ } |
