diff options
| author | Yong He <yonghe@outlook.com> | 2021-03-31 11:35:17 -0700 |
|---|---|---|
| committer | GitHub <noreply@github.com> | 2021-03-31 11:35:17 -0700 |
| commit | 3f1632a1450a5879f337b4bd178e48880cd583f8 (patch) | |
| tree | db4adc2ac0f6113dfd4a97a0e2f7a0c4312ef48b | |
| parent | 5fde038b1a6b3c8b335cd5b380c3ee8d15403052 (diff) | |
`gfx` explicit transient resource management. (#1774)
34 files changed, 1077 insertions, 1186 deletions
diff --git a/build/visual-studio/cpu-hello-world/cpu-hello-world.vcxproj b/build/visual-studio/cpu-hello-world/cpu-hello-world.vcxproj index 0ba5973d0..9b21c1410 100644 --- a/build/visual-studio/cpu-hello-world/cpu-hello-world.vcxproj +++ b/build/visual-studio/cpu-hello-world/cpu-hello-world.vcxproj @@ -168,12 +168,12 @@ <None Include="..\..\..\examples\cpu-hello-world\shader.slang" /> </ItemGroup> <ItemGroup> + <ProjectReference Include="..\example-base\example-base.vcxproj"> + <Project>{37BED5B5-23FA-D81F-8C0C-F1167867813A}</Project> + </ProjectReference> <ProjectReference Include="..\slang\slang.vcxproj"> <Project>{DB00DA62-0533-4AFD-B59F-A67D5B3A0808}</Project> </ProjectReference> - <ProjectReference Include="..\core\core.vcxproj"> - <Project>{F9BE7957-8399-899E-0C49-E714FDDD4B65}</Project> - </ProjectReference> <ProjectReference Include="..\gfx\gfx.vcxproj"> <Project>{222F7498-B40C-4F3F-A704-DDEB91A4484A}</Project> </ProjectReference> @@ -183,6 +183,9 @@ <ProjectReference Include="..\platform\platform.vcxproj"> <Project>{3565FE5E-4FA3-11EB-AE93-0242AC130002}</Project> </ProjectReference> + <ProjectReference Include="..\core\core.vcxproj"> + <Project>{F9BE7957-8399-899E-0C49-E714FDDD4B65}</Project> + </ProjectReference> </ItemGroup> <Import Project="$(VCTargetsPath)\Microsoft.Cpp.targets" /> <ImportGroup Label="ExtensionTargets"> diff --git a/build/visual-studio/example-base/example-base.vcxproj b/build/visual-studio/example-base/example-base.vcxproj new file mode 100644 index 000000000..794e0b114 --- /dev/null +++ b/build/visual-studio/example-base/example-base.vcxproj @@ -0,0 +1,186 @@ +<?xml version="1.0" encoding="utf-8"?> +<Project DefaultTargets="Build" ToolsVersion="14.0" xmlns="http://schemas.microsoft.com/developer/msbuild/2003"> + <ItemGroup Label="ProjectConfigurations"> + <ProjectConfiguration Include="Debug|Win32"> + <Configuration>Debug</Configuration> + <Platform>Win32</Platform> + </ProjectConfiguration> + <ProjectConfiguration Include="Debug|x64"> + <Configuration>Debug</Configuration> + <Platform>x64</Platform> + </ProjectConfiguration> + <ProjectConfiguration Include="Release|Win32"> + <Configuration>Release</Configuration> + <Platform>Win32</Platform> + </ProjectConfiguration> + <ProjectConfiguration Include="Release|x64"> + <Configuration>Release</Configuration> + <Platform>x64</Platform> + </ProjectConfiguration> + </ItemGroup> + <PropertyGroup Label="Globals"> + <ProjectGuid>{37BED5B5-23FA-D81F-8C0C-F1167867813A}</ProjectGuid> + <IgnoreWarnCompileDuplicatedFilename>true</IgnoreWarnCompileDuplicatedFilename> + <Keyword>Win32Proj</Keyword> + <RootNamespace>example-base</RootNamespace> + </PropertyGroup> + <Import Project="$(VCTargetsPath)\Microsoft.Cpp.Default.props" /> + <PropertyGroup Condition="'$(Configuration)|$(Platform)'=='Debug|Win32'" Label="Configuration"> + <ConfigurationType>StaticLibrary</ConfigurationType> + <UseDebugLibraries>true</UseDebugLibraries> + <CharacterSet>Unicode</CharacterSet> + <PlatformToolset>v140</PlatformToolset> + </PropertyGroup> + <PropertyGroup Condition="'$(Configuration)|$(Platform)'=='Debug|x64'" Label="Configuration"> + <ConfigurationType>StaticLibrary</ConfigurationType> + <UseDebugLibraries>true</UseDebugLibraries> + <CharacterSet>Unicode</CharacterSet> + <PlatformToolset>v140</PlatformToolset> + </PropertyGroup> + <PropertyGroup Condition="'$(Configuration)|$(Platform)'=='Release|Win32'" Label="Configuration"> + <ConfigurationType>StaticLibrary</ConfigurationType> + <UseDebugLibraries>false</UseDebugLibraries> + <CharacterSet>Unicode</CharacterSet> + <PlatformToolset>v140</PlatformToolset> + </PropertyGroup> + <PropertyGroup Condition="'$(Configuration)|$(Platform)'=='Release|x64'" Label="Configuration"> + <ConfigurationType>StaticLibrary</ConfigurationType> + <UseDebugLibraries>false</UseDebugLibraries> + <CharacterSet>Unicode</CharacterSet> + <PlatformToolset>v140</PlatformToolset> + </PropertyGroup> + <Import Project="$(VCTargetsPath)\Microsoft.Cpp.props" /> + <ImportGroup Label="ExtensionSettings"> + </ImportGroup> + <ImportGroup Label="PropertySheets" Condition="'$(Configuration)|$(Platform)'=='Debug|Win32'"> + <Import Project="$(UserRootDir)\Microsoft.Cpp.$(Platform).user.props" Condition="exists('$(UserRootDir)\Microsoft.Cpp.$(Platform).user.props')" Label="LocalAppDataPlatform" /> + </ImportGroup> + <ImportGroup Label="PropertySheets" Condition="'$(Configuration)|$(Platform)'=='Debug|x64'"> + <Import Project="$(UserRootDir)\Microsoft.Cpp.$(Platform).user.props" Condition="exists('$(UserRootDir)\Microsoft.Cpp.$(Platform).user.props')" Label="LocalAppDataPlatform" /> + </ImportGroup> + <ImportGroup Label="PropertySheets" Condition="'$(Configuration)|$(Platform)'=='Release|Win32'"> + <Import Project="$(UserRootDir)\Microsoft.Cpp.$(Platform).user.props" Condition="exists('$(UserRootDir)\Microsoft.Cpp.$(Platform).user.props')" Label="LocalAppDataPlatform" /> + </ImportGroup> + <ImportGroup Label="PropertySheets" Condition="'$(Configuration)|$(Platform)'=='Release|x64'"> + <Import Project="$(UserRootDir)\Microsoft.Cpp.$(Platform).user.props" Condition="exists('$(UserRootDir)\Microsoft.Cpp.$(Platform).user.props')" Label="LocalAppDataPlatform" /> + </ImportGroup> + <PropertyGroup Label="UserMacros" /> + <PropertyGroup Condition="'$(Configuration)|$(Platform)'=='Debug|Win32'"> + <OutDir>..\..\..\bin\windows-x86\debug\</OutDir> + <IntDir>..\..\..\intermediate\windows-x86\debug\example-base\</IntDir> + <TargetName>example-base</TargetName> + <TargetExt>.lib</TargetExt> + </PropertyGroup> + <PropertyGroup Condition="'$(Configuration)|$(Platform)'=='Debug|x64'"> + <OutDir>..\..\..\bin\windows-x64\debug\</OutDir> + <IntDir>..\..\..\intermediate\windows-x64\debug\example-base\</IntDir> + <TargetName>example-base</TargetName> + <TargetExt>.lib</TargetExt> + </PropertyGroup> + <PropertyGroup Condition="'$(Configuration)|$(Platform)'=='Release|Win32'"> + <OutDir>..\..\..\bin\windows-x86\release\</OutDir> + <IntDir>..\..\..\intermediate\windows-x86\release\example-base\</IntDir> + <TargetName>example-base</TargetName> + <TargetExt>.lib</TargetExt> + </PropertyGroup> + <PropertyGroup Condition="'$(Configuration)|$(Platform)'=='Release|x64'"> + <OutDir>..\..\..\bin\windows-x64\release\</OutDir> + <IntDir>..\..\..\intermediate\windows-x64\release\example-base\</IntDir> + <TargetName>example-base</TargetName> + <TargetExt>.lib</TargetExt> + </PropertyGroup> + <ItemDefinitionGroup Condition="'$(Configuration)|$(Platform)'=='Debug|Win32'"> + <ClCompile> + <PrecompiledHeader>NotUsing</PrecompiledHeader> + <WarningLevel>Level3</WarningLevel> + <PreprocessorDefinitions>_DEBUG;%(PreprocessorDefinitions)</PreprocessorDefinitions> + <AdditionalIncludeDirectories>..\..\..;..\..\..\tools;%(AdditionalIncludeDirectories)</AdditionalIncludeDirectories> + <DebugInformationFormat>EditAndContinue</DebugInformationFormat> + <Optimization>Disabled</Optimization> + <RuntimeLibrary>MultiThreadedDebug</RuntimeLibrary> + </ClCompile> + <Link> + <SubSystem>Windows</SubSystem> + <GenerateDebugInformation>true</GenerateDebugInformation> + </Link> + </ItemDefinitionGroup> + <ItemDefinitionGroup Condition="'$(Configuration)|$(Platform)'=='Debug|x64'"> + <ClCompile> + <PrecompiledHeader>NotUsing</PrecompiledHeader> + <WarningLevel>Level3</WarningLevel> + <PreprocessorDefinitions>_DEBUG;%(PreprocessorDefinitions)</PreprocessorDefinitions> + <AdditionalIncludeDirectories>..\..\..;..\..\..\tools;%(AdditionalIncludeDirectories)</AdditionalIncludeDirectories> + <DebugInformationFormat>EditAndContinue</DebugInformationFormat> + <Optimization>Disabled</Optimization> + <RuntimeLibrary>MultiThreadedDebug</RuntimeLibrary> + </ClCompile> + <Link> + <SubSystem>Windows</SubSystem> + <GenerateDebugInformation>true</GenerateDebugInformation> + </Link> + </ItemDefinitionGroup> + <ItemDefinitionGroup Condition="'$(Configuration)|$(Platform)'=='Release|Win32'"> + <ClCompile> + <PrecompiledHeader>NotUsing</PrecompiledHeader> + <WarningLevel>Level3</WarningLevel> + <PreprocessorDefinitions>NDEBUG;%(PreprocessorDefinitions)</PreprocessorDefinitions> + <AdditionalIncludeDirectories>..\..\..;..\..\..\tools;%(AdditionalIncludeDirectories)</AdditionalIncludeDirectories> + <Optimization>Full</Optimization> + <FunctionLevelLinking>true</FunctionLevelLinking> + <IntrinsicFunctions>true</IntrinsicFunctions> + <MinimalRebuild>false</MinimalRebuild> + <StringPooling>true</StringPooling> + <RuntimeLibrary>MultiThreaded</RuntimeLibrary> + </ClCompile> + <Link> + <SubSystem>Windows</SubSystem> + <EnableCOMDATFolding>true</EnableCOMDATFolding> + <OptimizeReferences>true</OptimizeReferences> + </Link> + </ItemDefinitionGroup> + <ItemDefinitionGroup Condition="'$(Configuration)|$(Platform)'=='Release|x64'"> + <ClCompile> + <PrecompiledHeader>NotUsing</PrecompiledHeader> + <WarningLevel>Level3</WarningLevel> + <PreprocessorDefinitions>NDEBUG;%(PreprocessorDefinitions)</PreprocessorDefinitions> + <AdditionalIncludeDirectories>..\..\..;..\..\..\tools;%(AdditionalIncludeDirectories)</AdditionalIncludeDirectories> + <Optimization>Full</Optimization> + <FunctionLevelLinking>true</FunctionLevelLinking> + <IntrinsicFunctions>true</IntrinsicFunctions> + <MinimalRebuild>false</MinimalRebuild> + <StringPooling>true</StringPooling> + <RuntimeLibrary>MultiThreaded</RuntimeLibrary> + </ClCompile> + <Link> + <SubSystem>Windows</SubSystem> + <EnableCOMDATFolding>true</EnableCOMDATFolding> + <OptimizeReferences>true</OptimizeReferences> + </Link> + </ItemDefinitionGroup> + <ItemGroup> + <ClInclude Include="..\..\..\examples\example-base\example-base.h" /> + </ItemGroup> + <ItemGroup> + <ClCompile Include="..\..\..\examples\example-base\example-base.cpp" /> + </ItemGroup> + <ItemGroup> + <ProjectReference Include="..\gfx\gfx.vcxproj"> + <Project>{222F7498-B40C-4F3F-A704-DDEB91A4484A}</Project> + </ProjectReference> + <ProjectReference Include="..\slang\slang.vcxproj"> + <Project>{DB00DA62-0533-4AFD-B59F-A67D5B3A0808}</Project> + </ProjectReference> + <ProjectReference Include="..\platform\platform.vcxproj"> + <Project>{3565FE5E-4FA3-11EB-AE93-0242AC130002}</Project> + </ProjectReference> + <ProjectReference Include="..\gfx-util\gfx-util.vcxproj"> + <Project>{F5ADB74E-02A7-44FB-AA3B-FC02F8AC7A4B}</Project> + </ProjectReference> + <ProjectReference Include="..\core\core.vcxproj"> + <Project>{F9BE7957-8399-899E-0C49-E714FDDD4B65}</Project> + </ProjectReference> + </ItemGroup> + <Import Project="$(VCTargetsPath)\Microsoft.Cpp.targets" /> + <ImportGroup Label="ExtensionTargets"> + </ImportGroup> +</Project>
\ No newline at end of file diff --git a/build/visual-studio/example-base/example-base.vcxproj.filters b/build/visual-studio/example-base/example-base.vcxproj.filters new file mode 100644 index 000000000..dc6cf4236 --- /dev/null +++ b/build/visual-studio/example-base/example-base.vcxproj.filters @@ -0,0 +1,21 @@ +<?xml version="1.0" encoding="utf-8"?> +<Project ToolsVersion="4.0" xmlns="http://schemas.microsoft.com/developer/msbuild/2003"> + <ItemGroup> + <Filter Include="Header Files"> + <UniqueIdentifier>{21EB8090-0D4E-1035-B6D3-48EBA215DCB7}</UniqueIdentifier> + </Filter> + <Filter Include="Source Files"> + <UniqueIdentifier>{E9C7FDCE-D52A-8D73-7EB0-C5296AF258F6}</UniqueIdentifier> + </Filter> + </ItemGroup> + <ItemGroup> + <ClInclude Include="..\..\..\examples\example-base\example-base.h"> + <Filter>Header Files</Filter> + </ClInclude> + </ItemGroup> + <ItemGroup> + <ClCompile Include="..\..\..\examples\example-base\example-base.cpp"> + <Filter>Source Files</Filter> + </ClCompile> + </ItemGroup> +</Project>
\ No newline at end of file diff --git a/build/visual-studio/gfx/gfx.vcxproj b/build/visual-studio/gfx/gfx.vcxproj index 01faf88bd..e24e09e07 100644 --- a/build/visual-studio/gfx/gfx.vcxproj +++ b/build/visual-studio/gfx/gfx.vcxproj @@ -186,7 +186,6 @@ <ClInclude Include="..\..\..\tools\gfx\d3d\d3d-swapchain.h" /> <ClInclude Include="..\..\..\tools\gfx\d3d\d3d-util.h" /> <ClInclude Include="..\..\..\tools\gfx\d3d11\render-d3d11.h" /> - <ClInclude Include="..\..\..\tools\gfx\d3d12\circular-resource-heap-d3d12.h" /> <ClInclude Include="..\..\..\tools\gfx\d3d12\descriptor-heap-d3d12.h" /> <ClInclude Include="..\..\..\tools\gfx\d3d12\render-d3d12.h" /> <ClInclude Include="..\..\..\tools\gfx\d3d12\resource-d3d12.h" /> @@ -197,6 +196,7 @@ <ClInclude Include="..\..\..\tools\gfx\open-gl\render-gl.h" /> <ClInclude Include="..\..\..\tools\gfx\renderer-shared.h" /> <ClInclude Include="..\..\..\tools\gfx\simple-render-pass-layout.h" /> + <ClInclude Include="..\..\..\tools\gfx\simple-transient-resource-heap.h" /> <ClInclude Include="..\..\..\tools\gfx\slang-context.h" /> <ClInclude Include="..\..\..\tools\gfx\vulkan\render-vk.h" /> <ClInclude Include="..\..\..\tools\gfx\vulkan\vk-api.h" /> @@ -211,7 +211,6 @@ <ClCompile Include="..\..\..\tools\gfx\d3d\d3d-swapchain.cpp" /> <ClCompile Include="..\..\..\tools\gfx\d3d\d3d-util.cpp" /> <ClCompile Include="..\..\..\tools\gfx\d3d11\render-d3d11.cpp" /> - <ClCompile Include="..\..\..\tools\gfx\d3d12\circular-resource-heap-d3d12.cpp" /> <ClCompile Include="..\..\..\tools\gfx\d3d12\descriptor-heap-d3d12.cpp" /> <ClCompile Include="..\..\..\tools\gfx\d3d12\render-d3d12.cpp" /> <ClCompile Include="..\..\..\tools\gfx\d3d12\resource-d3d12.cpp" /> diff --git a/build/visual-studio/gfx/gfx.vcxproj.filters b/build/visual-studio/gfx/gfx.vcxproj.filters index 99af1fbbd..fa859d6ac 100644 --- a/build/visual-studio/gfx/gfx.vcxproj.filters +++ b/build/visual-studio/gfx/gfx.vcxproj.filters @@ -30,9 +30,6 @@ <ClInclude Include="..\..\..\tools\gfx\d3d11\render-d3d11.h"> <Filter>Header Files</Filter> </ClInclude> - <ClInclude Include="..\..\..\tools\gfx\d3d12\circular-resource-heap-d3d12.h"> - <Filter>Header Files</Filter> - </ClInclude> <ClInclude Include="..\..\..\tools\gfx\d3d12\descriptor-heap-d3d12.h"> <Filter>Header Files</Filter> </ClInclude> @@ -63,6 +60,9 @@ <ClInclude Include="..\..\..\tools\gfx\simple-render-pass-layout.h"> <Filter>Header Files</Filter> </ClInclude> + <ClInclude Include="..\..\..\tools\gfx\simple-transient-resource-heap.h"> + <Filter>Header Files</Filter> + </ClInclude> <ClInclude Include="..\..\..\tools\gfx\slang-context.h"> <Filter>Header Files</Filter> </ClInclude> @@ -101,9 +101,6 @@ <ClCompile Include="..\..\..\tools\gfx\d3d11\render-d3d11.cpp"> <Filter>Source Files</Filter> </ClCompile> - <ClCompile Include="..\..\..\tools\gfx\d3d12\circular-resource-heap-d3d12.cpp"> - <Filter>Source Files</Filter> - </ClCompile> <ClCompile Include="..\..\..\tools\gfx\d3d12\descriptor-heap-d3d12.cpp"> <Filter>Source Files</Filter> </ClCompile> diff --git a/build/visual-studio/gpu-printing/gpu-printing.vcxproj b/build/visual-studio/gpu-printing/gpu-printing.vcxproj index b55e289d1..4ab526230 100644 --- a/build/visual-studio/gpu-printing/gpu-printing.vcxproj +++ b/build/visual-studio/gpu-printing/gpu-printing.vcxproj @@ -174,12 +174,12 @@ <None Include="..\..\..\examples\gpu-printing\printing.slang" /> </ItemGroup> <ItemGroup> + <ProjectReference Include="..\example-base\example-base.vcxproj"> + <Project>{37BED5B5-23FA-D81F-8C0C-F1167867813A}</Project> + </ProjectReference> <ProjectReference Include="..\slang\slang.vcxproj"> <Project>{DB00DA62-0533-4AFD-B59F-A67D5B3A0808}</Project> </ProjectReference> - <ProjectReference Include="..\core\core.vcxproj"> - <Project>{F9BE7957-8399-899E-0C49-E714FDDD4B65}</Project> - </ProjectReference> <ProjectReference Include="..\gfx\gfx.vcxproj"> <Project>{222F7498-B40C-4F3F-A704-DDEB91A4484A}</Project> </ProjectReference> @@ -189,6 +189,9 @@ <ProjectReference Include="..\platform\platform.vcxproj"> <Project>{3565FE5E-4FA3-11EB-AE93-0242AC130002}</Project> </ProjectReference> + <ProjectReference Include="..\core\core.vcxproj"> + <Project>{F9BE7957-8399-899E-0C49-E714FDDD4B65}</Project> + </ProjectReference> </ItemGroup> <Import Project="$(VCTargetsPath)\Microsoft.Cpp.targets" /> <ImportGroup Label="ExtensionTargets"> diff --git a/build/visual-studio/hello-world/hello-world.vcxproj b/build/visual-studio/hello-world/hello-world.vcxproj index 28f8d1425..7164fd9e6 100644 --- a/build/visual-studio/hello-world/hello-world.vcxproj +++ b/build/visual-studio/hello-world/hello-world.vcxproj @@ -168,12 +168,12 @@ <None Include="..\..\..\examples\hello-world\shaders.slang" /> </ItemGroup> <ItemGroup> + <ProjectReference Include="..\example-base\example-base.vcxproj"> + <Project>{37BED5B5-23FA-D81F-8C0C-F1167867813A}</Project> + </ProjectReference> <ProjectReference Include="..\slang\slang.vcxproj"> <Project>{DB00DA62-0533-4AFD-B59F-A67D5B3A0808}</Project> </ProjectReference> - <ProjectReference Include="..\core\core.vcxproj"> - <Project>{F9BE7957-8399-899E-0C49-E714FDDD4B65}</Project> - </ProjectReference> <ProjectReference Include="..\gfx\gfx.vcxproj"> <Project>{222F7498-B40C-4F3F-A704-DDEB91A4484A}</Project> </ProjectReference> @@ -183,6 +183,9 @@ <ProjectReference Include="..\platform\platform.vcxproj"> <Project>{3565FE5E-4FA3-11EB-AE93-0242AC130002}</Project> </ProjectReference> + <ProjectReference Include="..\core\core.vcxproj"> + <Project>{F9BE7957-8399-899E-0C49-E714FDDD4B65}</Project> + </ProjectReference> </ItemGroup> <Import Project="$(VCTargetsPath)\Microsoft.Cpp.targets" /> <ImportGroup Label="ExtensionTargets"> diff --git a/build/visual-studio/shader-object/shader-object.vcxproj b/build/visual-studio/shader-object/shader-object.vcxproj index 04315f910..a65b7e544 100644 --- a/build/visual-studio/shader-object/shader-object.vcxproj +++ b/build/visual-studio/shader-object/shader-object.vcxproj @@ -168,12 +168,12 @@ <None Include="..\..\..\examples\shader-object\shader-object.slang" /> </ItemGroup> <ItemGroup> + <ProjectReference Include="..\example-base\example-base.vcxproj"> + <Project>{37BED5B5-23FA-D81F-8C0C-F1167867813A}</Project> + </ProjectReference> <ProjectReference Include="..\slang\slang.vcxproj"> <Project>{DB00DA62-0533-4AFD-B59F-A67D5B3A0808}</Project> </ProjectReference> - <ProjectReference Include="..\core\core.vcxproj"> - <Project>{F9BE7957-8399-899E-0C49-E714FDDD4B65}</Project> - </ProjectReference> <ProjectReference Include="..\gfx\gfx.vcxproj"> <Project>{222F7498-B40C-4F3F-A704-DDEB91A4484A}</Project> </ProjectReference> @@ -183,6 +183,9 @@ <ProjectReference Include="..\platform\platform.vcxproj"> <Project>{3565FE5E-4FA3-11EB-AE93-0242AC130002}</Project> </ProjectReference> + <ProjectReference Include="..\core\core.vcxproj"> + <Project>{F9BE7957-8399-899E-0C49-E714FDDD4B65}</Project> + </ProjectReference> </ItemGroup> <Import Project="$(VCTargetsPath)\Microsoft.Cpp.targets" /> <ImportGroup Label="ExtensionTargets"> diff --git a/build/visual-studio/shader-toy/shader-toy.vcxproj b/build/visual-studio/shader-toy/shader-toy.vcxproj index 823841a46..d1aad7d70 100644 --- a/build/visual-studio/shader-toy/shader-toy.vcxproj +++ b/build/visual-studio/shader-toy/shader-toy.vcxproj @@ -169,12 +169,12 @@ <None Include="..\..\..\examples\shader-toy\shader-toy.slang" /> </ItemGroup> <ItemGroup> + <ProjectReference Include="..\example-base\example-base.vcxproj"> + <Project>{37BED5B5-23FA-D81F-8C0C-F1167867813A}</Project> + </ProjectReference> <ProjectReference Include="..\slang\slang.vcxproj"> <Project>{DB00DA62-0533-4AFD-B59F-A67D5B3A0808}</Project> </ProjectReference> - <ProjectReference Include="..\core\core.vcxproj"> - <Project>{F9BE7957-8399-899E-0C49-E714FDDD4B65}</Project> - </ProjectReference> <ProjectReference Include="..\gfx\gfx.vcxproj"> <Project>{222F7498-B40C-4F3F-A704-DDEB91A4484A}</Project> </ProjectReference> @@ -184,6 +184,9 @@ <ProjectReference Include="..\platform\platform.vcxproj"> <Project>{3565FE5E-4FA3-11EB-AE93-0242AC130002}</Project> </ProjectReference> + <ProjectReference Include="..\core\core.vcxproj"> + <Project>{F9BE7957-8399-899E-0C49-E714FDDD4B65}</Project> + </ProjectReference> </ItemGroup> <Import Project="$(VCTargetsPath)\Microsoft.Cpp.targets" /> <ImportGroup Label="ExtensionTargets"> diff --git a/examples/example-base/example-base.cpp b/examples/example-base/example-base.cpp new file mode 100644 index 000000000..b06bbdf7e --- /dev/null +++ b/examples/example-base/example-base.cpp @@ -0,0 +1,160 @@ +#include "example-base.h" + +using namespace Slang; +using namespace gfx; + +Slang::Result WindowedAppBase::initializeBase(const char* title, int width, int height) +{ + // 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. + IDevice::Desc deviceDesc = {}; + gfx::Result res = gfxCreateDevice(&deviceDesc, gDevice.writeRef()); + 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::RGBA_Unorm_UInt8; + 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::AttachmentLayout renderTargetLayout = {gSwapchain->getDesc().format, 1}; + IFramebufferLayout::AttachmentLayout depthLayout = {gfx::Format::D_Float32, 1}; + IFramebufferLayout::Desc framebufferLayoutDesc; + framebufferLayoutDesc.renderTargetCount = 1; + framebufferLayoutDesc.renderTargets = &renderTargetLayout; + framebufferLayoutDesc.depthStencil = &depthLayout; + SLANG_RETURN_ON_FAIL( + gDevice->createFramebufferLayout(framebufferLayoutDesc, gFramebufferLayout.writeRef())); + + createSwapchainFramebuffers(); + + for (uint32_t i = 0; i < kSwapchainImageCount; i++) + { + gfx::ITransientResourceHeap::Desc transientHeapDesc; + transientHeapDesc.constantBufferSize = 4096 * 1024; + auto transientHeap = gDevice->createTransientResourceHeap(transientHeapDesc); + gTransientHeaps.add(transientHeap); + } + + gfx::IRenderPassLayout::Desc renderPassDesc = {}; + renderPassDesc.framebufferLayout = gFramebufferLayout; + renderPassDesc.renderTargetCount = 1; + IRenderPassLayout::AttachmentAccessDesc renderTargetAccess = {}; + IRenderPassLayout::AttachmentAccessDesc depthStencilAccess = {}; + renderTargetAccess.loadOp = IRenderPassLayout::AttachmentLoadOp::Clear; + renderTargetAccess.storeOp = IRenderPassLayout::AttachmentStoreOp::Store; + renderTargetAccess.initialState = ResourceState::Undefined; + renderTargetAccess.finalState = ResourceState::Present; + depthStencilAccess.loadOp = IRenderPassLayout::AttachmentLoadOp::Clear; + depthStencilAccess.storeOp = IRenderPassLayout::AttachmentStoreOp::Store; + depthStencilAccess.initialState = ResourceState::Undefined; + depthStencilAccess.finalState = ResourceState::DepthWrite; + renderPassDesc.renderTargetAccess = &renderTargetAccess; + renderPassDesc.depthStencilAccess = &depthStencilAccess; + gRenderPass = gDevice->createRenderPassLayout(renderPassDesc); + + return SLANG_OK; +} + +void WindowedAppBase::mainLoop() +{ + int frameBufferIndex = gSwapchain->acquireNextImage(); + if (frameBufferIndex == -1) + return; + + gTransientHeaps[frameBufferIndex]->synchronizeAndReset(); + renderFrame(frameBufferIndex); +} + +void WindowedAppBase::createSwapchainFramebuffers() +{ + gFramebuffers.clear(); + for (uint32_t i = 0; i < kSwapchainImageCount; i++) + { + gfx::ITextureResource::Desc depthBufferDesc; + depthBufferDesc.setDefaults(gfx::IResource::Usage::DepthWrite); + depthBufferDesc.init2D( + gfx::IResource::Type::Texture2D, + gfx::Format::D_Float32, + gSwapchain->getDesc().width, + gSwapchain->getDesc().height, + 0); + + ComPtr<gfx::ITextureResource> depthBufferResource = gDevice->createTextureResource( + gfx::IResource::Usage::DepthWrite, depthBufferDesc, nullptr); + ComPtr<gfx::ITextureResource> colorBuffer; + gSwapchain->getImage(i, colorBuffer.writeRef()); + + gfx::IResourceView::Desc colorBufferViewDesc; + memset(&colorBufferViewDesc, 0, sizeof(colorBufferViewDesc)); + colorBufferViewDesc.format = gSwapchain->getDesc().format; + colorBufferViewDesc.renderTarget.shape = gfx::IResource::Type::Texture2D; + colorBufferViewDesc.type = gfx::IResourceView::Type::RenderTarget; + ComPtr<gfx::IResourceView> rtv = + gDevice->createTextureView(colorBuffer.get(), colorBufferViewDesc); + + gfx::IResourceView::Desc depthBufferViewDesc; + memset(&depthBufferViewDesc, 0, sizeof(depthBufferViewDesc)); + depthBufferViewDesc.format = gfx::Format::D_Float32; + depthBufferViewDesc.renderTarget.shape = gfx::IResource::Type::Texture2D; + depthBufferViewDesc.type = gfx::IResourceView::Type::DepthStencil; + ComPtr<gfx::IResourceView> dsv = + gDevice->createTextureView(depthBufferResource.get(), depthBufferViewDesc); + + gfx::IFramebuffer::Desc framebufferDesc; + framebufferDesc.renderTargetCount = 1; + framebufferDesc.depthStencilView = dsv.get(); + framebufferDesc.renderTargetViews = rtv.readRef(); + framebufferDesc.layout = gFramebufferLayout; + ComPtr<gfx::IFramebuffer> frameBuffer = gDevice->createFramebuffer(framebufferDesc); + gFramebuffers.add(frameBuffer); + } +} + +void WindowedAppBase::windowSizeChanged() +{ + // Wait for the GPU to finish. + gQueue->wait(); + + auto clientRect = gWindow->getClientRect(); + if (clientRect.width > 0 && clientRect.height > 0) + { + // Free all framebuffers before resizing swapchain. + gFramebuffers = decltype(gFramebuffers)(); + + // Resize swapchain. + if (gSwapchain->resize(clientRect.width, clientRect.height) == SLANG_OK) + { + // Recreate framebuffers for each swapchain back buffer image. + createSwapchainFramebuffers(); + windowWidth = clientRect.width; + windowHeight = clientRect.height; + } + } +} diff --git a/examples/example-base/example-base.h b/examples/example-base/example-base.h new file mode 100644 index 000000000..ab7522f06 --- /dev/null +++ b/examples/example-base/example-base.h @@ -0,0 +1,52 @@ +#pragma once + +#include "slang-gfx.h" +#include "tools/platform/window.h" +#include "source/core/slang-basic.h" + +struct WindowedAppBase +{ +protected: + static const int kSwapchainImageCount = 2; + + Slang::RefPtr<platform::Window> gWindow; + uint32_t windowWidth; + uint32_t windowHeight; + + Slang::ComPtr<gfx::IDevice> gDevice; + + Slang::ComPtr<gfx::ISwapchain> gSwapchain; + Slang::ComPtr<gfx::IFramebufferLayout> gFramebufferLayout; + Slang::List<Slang::ComPtr<gfx::IFramebuffer>> gFramebuffers; + Slang::List<Slang::ComPtr<gfx::ITransientResourceHeap>> gTransientHeaps; + Slang::ComPtr<gfx::IRenderPassLayout> gRenderPass; + Slang::ComPtr<gfx::ICommandQueue> gQueue; + + Slang::Result initializeBase(const char* titile, int width, int height); + void createSwapchainFramebuffers(); + void mainLoop(); + + virtual void windowSizeChanged(); + +protected: + virtual void renderFrame(int framebufferIndex) = 0; +public: + platform::Window* getWindow() { return gWindow.Ptr(); } + virtual void finalize() { gQueue->wait(); } +}; + +template<typename TApp> +int innerMain() +{ + TApp app; + + if (SLANG_FAILED(app.initialize())) + { + return -1; + } + + platform::Application::run(app.getWindow()); + + app.finalize(); + return 0; +} diff --git a/examples/gpu-printing/main.cpp b/examples/gpu-printing/main.cpp index 0315d4827..a0acb8159 100644 --- a/examples/gpu-printing/main.cpp +++ b/examples/gpu-printing/main.cpp @@ -113,9 +113,13 @@ Result execute() printBufferViewDesc.type = IResourceView::Type::UnorderedAccess; auto printBufferView = gDevice->createBufferView(printBuffer, printBufferViewDesc); + ITransientResourceHeap::Desc transientResourceHeapDesc = {}; + transientResourceHeapDesc.constantBufferSize = 256; + auto transientHeap = gDevice->createTransientResourceHeap(transientResourceHeapDesc); + ICommandQueue::Desc queueDesc = {ICommandQueue::QueueType::Graphics}; auto queue = gDevice->createCommandQueue(queueDesc); - auto commandBuffer = queue->createCommandBuffer(); + auto commandBuffer = transientHeap->createCommandBuffer(); auto encoder = commandBuffer->encodeComputeCommands(); auto rootShaderObject = gDevice->createRootShaderObject(gProgram); encoder->setPipelineState(gPipelineState); diff --git a/examples/hello-world/main.cpp b/examples/hello-world/main.cpp index b3bee9c07..19413948a 100644 --- a/examples/hello-world/main.cpp +++ b/examples/hello-world/main.cpp @@ -37,6 +37,7 @@ #include "tools/platform/window.h" #include "slang-com-ptr.h" #include "source/core/slang-basic.h" +#include "examples/example-base/example-base.h" using namespace gfx; using namespace Slang; @@ -62,7 +63,7 @@ static const Vertex kVertexData[kVertexCount] = // The example application will be implemented as a `struct`, so that // we can scope the resources it allocates without using global variables. // -struct HelloWorld +struct HelloWorld : public WindowedAppBase { // Many Slang API functions return detailed diagnostic information @@ -197,12 +198,6 @@ gfx::Result loadShaderProgram( // we have just defined. // -// We will hard-code the size of our rendering window. -// -int gWindowWidth = 1024; -int gWindowHeight = 768; -const uint32_t kSwapchainImageCount = 2; - // We will define global variables for the various platform and // graphics API objects that our application needs: // @@ -210,16 +205,9 @@ const uint32_t kSwapchainImageCount = 2; // of them come from the utility library we are using to simplify // building an example program. // -RefPtr<platform::Window> gWindow; -Slang::ComPtr<gfx::IDevice> gDevice; - ComPtr<gfx::IPipelineState> gPipelineState; ComPtr<gfx::IShaderObject> gRootObject; -ComPtr<gfx::ISwapchain> gSwapchain; -List<ComPtr<gfx::IFramebuffer>> gFramebuffers; ComPtr<gfx::IBufferResource> gVertexBuffer; -ComPtr<gfx::IRenderPassLayout> gRenderPass; -ComPtr<gfx::ICommandQueue> gQueue; // Now that we've covered the function that actually loads and // compiles our Slang shade code, we can go through the rest @@ -229,29 +217,9 @@ Slang::Result initialize() { // Create a window for our application to render into. // - platform::WindowDesc windowDesc; - windowDesc.title = "Hello, World!"; - windowDesc.width = gWindowWidth; - windowDesc.height = gWindowHeight; - windowDesc.style = platform::WindowStyle::FixedSize; - gWindow = platform::Application::createWindow(windowDesc); - gWindow->events.mainLoop = [this]() { renderFrame(); }; - // Initialize the rendering layer. - // - // Note: for now we are hard-coding logic to use the - // Direct3D11 back-end for the graphics API abstraction. - // A future version of this example may support multiple - // platforms/APIs. - // - IDevice::Desc deviceDesc = {}; - gfx::Result res = gfxCreateDevice(&deviceDesc, gDevice.writeRef()); - if(SLANG_FAILED(res)) return res; - - ICommandQueue::Desc queueDesc = {}; - queueDesc.type = ICommandQueue::QueueType::Graphics; - gQueue = gDevice->createCommandQueue(queueDesc); - - // Now we will create objects needed to configur the "input assembler" + initializeBase("hello-world", 1024, 768); + + // We will create objects needed to configur the "input assembler" // (IA) stage of the D3D pipeline. // // First, we create an input layout: @@ -315,95 +283,19 @@ Slang::Result initialize() SLANG_RETURN_ON_FAIL(gDevice->createRootShaderObject(shaderProgram, rootObject.writeRef())); gRootObject = rootObject; - // Create swapchain and framebuffers. - gfx::ISwapchain::Desc swapchainDesc = {}; - swapchainDesc.format = gfx::Format::RGBA_Unorm_UInt8; - swapchainDesc.width = gWindowWidth; - swapchainDesc.height = gWindowHeight; - swapchainDesc.imageCount = kSwapchainImageCount; - swapchainDesc.queue = gQueue; - gfx::WindowHandle windowHandle = gWindow->getNativeHandle().convert<gfx::WindowHandle>(); - gSwapchain = gDevice->createSwapchain(swapchainDesc, windowHandle); - - IFramebufferLayout::AttachmentLayout renderTargetLayout = {gSwapchain->getDesc().format, 1}; - IFramebufferLayout::AttachmentLayout depthLayout = {gfx::Format::D_Float32, 1}; - IFramebufferLayout::Desc framebufferLayoutDesc; - framebufferLayoutDesc.renderTargetCount = 1; - framebufferLayoutDesc.renderTargets = &renderTargetLayout; - framebufferLayoutDesc.depthStencil = &depthLayout; - ComPtr<IFramebufferLayout> framebufferLayout; - SLANG_RETURN_ON_FAIL( - gDevice->createFramebufferLayout(framebufferLayoutDesc, framebufferLayout.writeRef())); - - for (uint32_t i = 0; i < kSwapchainImageCount; i++) - { - gfx::ITextureResource::Desc depthBufferDesc = {}; - depthBufferDesc.setDefaults(gfx::IResource::Usage::DepthWrite); - depthBufferDesc.init2D( - gfx::IResource::Type::Texture2D, - gfx::Format::D_Float32, - gSwapchain->getDesc().width, - gSwapchain->getDesc().height, - 0); - - ComPtr<gfx::ITextureResource> depthBufferResource = gDevice->createTextureResource( - gfx::IResource::Usage::DepthWrite, depthBufferDesc, nullptr); - ComPtr<gfx::ITextureResource> colorBuffer; - gSwapchain->getImage(i, colorBuffer.writeRef()); - - gfx::IResourceView::Desc colorBufferViewDesc = {}; - colorBufferViewDesc.format = gSwapchain->getDesc().format; - colorBufferViewDesc.renderTarget.shape = gfx::IResource::Type::Texture2D; - colorBufferViewDesc.type = gfx::IResourceView::Type::RenderTarget; - ComPtr<gfx::IResourceView> rtv = - gDevice->createTextureView(colorBuffer.get(), colorBufferViewDesc); - - gfx::IResourceView::Desc depthBufferViewDesc = {}; - depthBufferViewDesc.format = gfx::Format::D_Float32; - depthBufferViewDesc.renderTarget.shape = gfx::IResource::Type::Texture2D; - depthBufferViewDesc.type = gfx::IResourceView::Type::DepthStencil; - ComPtr<gfx::IResourceView> dsv = - gDevice->createTextureView(depthBufferResource.get(), depthBufferViewDesc); - - gfx::IFramebuffer::Desc framebufferDesc = {}; - framebufferDesc.renderTargetCount = 1; - framebufferDesc.depthStencilView = dsv.get(); - framebufferDesc.renderTargetViews = rtv.readRef(); - framebufferDesc.layout = framebufferLayout; - ComPtr<gfx::IFramebuffer> frameBuffer = gDevice->createFramebuffer(framebufferDesc); - gFramebuffers.add(frameBuffer); - } - // Following the D3D12/Vulkan style of API, we need a pipeline state object // (PSO) to encapsulate the configuration of the overall graphics pipeline. // GraphicsPipelineStateDesc desc; desc.inputLayout = inputLayout; desc.program = shaderProgram; - desc.framebufferLayout = framebufferLayout; + desc.framebufferLayout = gFramebufferLayout; auto pipelineState = gDevice->createGraphicsPipelineState(desc); if (!pipelineState) return SLANG_FAIL; gPipelineState = pipelineState; - gfx::IRenderPassLayout::Desc renderPassDesc = {}; - renderPassDesc.framebufferLayout = framebufferLayout; - renderPassDesc.renderTargetCount = 1; - IRenderPassLayout::AttachmentAccessDesc renderTargetAccess = {}; - IRenderPassLayout::AttachmentAccessDesc depthStencilAccess = {}; - renderTargetAccess.loadOp = IRenderPassLayout::AttachmentLoadOp::Clear; - renderTargetAccess.storeOp = IRenderPassLayout::AttachmentStoreOp::Store; - renderTargetAccess.initialState = ResourceState::Undefined; - renderTargetAccess.finalState = ResourceState::Present; - depthStencilAccess.loadOp = IRenderPassLayout::AttachmentLoadOp::Clear; - depthStencilAccess.storeOp = IRenderPassLayout::AttachmentStoreOp::Store; - depthStencilAccess.initialState = ResourceState::Undefined; - depthStencilAccess.finalState = ResourceState::DepthWrite; - renderPassDesc.renderTargetAccess = &renderTargetAccess; - renderPassDesc.depthStencilAccess = &depthStencilAccess; - gRenderPass = gDevice->createRenderPassLayout(renderPassDesc); - return SLANG_OK; } @@ -412,17 +304,15 @@ Slang::Result initialize() // nothing really Slang-specific here, so the commentary doesn't need // to be very detailed. // -void renderFrame() +virtual void renderFrame(int frameBufferIndex) override { - uint32_t frameBufferIndex = gSwapchain->acquireNextImage(); - - ComPtr<ICommandBuffer> commandBuffer = gQueue->createCommandBuffer(); + ComPtr<ICommandBuffer> commandBuffer = gTransientHeaps[frameBufferIndex]->createCommandBuffer(); auto renderEncoder = commandBuffer->encodeRenderCommands(gRenderPass, gFramebuffers[frameBufferIndex]); gfx::Viewport viewport = {}; viewport.maxZ = 1.0f; - viewport.extentX = (float)gWindowWidth; - viewport.extentY = (float)gWindowHeight; + viewport.extentX = (float)windowWidth; + viewport.extentY = (float)windowHeight; renderEncoder->setViewportAndScissor(viewport); // We will update the model-view-projection matrix that is passed @@ -510,39 +400,8 @@ void renderFrame() gSwapchain->present(); } -void finalize() -{ - gQueue->wait(); - gSwapchain = nullptr; -} - }; -// This "inner" main function is used by the platform abstraction -// layer to deal with differences in how an entry point needs -// to be defined for different platforms. -// -int innerMain() -{ - // We construct an instance of our example application - // `struct` type, and then walk through the lifecyle - // of the application. - - HelloWorld app; - - if (SLANG_FAILED(app.initialize())) - { - return -1; - } - - platform::Application::run(app.gWindow); - - app.finalize(); - - return 0; -} - // This macro instantiates an appropriate main function to -// invoke the `innerMain` above. -// -PLATFORM_UI_MAIN(innerMain) +// run the application defined above. +PLATFORM_UI_MAIN(innerMain<HelloWorld>) diff --git a/examples/shader-object/main.cpp b/examples/shader-object/main.cpp index aaafc010a..9329a5418 100644 --- a/examples/shader-object/main.cpp +++ b/examples/shader-object/main.cpp @@ -136,8 +136,15 @@ int main() // interacting with the graphics API. Slang::ComPtr<gfx::IDevice> device; IDevice::Desc deviceDesc = {}; + deviceDesc.deviceType = DeviceType::Vulkan; SLANG_RETURN_ON_FAIL(gfxCreateDevice(&deviceDesc, device.writeRef())); + Slang::ComPtr<gfx::ITransientResourceHeap> transientHeap; + ITransientResourceHeap::Desc transientHeapDesc = {}; + transientHeapDesc.constantBufferSize = 4096; + SLANG_RETURN_ON_FAIL( + device->createTransientResourceHeap(transientHeapDesc, transientHeap.writeRef())); + // Now we can load the shader code. // A `gfx::IShaderProgram` object for use in the `gfx` layer. ComPtr<gfx::IShaderProgram> shaderProgram; @@ -212,7 +219,8 @@ int main() { ICommandQueue::Desc queueDesc = {ICommandQueue::QueueType::Graphics}; auto queue = device->createCommandQueue(queueDesc); - auto commandBuffer = queue->createCommandBuffer(); + + auto commandBuffer = transientHeap->createCommandBuffer(); auto encoder = commandBuffer->encodeComputeCommands(); encoder->setPipelineState(pipelineState); encoder->bindRootShaderObject(rootObject); diff --git a/examples/shader-toy/main.cpp b/examples/shader-toy/main.cpp index c4424b294..a142c3c15 100644 --- a/examples/shader-toy/main.cpp +++ b/examples/shader-toy/main.cpp @@ -23,6 +23,7 @@ using Slang::ComPtr; #include "tools/gfx-util/shader-cursor.h" #include "tools/platform/window.h" #include "tools/platform/performance-counter.h" +#include "examples/example-base/example-base.h" #include "source/core/slang-basic.h" #include <chrono> @@ -54,7 +55,7 @@ const FullScreenTriangle::Vertex FullScreenTriangle::kVertices[FullScreenTriangl // The application itself will be encapsulated in a C++ `struct` type // so that it can easily scope its state without use of global variables. // -struct ShaderToyApp +struct ShaderToyApp : public WindowedAppBase { // The uniform data used by the shader is defined here as a simple @@ -284,48 +285,17 @@ Result loadShaderProgram(gfx::IDevice* device, ComPtr<gfx::IShaderProgram>& outS return SLANG_OK; } -int gWindowWidth = 1024; -int gWindowHeight = 768; -static const uint32_t kSwapchainImageCount = 2; - -Slang::RefPtr<platform::Window> gWindow; -Slang::ComPtr<gfx::IDevice> gDevice; ComPtr<IShaderProgram> gShaderProgram; ComPtr<gfx::IShaderObject> gRootObject[kSwapchainImageCount]; -ComPtr<gfx::IFramebufferLayout> gFramebufferLayout; ComPtr<gfx::IPipelineState> gPipelineState; ComPtr<gfx::IBufferResource> gVertexBuffer; -ComPtr<gfx::ISwapchain> gSwapchain; -Slang::List<ComPtr<gfx::IFramebuffer>> gFramebuffers; -ComPtr<gfx::IRenderPassLayout> gRenderPass; -ComPtr<gfx::ICommandQueue> gQueue; Result initialize() { - platform::WindowDesc windowDesc; - const char* title = "Slang Shader Toy"; - windowDesc.title = title; - windowDesc.width = gWindowWidth; - windowDesc.height = gWindowHeight; - gWindow = platform::Application::createWindow(windowDesc); - gWindow->events.mainLoop = [this]() { renderFrame(); }; + initializeBase("Shader Toy", 1024, 768); gWindow->events.mouseMove = [this](const platform::MouseEventArgs& e) { handleEvent(e); }; gWindow->events.mouseUp = [this](const platform::MouseEventArgs& e) { handleEvent(e); }; gWindow->events.mouseDown = [this](const platform::MouseEventArgs& e) { handleEvent(e); }; - gWindow->events.sizeChanged = Slang::Action<>(this, &ShaderToyApp::windowSizeChanged); - - IDevice::Desc deviceDesc; - Result res = gfxCreateDevice(&deviceDesc, gDevice.writeRef()); - 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); InputElementDesc inputElements[] = { { "POSITION", 0, Format::RG_Float32, offsetof(FullScreenTriangle::Vertex, position) }, @@ -346,26 +316,6 @@ Result initialize() SLANG_RETURN_ON_FAIL(loadShaderProgram(gDevice, gShaderProgram)); - // Create swapchain and framebuffers. - gfx::ISwapchain::Desc swapchainDesc = {}; - swapchainDesc.format = gfx::Format::RGBA_Unorm_UInt8; - swapchainDesc.width = gWindowWidth; - swapchainDesc.height = gWindowHeight; - swapchainDesc.imageCount = kSwapchainImageCount; - swapchainDesc.queue = gQueue; - gSwapchain = gDevice->createSwapchain(swapchainDesc, gWindow->getNativeHandle().convert<gfx::WindowHandle>()); - - IFramebufferLayout::AttachmentLayout renderTargetLayout = {gSwapchain->getDesc().format, 1}; - IFramebufferLayout::AttachmentLayout depthLayout = {gfx::Format::D_Float32, 1}; - IFramebufferLayout::Desc framebufferLayoutDesc; - framebufferLayoutDesc.renderTargetCount = 1; - framebufferLayoutDesc.renderTargets = &renderTargetLayout; - framebufferLayoutDesc.depthStencil = &depthLayout; - SLANG_RETURN_ON_FAIL( - gDevice->createFramebufferLayout(framebufferLayoutDesc, gFramebufferLayout.writeRef())); - - createSwapchainFramebuffers(); - // Create pipeline. GraphicsPipelineStateDesc desc; desc.inputLayout = inputLayout; @@ -377,23 +327,6 @@ Result initialize() gPipelineState = pipelineState; - // Create render pass. - gfx::IRenderPassLayout::Desc renderPassDesc = {}; - renderPassDesc.framebufferLayout = gFramebufferLayout; - renderPassDesc.renderTargetCount = 1; - IRenderPassLayout::AttachmentAccessDesc renderTargetAccess = {}; - IRenderPassLayout::AttachmentAccessDesc depthStencilAccess = {}; - renderTargetAccess.loadOp = IRenderPassLayout::AttachmentLoadOp::Clear; - renderTargetAccess.storeOp = IRenderPassLayout::AttachmentStoreOp::Store; - renderTargetAccess.initialState = ResourceState::Undefined; - renderTargetAccess.finalState = ResourceState::Present; - depthStencilAccess.loadOp = IRenderPassLayout::AttachmentLoadOp::Clear; - depthStencilAccess.storeOp = IRenderPassLayout::AttachmentStoreOp::Store; - depthStencilAccess.initialState = ResourceState::Undefined; - depthStencilAccess.finalState = ResourceState::DepthWrite; - renderPassDesc.renderTargetAccess = &renderTargetAccess; - renderPassDesc.depthStencilAccess = &depthStencilAccess; - gRenderPass = gDevice->createRenderPassLayout(renderPassDesc); return SLANG_OK; } @@ -407,13 +340,9 @@ float clickMouseY = 0.0f; bool firstTime = true; platform::TimePoint startTime; -void renderFrame() +virtual void renderFrame(int frameIndex) override { - auto frameIndex = gSwapchain->acquireNextImage(); - if (frameIndex == -1) - return; - - auto commandBuffer = gQueue->createCommandBuffer(); + auto commandBuffer = gTransientHeaps[frameIndex]->createCommandBuffer(); if( firstTime ) { startTime = platform::PerformanceCounter::now(); @@ -438,8 +367,8 @@ void renderFrame() uniforms.iMouse[2] = isMouseDown ? clickMouseX : -clickMouseX; uniforms.iMouse[3] = isMouseClick ? clickMouseY : -clickMouseY; uniforms.iTime = platform::PerformanceCounter::getElapsedTimeInSeconds(startTime); - uniforms.iResolution[0] = float(gWindowWidth); - uniforms.iResolution[1] = float(gWindowHeight); + uniforms.iResolution[0] = float(windowWidth); + uniforms.iResolution[1] = float(windowHeight); } gRootObject[frameIndex] = gDevice->createRootShaderObject(gShaderProgram); @@ -451,8 +380,8 @@ void renderFrame() gfx::Viewport viewport = {}; viewport.maxZ = 1.0f; - viewport.extentX = (float)gWindowWidth; - viewport.extentY = (float)gWindowHeight; + viewport.extentX = (float)windowWidth; + viewport.extentY = (float)windowHeight; encoder->setViewportAndScissor(viewport); encoder->setPipelineState(gPipelineState); encoder->bindRootShaderObject(gRootObject[frameIndex]); @@ -466,101 +395,14 @@ void renderFrame() gSwapchain->present(); } -void finalize() -{ - gQueue->wait(); -} - void handleEvent(const platform::MouseEventArgs& event) { isMouseDown = ((int)event.buttons & (int)platform::ButtonState::Enum::LeftButton) != 0; lastMouseX = (float)event.x; lastMouseY = (float)event.y; } - -void createSwapchainFramebuffers() -{ - gFramebuffers.clear(); - for (uint32_t i = 0; i < kSwapchainImageCount; i++) - { - gfx::ITextureResource::Desc depthBufferDesc; - depthBufferDesc.setDefaults(gfx::IResource::Usage::DepthWrite); - depthBufferDesc.init2D( - gfx::IResource::Type::Texture2D, - gfx::Format::D_Float32, - gSwapchain->getDesc().width, - gSwapchain->getDesc().height, - 0); - - ComPtr<gfx::ITextureResource> depthBufferResource = gDevice->createTextureResource( - gfx::IResource::Usage::DepthWrite, depthBufferDesc, nullptr); - ComPtr<gfx::ITextureResource> colorBuffer; - gSwapchain->getImage(i, colorBuffer.writeRef()); - - gfx::IResourceView::Desc colorBufferViewDesc; - memset(&colorBufferViewDesc, 0, sizeof(colorBufferViewDesc)); - colorBufferViewDesc.format = gSwapchain->getDesc().format; - colorBufferViewDesc.renderTarget.shape = gfx::IResource::Type::Texture2D; - colorBufferViewDesc.type = gfx::IResourceView::Type::RenderTarget; - ComPtr<gfx::IResourceView> rtv = - gDevice->createTextureView(colorBuffer.get(), colorBufferViewDesc); - - gfx::IResourceView::Desc depthBufferViewDesc; - memset(&depthBufferViewDesc, 0, sizeof(depthBufferViewDesc)); - depthBufferViewDesc.format = gfx::Format::D_Float32; - depthBufferViewDesc.renderTarget.shape = gfx::IResource::Type::Texture2D; - depthBufferViewDesc.type = gfx::IResourceView::Type::DepthStencil; - ComPtr<gfx::IResourceView> dsv = - gDevice->createTextureView(depthBufferResource.get(), depthBufferViewDesc); - - gfx::IFramebuffer::Desc framebufferDesc; - framebufferDesc.renderTargetCount = 1; - framebufferDesc.depthStencilView = dsv.get(); - framebufferDesc.renderTargetViews = rtv.readRef(); - framebufferDesc.layout = gFramebufferLayout; - ComPtr<gfx::IFramebuffer> frameBuffer = gDevice->createFramebuffer(framebufferDesc); - gFramebuffers.add(frameBuffer); - } -} - -void windowSizeChanged() -{ - // Wait for the GPU to finish. - gQueue->wait(); - - auto clientRect = gWindow->getClientRect(); - if (clientRect.width > 0 && clientRect.height > 0) - { - // Free all framebuffers before resizing swapchain. - gFramebuffers = decltype(gFramebuffers)(); - - // Resize swapchain. - if (gSwapchain->resize(clientRect.width, clientRect.height) == SLANG_OK) - { - // Recreate framebuffers for each swapchain back buffer image. - createSwapchainFramebuffers(); - gWindowWidth = clientRect.width; - gWindowHeight = clientRect.height; - } - } -} - }; -int innerMain() -{ - ShaderToyApp app; - - if (SLANG_FAILED(app.initialize())) - { - return -1; - } - - platform::Application::run(app.gWindow); - - app.finalize(); - - return 0; -} - -PLATFORM_UI_MAIN(innerMain) +// This macro instantiates an appropriate main function to +// run the application defined above. +PLATFORM_UI_MAIN(innerMain<ShaderToyApp>) diff --git a/premake5.lua b/premake5.lua index 7052d1de9..c53c4f48a 100644 --- a/premake5.lua +++ b/premake5.lua @@ -548,6 +548,17 @@ function toolSharedLibrary(name) kind "SharedLib" end +function exampleLibrary(name) + group "examples" + baseSlangProject(name, "examples/"..name) + kind "StaticLib" + includedirs { ".", "tools" } + links { "gfx", "slang", "platform", "gfx-util", "core"} + addCUDAIfEnabled(); +end + +exampleLibrary "example-base" + -- Finally we have the example programs that show how to use Slang. -- function example(name) @@ -579,7 +590,7 @@ function example(name) -- and the `gfx` abstraction layer (which in turn -- depends on the `core` library). We specify all of that here, -- rather than in each example. - links { "slang", "core", "gfx", "gfx-util", "platform" } + links { "example-base", "slang", "gfx", "gfx-util", "platform", "core" } if isTargetWindows then else diff --git a/slang-gfx.h b/slang-gfx.h index cdb9f38cb..02b4a9325 100644 --- a/slang-gfx.h +++ b/slang-gfx.h @@ -1242,19 +1242,6 @@ public: }; virtual SLANG_NO_THROW const Desc& SLANG_MCALL getDesc() = 0; - // User must finish recording a command buffer before creating another command buffer. - // Command buffers are one-time use. Once it is submitted to the queue via `executeCommandBuffers` - // a command buffer is no longer valid to be used any more. - // Command buffers must be closed before submission. - virtual SLANG_NO_THROW Result SLANG_MCALL - createCommandBuffer(ICommandBuffer** outCommandBuffer) = 0; - inline ComPtr<ICommandBuffer> createCommandBuffer() - { - ComPtr<ICommandBuffer> result; - SLANG_RETURN_NULL_ON_FAIL(createCommandBuffer(result.writeRef())); - return result; - } - virtual SLANG_NO_THROW void SLANG_MCALL executeCommandBuffers(uint32_t count, ICommandBuffer* const* commandBuffers) = 0; inline void executeCommandBuffer(ICommandBuffer* commandBuffer) @@ -1268,6 +1255,34 @@ public: 0x14e2bed0, 0xad0, 0x4dc8, { 0xb3, 0x41, 0x6, 0x3f, 0xe7, 0x2d, 0xbf, 0xe } \ } +class ITransientResourceHeap : public ISlangUnknown +{ +public: + struct Desc + { + size_t constantBufferSize; + }; + virtual SLANG_NO_THROW Result SLANG_MCALL synchronizeAndReset() = 0; + + // Command buffers are one-time use. Once it is submitted to the queue via + // `executeCommandBuffers` a command buffer is no longer valid to be used any more. Command + // buffers must be closed before submission. The current D3D12 implementation has a limitation + // that only one command buffer maybe recorded at a time. User must finish recording a command + // buffer before creating another command buffer. + virtual SLANG_NO_THROW Result SLANG_MCALL + createCommandBuffer(ICommandBuffer** outCommandBuffer) = 0; + inline ComPtr<ICommandBuffer> createCommandBuffer() + { + ComPtr<ICommandBuffer> result; + SLANG_RETURN_NULL_ON_FAIL(createCommandBuffer(result.writeRef())); + return result; + } +}; +#define SLANG_UUID_ITransientResourceHeap \ + { \ + 0xcd48bd29, 0xee72, 0x41b8, { 0xbc, 0xff, 0xa, 0x2b, 0x3a, 0xaa, 0x6d, 0xeb } \ + } + class ISwapchain : public ISlangUnknown { public: @@ -1364,6 +1379,18 @@ public: getSlangSession(result.writeRef()); return result; } + + virtual SLANG_NO_THROW Result SLANG_MCALL createTransientResourceHeap( + const ITransientResourceHeap::Desc& desc, + ITransientResourceHeap** outHeap) = 0; + inline ComPtr<ITransientResourceHeap> createTransientResourceHeap( + const ITransientResourceHeap::Desc& desc) + { + ComPtr<ITransientResourceHeap> result; + createTransientResourceHeap(desc, result.writeRef()); + return result; + } + /// Create a texture resource. /// /// If `initData` is non-null, then it must point to an array of @@ -7,6 +7,8 @@ Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "examples", "examples", "{EB EndProject Project("{8BC9CEB8-8B4A-11D0-8D11-00A0C91BC942}") = "cpu-hello-world", "build\visual-studio\cpu-hello-world\cpu-hello-world.vcxproj", "{4B47A364-37C4-96A7-6041-97BB4C1D333B}" EndProject +Project("{8BC9CEB8-8B4A-11D0-8D11-00A0C91BC942}") = "example-base", "build\visual-studio\example-base\example-base.vcxproj", "{37BED5B5-23FA-D81F-8C0C-F1167867813A}" +EndProject Project("{8BC9CEB8-8B4A-11D0-8D11-00A0C91BC942}") = "gpu-printing", "build\visual-studio\gpu-printing\gpu-printing.vcxproj", "{57C81DD3-4304-213D-AC16-39349871C957}" EndProject Project("{8BC9CEB8-8B4A-11D0-8D11-00A0C91BC942}") = "hello-world", "build\visual-studio\hello-world\hello-world.vcxproj", "{010BE414-ED5B-CF56-16C0-BD18027062C0}" @@ -81,6 +83,14 @@ Global {4B47A364-37C4-96A7-6041-97BB4C1D333B}.Release|Win32.Build.0 = Release|Win32 {4B47A364-37C4-96A7-6041-97BB4C1D333B}.Release|x64.ActiveCfg = Release|x64 {4B47A364-37C4-96A7-6041-97BB4C1D333B}.Release|x64.Build.0 = Release|x64 + {37BED5B5-23FA-D81F-8C0C-F1167867813A}.Debug|Win32.ActiveCfg = Debug|Win32 + {37BED5B5-23FA-D81F-8C0C-F1167867813A}.Debug|Win32.Build.0 = Debug|Win32 + {37BED5B5-23FA-D81F-8C0C-F1167867813A}.Debug|x64.ActiveCfg = Debug|x64 + {37BED5B5-23FA-D81F-8C0C-F1167867813A}.Debug|x64.Build.0 = Debug|x64 + {37BED5B5-23FA-D81F-8C0C-F1167867813A}.Release|Win32.ActiveCfg = Release|Win32 + {37BED5B5-23FA-D81F-8C0C-F1167867813A}.Release|Win32.Build.0 = Release|Win32 + {37BED5B5-23FA-D81F-8C0C-F1167867813A}.Release|x64.ActiveCfg = Release|x64 + {37BED5B5-23FA-D81F-8C0C-F1167867813A}.Release|x64.Build.0 = Release|x64 {57C81DD3-4304-213D-AC16-39349871C957}.Debug|Win32.ActiveCfg = Debug|Win32 {57C81DD3-4304-213D-AC16-39349871C957}.Debug|Win32.Build.0 = Debug|Win32 {57C81DD3-4304-213D-AC16-39349871C957}.Debug|x64.ActiveCfg = Debug|x64 @@ -231,6 +241,7 @@ Global EndGlobalSection GlobalSection(NestedProjects) = preSolution {4B47A364-37C4-96A7-6041-97BB4C1D333B} = {EB5FC2C6-D72D-B6CC-C0C1-26F3AC2E9231} + {37BED5B5-23FA-D81F-8C0C-F1167867813A} = {EB5FC2C6-D72D-B6CC-C0C1-26F3AC2E9231} {57C81DD3-4304-213D-AC16-39349871C957} = {EB5FC2C6-D72D-B6CC-C0C1-26F3AC2E9231} {010BE414-ED5B-CF56-16C0-BD18027062C0} = {EB5FC2C6-D72D-B6CC-C0C1-26F3AC2E9231} {25512BFB-1138-EDF2-BA88-5310A64E6659} = {EB5FC2C6-D72D-B6CC-C0C1-26F3AC2E9231} diff --git a/tools/gfx/cpu/render-cpu.cpp b/tools/gfx/cpu/render-cpu.cpp index fa31f7ee1..f0b612e2f 100644 --- a/tools/gfx/cpu/render-cpu.cpp +++ b/tools/gfx/cpu/render-cpu.cpp @@ -9,6 +9,7 @@ #include "../command-writer.h" #include "../renderer-shared.h" +#include "../simple-transient-resource-heap.h" #include "../slang-context.h" #define SLANG_PRELUDE_NAMESPACE slang_prelude @@ -1119,6 +1120,10 @@ private: return nullptr; } public: + void init(CPUDevice* device) + { + SLANG_UNUSED(device); + } virtual SLANG_NO_THROW void SLANG_MCALL encodeRenderCommands( IRenderPassLayout* renderPass, IFramebuffer* framebuffer, @@ -1273,13 +1278,6 @@ private: { return m_desc; } - virtual SLANG_NO_THROW Result SLANG_MCALL - createCommandBuffer(ICommandBuffer** outCommandBuffer) override - { - RefPtr<CommandBufferImpl> result = new CommandBufferImpl(); - *outCommandBuffer = result.detach(); - return SLANG_OK; - } virtual SLANG_NO_THROW void SLANG_MCALL executeCommandBuffers(uint32_t count, ICommandBuffer* const* commandBuffers) override @@ -1400,6 +1398,8 @@ private: } }; + using TransientResourceHeapImpl = SimpleTransientResourceHeap<CPUDevice, CommandBufferImpl>; + public: ~CPUDevice() { @@ -1559,6 +1559,15 @@ public: *outQueue = queue.detach(); return SLANG_OK; } + virtual SLANG_NO_THROW Result SLANG_MCALL createTransientResourceHeap( + const ITransientResourceHeap::Desc& desc, + ITransientResourceHeap** outHeap) override + { + RefPtr<TransientResourceHeapImpl> result = new TransientResourceHeapImpl(); + SLANG_RETURN_ON_FAIL(result->init(this, desc)); + *outHeap = result.detach(); + return SLANG_OK; + } virtual SLANG_NO_THROW Result SLANG_MCALL createSwapchain( const ISwapchain::Desc& desc, WindowHandle window, ISwapchain** outSwapchain) override { diff --git a/tools/gfx/cuda/render-cuda.cpp b/tools/gfx/cuda/render-cuda.cpp index 383ccc924..b29f7f7e4 100644 --- a/tools/gfx/cuda/render-cuda.cpp +++ b/tools/gfx/cuda/render-cuda.cpp @@ -12,6 +12,7 @@ #include "slang-com-helper.h" #include "../command-writer.h" #include "../renderer-shared.h" +#include "../simple-transient-resource-heap.h" #include "../slang-context.h" # ifdef RENDER_TEST_OPTIX @@ -989,6 +990,7 @@ public: return nullptr; } public: + void init(CUDADevice* device) { SLANG_UNUSED(device); } virtual SLANG_NO_THROW void SLANG_MCALL encodeRenderCommands( IRenderPassLayout* renderPass, IFramebuffer* framebuffer, @@ -1147,13 +1149,6 @@ public: { return m_desc; } - virtual SLANG_NO_THROW Result SLANG_MCALL - createCommandBuffer(ICommandBuffer** outCommandBuffer) override - { - RefPtr<CommandBufferImpl> result = new CommandBufferImpl(); - *outCommandBuffer = result.detach(); - return SLANG_OK; - } virtual SLANG_NO_THROW void SLANG_MCALL executeCommandBuffers(uint32_t count, ICommandBuffer* const* commandBuffers) override @@ -1320,6 +1315,8 @@ public: } }; + using TransientResourceHeapImpl = SimpleTransientResourceHeap<CUDADevice, CommandBufferImpl>; + public: ~CUDADevice() { @@ -1921,6 +1918,16 @@ public: } public: + virtual SLANG_NO_THROW Result SLANG_MCALL createTransientResourceHeap( + const ITransientResourceHeap::Desc& desc, + ITransientResourceHeap** outHeap) override + { + RefPtr<TransientResourceHeapImpl> result = new TransientResourceHeapImpl(); + SLANG_RETURN_ON_FAIL(result->init(this, desc)); + *outHeap = result.detach(); + return SLANG_OK; + } + virtual SLANG_NO_THROW Result SLANG_MCALL createCommandQueue(const ICommandQueue::Desc& desc, ICommandQueue** outQueue) override { diff --git a/tools/gfx/d3d12/circular-resource-heap-d3d12.cpp b/tools/gfx/d3d12/circular-resource-heap-d3d12.cpp deleted file mode 100644 index 685dd364f..000000000 --- a/tools/gfx/d3d12/circular-resource-heap-d3d12.cpp +++ /dev/null @@ -1,222 +0,0 @@ -#include "circular-resource-heap-d3d12.h" - -namespace gfx { -using namespace Slang; - -D3D12CircularResourceHeap::D3D12CircularResourceHeap(): - m_fence(nullptr), - m_device(nullptr), - m_blockFreeList(sizeof(Block), SLANG_ALIGN_OF(Block), 16), - m_blocks(nullptr) -{ - m_back.m_block = nullptr; - m_back.m_position = nullptr; - m_front.m_block = nullptr; - m_front.m_position = nullptr; -} - -D3D12CircularResourceHeap::~D3D12CircularResourceHeap() -{ - _freeBlockListResources(m_blocks); -} - -void D3D12CircularResourceHeap::_freeBlockListResources(const Block* start) -{ - if (start) - { - const Block* block = start; - do - { - ID3D12Resource* resource = block->m_resource; - - resource->Unmap(0, nullptr); - resource->Release(); - - // Next in list - block = block->m_next; - - } while (block != start); - } -} - -Result D3D12CircularResourceHeap::init(ID3D12Device* device, const Desc& desc, D3D12CounterFence* fence) -{ - assert(m_blocks == nullptr); - assert(desc.m_blockSize > 0); - - m_fence = fence; - m_desc = desc; - m_device = device; - - return SLANG_OK; -} - -void D3D12CircularResourceHeap::addSync(uint64_t signalValue) -{ - assert(signalValue == m_fence->getCurrentValue()); - PendingEntry entry; - entry.m_completedValue = signalValue; - entry.m_cursor = m_front; - m_pendingQueue.add(entry); -} - -void D3D12CircularResourceHeap::updateCompleted() -{ - const uint64_t completedValue = m_fence->getCompletedValue(); - -#if 0 - while (m_pendingQueue.getCount() != 0) - { - const PendingEntry& entry = m_pendingQueue[0]; - if (entry.m_completedValue <= completedValue) - { - m_back = entry.m_cursor; - m_pendingQueue.removeAt(0); - } - else - { - break; - } - } -#else - // A more efficient implementation is m_pendingQueue is implemented as a vector like type - const Index size = m_pendingQueue.getCount(); - Index end = 0; - while (end < size && m_pendingQueue[end].m_completedValue <= completedValue) - { - end++; - } - - if (end > 0) - { - // Set the back position - m_back = m_pendingQueue[end - 1].m_cursor; - if (end == size) - { - m_pendingQueue.clear(); - } - else - { - m_pendingQueue.removeRange(0, size); - } - } -#endif -} - -D3D12CircularResourceHeap::Block* D3D12CircularResourceHeap::_newBlock() -{ - D3D12_RESOURCE_DESC desc; - - desc.Dimension = D3D12_RESOURCE_DIMENSION_BUFFER; - desc.Alignment = 0; - desc.Width = m_desc.m_blockSize; - desc.Height = 1; - desc.DepthOrArraySize = 1; - desc.MipLevels = 1; - desc.Format = DXGI_FORMAT_UNKNOWN; - desc.SampleDesc.Count = 1; - desc.SampleDesc.Quality = 0; - desc.Layout = D3D12_TEXTURE_LAYOUT_ROW_MAJOR; - desc.Flags = D3D12_RESOURCE_FLAG_NONE; - - ComPtr<ID3D12Resource> resource; - Result res = m_device->CreateCommittedResource(&m_desc.m_heapProperties, m_desc.m_heapFlags, &desc, m_desc.m_initialState, nullptr, IID_PPV_ARGS(resource.writeRef())); - if (SLANG_FAILED(res)) - { - assert(!"Resource allocation failed"); - return nullptr; - } - - uint8_t* data = nullptr; - if (m_desc.m_heapProperties.Type == D3D12_HEAP_TYPE_READBACK) - { - } - else - { - // Map it, and keep it mapped - resource->Map(0, nullptr, (void**)&data); - } - - // We have no blocks -> so lets allocate the first - Block* block = (Block*)m_blockFreeList.allocate(); - block->m_next = nullptr; - - block->m_resource = resource.detach(); - block->m_start = data; - return block; -} - -D3D12CircularResourceHeap::Cursor D3D12CircularResourceHeap::allocate(size_t size, size_t alignment) -{ - const size_t blockSize = getBlockSize(); - - assert(size <= blockSize); - - // If nothing is allocated add the first block - if (m_blocks == nullptr) - { - Block* block = _newBlock(); - if (!block) - { - Cursor cursor = {}; - return cursor; - } - m_blocks = block; - // Make circular - block->m_next = block; - - // Point front and back to same position, as currently it is all free - m_back = { block, block->m_start }; - m_front = m_back; - } - - // If front and back are in the same block then front MUST be ahead of back (as that defined as - // an invariant and is required for block insertion to be possible - Block* block = m_front.m_block; - - // Check the invariant - assert(block != m_back.m_block || m_front.m_position >= m_back.m_position); - - { - uint8_t* cur = (uint8_t*)((size_t(m_front.m_position) + alignment - 1) & ~(alignment - 1)); - // Does the the allocation fit? - if (cur + size <= block->m_start + blockSize) - { - // It fits - // Move the front forward - m_front.m_position = cur + size; - Cursor cursor = { block, cur }; - return cursor; - } - } - - // Okay I can't fit into current block... - - // If the next block contains front, we need to add a block, else we can use that block - if (block->m_next == m_back.m_block) - { - Block* newBlock = _newBlock(); - // Insert into the list - newBlock->m_next = block->m_next; - block->m_next = newBlock; - } - - // Use the block we are going to add to - block = block->m_next; - uint8_t* cur = (uint8_t*)((size_t(block->m_start) + alignment - 1) & ~(alignment - 1)); - // Does the the allocation fit? - if (cur + size > block->m_start + blockSize) - { - assert(!"Couldn't fit into a free block(!) Alignment breaks it?"); - Cursor cursor = {}; - return cursor; - } - // It fits - // Move the front forward - m_front.m_block = block; - m_front.m_position = cur + size; - Cursor cursor = { block, cur }; - return cursor; -} - -} // namespace gfx diff --git a/tools/gfx/d3d12/circular-resource-heap-d3d12.h b/tools/gfx/d3d12/circular-resource-heap-d3d12.h deleted file mode 100644 index 7eacf9572..000000000 --- a/tools/gfx/d3d12/circular-resource-heap-d3d12.h +++ /dev/null @@ -1,206 +0,0 @@ -#pragma once - -#include "slang-com-ptr.h" -#include "core/slang-list.h" -#include "core/slang-free-list.h" - -#include "resource-d3d12.h" - -namespace gfx { - -/*! \brief The D3D12CircularResourceHeap is a heap that is suited for size constrained real-time resources allocation that -is transitory in nature. It is designed to allocate resources which are used and discarded, often used where in -previous versions of DirectX the 'DISCARD' flag was used. - -The idea is to have a heap which chunks of resource can be allocated, and used for GPU execution, -and that the heap is able through the addSync/updateCompleted idiom is able to track when the usage of the resources is -completed allowing them to be reused. The heap is arranged as circularly, with new allocations made from the front, and the back -being updated as the GPU updating the back when it is informed anything using prior parts of the heap have completed. In this -arrangement all the heap between the back and the front can be thought of as in use or potentially in use by the GPU. All the heap -from the front back around to the back, is free and can be allocated from. It is the responsibility of the user of the Heap to make -sure the invariant holds, but in most normal usage it does so simply. - -Another feature of the heap is that it does not require upfront knowledge of how big a heap is needed. The backing resources will be expanded -dynamically with requests as needed. The only requirement is that know single request can be larger than m_blockSize specified in the Desc -used to initialize the heap. This is because all the backing resources are allocated to a single size. This limitation means the D3D12CircularResourceHeap -may not be the best use for example for uploading a texture - because it's design is really around transitory uploads or write backs, and so more suited -to constant buffers, vertex buffer, index buffers and the like. - -To upload a texture at program startup it is most likely better to use a D3D12ResourceScopeManager. - -\code{.cpp} - -typedef D3D12CircularResourceHeap Heap; - -Heap::Cursor cursor = heap.allocateVertexBuffer(sizeof(Vertex) * numVerts); -Memory:copy(cursor.m_position, verts, sizeof(Vertex) * numVerts); - -// Do a command using the GPU handle -m_commandList->... -// Do another command using the GPU handle - -m_commandList->... - -// Execute the command list on the command queue -{ - ID3D12CommandList* lists[] = { m_commandList }; - m_commandQueue->ExecuteCommandLists(SLANG_COUNT_OF(lists), lists); -} - -// Add a sync point -const uint64_t signalValue = m_fence.nextSignal(m_commandQueue); -heap.addSync(signalValue) - -// The cursors cannot be used anymore - -// At some later point call updateCompleted. This will see where the GPU is at, and make resources available that the GPU no longer accesses. -heap.updateCompleted(); - -\endcode - -### Implementation - -Front and back can be in the same block, but ONLY if back is behind front, because we have to always be able to insert -new blocks in front of front. So it must be possible to do an block insertion between the two of them. - -|--B---F-----| |----------| - -When B and F are on top of one another it means there is nothing in the list. NOTE this also means that a move of front can never place it -top of the back. - -https://msdn.microsoft.com/en-us/library/windows/desktop/dn899125%28v=vs.85%29.aspx -https://msdn.microsoft.com/en-us/library/windows/desktop/mt426646%28v=vs.85%29.aspx -*/ - -class D3D12CircularResourceHeap -{ - protected: - struct Block; - public: - typedef D3D12CircularResourceHeap ThisType; - - /// The alignment used for VERTEX_BUFFER allocations - /// Strictly speaking it seems the hardware can handle 4 byte alignment, but since often in use - /// data will be copied from CPU memory to the allocation, using 16 byte alignment is superior as allows - /// significantly faster memcpy. - /// The sample that shows sizeof(float) - 4 bytes is appropriate is at the link below. - /// https://msdn.microsoft.com/en-us/library/windows/desktop/mt426646%28v=vs.85%29.aspx - enum - { - VERTEX_BUFFER_ALIGNMENT = 16, - }; - - struct Desc - { - void init() - { - { - D3D12_HEAP_PROPERTIES& props = m_heapProperties; - - props.Type = D3D12_HEAP_TYPE_UPLOAD; - props.CPUPageProperty = D3D12_CPU_PAGE_PROPERTY_UNKNOWN; - props.MemoryPoolPreference = D3D12_MEMORY_POOL_UNKNOWN; - props.CreationNodeMask = 1; - props.VisibleNodeMask = 1; - } - m_heapFlags = D3D12_HEAP_FLAG_NONE; - m_initialState = D3D12_RESOURCE_STATE_GENERIC_READ; - m_blockSize = 0; - } - - D3D12_HEAP_PROPERTIES m_heapProperties; - D3D12_HEAP_FLAGS m_heapFlags; - D3D12_RESOURCE_STATES m_initialState; - size_t m_blockSize; - }; - - /// Cursor position - struct Cursor - { - /// Get GpuHandle - SLANG_FORCE_INLINE D3D12_GPU_VIRTUAL_ADDRESS getGpuHandle() const { return m_block->m_resource->GetGPUVirtualAddress() + size_t(m_position - m_block->m_start); } - /// Must have a block and position - SLANG_FORCE_INLINE bool isValid() const { return m_block != nullptr; } - /// Calculate the offset into the underlying resource - SLANG_FORCE_INLINE size_t getOffset() const { return size_t(m_position - m_block->m_start); } - /// Get the underlying resource - SLANG_FORCE_INLINE ID3D12Resource* getResource() const { return m_block->m_resource; } - - Block* m_block; ///< The block index - uint8_t* m_position; ///< The current position - }; - - /// Get the desc used to initialize the heap - SLANG_FORCE_INLINE const Desc& getDesc() const { return m_desc; } - - /// Must be called before used - /// Block size must be at least as large as the _largest_ thing allocated - /// Also note depending on alignment of a resource allocation, the block size might also need to take into account the - /// maximum alignment use. It is a REQUIREMENT that a newly allocated resource block is large enough to hold any - /// allocation taking into account the alignment used. - Slang::Result init(ID3D12Device* device, const Desc& desc, D3D12CounterFence* fence); - - /// Get the block size - SLANG_FORCE_INLINE size_t getBlockSize() const { return m_desc.m_blockSize; } - - /// Allocate constant buffer of specified size - Cursor allocate(size_t size, size_t alignment); - - /// Allocate a constant buffer - SLANG_FORCE_INLINE Cursor allocateConstantBuffer(size_t size) { return allocate(size, D3D12_CONSTANT_BUFFER_DATA_PLACEMENT_ALIGNMENT); } - /// Allocate a vertex buffer - SLANG_FORCE_INLINE Cursor allocateVertexBuffer(size_t size) { return allocate(size, VERTEX_BUFFER_ALIGNMENT); } - - /// Create filled in constant buffer - SLANG_FORCE_INLINE Cursor newConstantBuffer(const void* data, size_t size) { Cursor cursor = allocateConstantBuffer(size); ::memcpy(cursor.m_position, data, size); return cursor; } - /// Create in filled in constant buffer - template <typename T> - SLANG_FORCE_INLINE Cursor newConstantBuffer(const T& in) { return newConstantBuffer(&in, sizeof(T)); } - - /// Look where the GPU has got to and release anything not currently used - void updateCompleted(); - /// Add a sync point - meaning that when this point is hit in the queue - /// all of the resources up to this point will no longer be used. - void addSync(uint64_t signalValue); - - /// Get the gpu address of this cursor - D3D12_GPU_VIRTUAL_ADDRESS getGpuHandle(const Cursor& cursor) const { return cursor.m_block->m_resource->GetGPUVirtualAddress() + size_t(cursor.m_position - cursor.m_block->m_start); } - - /// Ctor - D3D12CircularResourceHeap(); - /// Dtor - ~D3D12CircularResourceHeap(); - - protected: - - struct Block - { - ID3D12Resource* m_resource; ///< The mapped resource - uint8_t* m_start; ///< Once created the resource is mapped to here - Block* m_next; ///< Points to next block in the list - }; - struct PendingEntry - { - uint64_t m_completedValue; ///< The value when this is completed - Cursor m_cursor; ///< the cursor at that point - }; - void _freeBlockListResources(const Block* block); - /// Create a new block (with associated resource), do not add the block list - Block* _newBlock(); - - Block* m_blocks; ///< Circular singly linked list of block. nullptr initially - Slang::FreeList m_blockFreeList; ///< Free list of actual allocations of blocks - Slang::List<PendingEntry> m_pendingQueue; ///< Holds the list of pending positions. When the fence value is greater than the value on the queue entry, the entry is done. - - // Allocation is made from the front, and freed from the back. - Cursor m_back; ///< Current back position. - Cursor m_front; ///< Current front position. - - Desc m_desc; ///< Describes the heap - - D3D12CounterFence* m_fence; ///< The fence to use - ID3D12Device* m_device; ///< The device that resources will be constructed on -}; - -} // namespace gfx - diff --git a/tools/gfx/d3d12/render-d3d12.cpp b/tools/gfx/d3d12/render-d3d12.cpp index e2629dc53..6b818f100 100644 --- a/tools/gfx/d3d12/render-d3d12.cpp +++ b/tools/gfx/d3d12/render-d3d12.cpp @@ -41,7 +41,6 @@ struct ID3D12GraphicsCommandList1 {}; #include "resource-d3d12.h" #include "descriptor-heap-d3d12.h" -#include "circular-resource-heap-d3d12.h" #include "../d3d/d3d-util.h" @@ -72,6 +71,9 @@ public: virtual SLANG_NO_THROW SlangResult SLANG_MCALL initialize(const Desc& desc) override; virtual SLANG_NO_THROW Result SLANG_MCALL createCommandQueue(const ICommandQueue::Desc& desc, ICommandQueue** outQueue) override; + virtual SLANG_NO_THROW Result SLANG_MCALL createTransientResourceHeap( + const ITransientResourceHeap::Desc& desc, + ITransientResourceHeap** outHeap) override; virtual SLANG_NO_THROW Result SLANG_MCALL createSwapchain( const ISwapchain::Desc& desc, WindowHandle window, @@ -211,12 +213,6 @@ public: public: typedef BufferResource Parent; - void bindConstantBufferView(D3D12CircularResourceHeap& circularHeap, int index, Submitter* submitter) const - { - // Set the constant buffer - submitter->setRootConstantBufferView(index, m_resource.getResource()->GetGPUVirtualAddress()); - } - BufferResourceImpl(IResource::Usage initialUsage, const Desc& desc): Parent(desc), m_initialUsage(initialUsage) , m_defaultState(_calcResourceState(initialUsage)) @@ -444,6 +440,23 @@ public: ID3D12GraphicsCommandList* m_commandList; }; + static void _initBufferResourceDesc(size_t bufferSize, D3D12_RESOURCE_DESC& out) + { + out = {}; + + out.Dimension = D3D12_RESOURCE_DIMENSION_BUFFER; + out.Alignment = 0; + out.Width = bufferSize; + out.Height = 1; + out.DepthOrArraySize = 1; + out.MipLevels = 1; + out.Format = DXGI_FORMAT_UNKNOWN; + out.SampleDesc.Count = 1; + out.SampleDesc.Quality = 0; + out.Layout = D3D12_TEXTURE_LAYOUT_ROW_MAJOR; + out.Flags = D3D12_RESOURCE_FLAG_NONE; + } + static Result _uploadBufferData( ID3D12GraphicsCommandList* cmdList, BufferResourceImpl* buffer, @@ -479,78 +492,111 @@ public: return SLANG_OK; } - // Use a circular buffer of execution frames to manage in-flight GPU command buffers. - // Each call to `executeCommandLists` advances the frame by 1. - // If we run out of avaialble frames, wait for the earliest submitted frame to finish. - struct ExecutionFrameResources + class TransientResourceHeapImpl + : public ITransientResourceHeap + , public RefObject { + public: + SLANG_REF_OBJECT_IUNKNOWN_ALL + ITransientResourceHeap* getInterface(const Guid& guid) + { + if (guid == GfxGUID::IID_ISlangUnknown || guid == GfxGUID::IID_ITransientResourceHeap) + return static_cast<ITransientResourceHeap*>(this); + return nullptr; + } + + public: + D3D12Resource m_constantBuffer; + D3D12Resource m_constantUploadBuffer; + + D3D12Device* m_device; ComPtr<ID3D12CommandAllocator> m_commandAllocator; - List<ComPtr<ID3D12GraphicsCommandList>> m_commandListPool; + List<ComPtr<ID3D12GraphicsCommandList>> m_d3dCommandListPool; + List<ComPtr<ICommandBuffer>> m_commandBufferPool; uint32_t m_commandListAllocId = 0; - HANDLE fenceEvent; + // Wait values for each command queue. + struct QueueWaitInfo + { + uint64_t waitValue; + HANDLE fenceEvent; + }; + ShortList<QueueWaitInfo, 4> m_waitInfos; + QueueWaitInfo& getQueueWaitInfo(uint32_t queueIndex) + { + if (queueIndex < (uint32_t)m_waitInfos.getCount()) + { + return m_waitInfos[queueIndex]; + } + auto oldCount = m_waitInfos.getCount(); + m_waitInfos.setCount(queueIndex + 1); + for (auto i = oldCount; i < m_waitInfos.getCount(); i++) + { + m_waitInfos[i].waitValue = 0; + m_waitInfos[i].fenceEvent = CreateEventEx( + nullptr, + false, + CREATE_EVENT_INITIAL_SET | CREATE_EVENT_MANUAL_RESET, + EVENT_ALL_ACCESS); + } + return m_waitInfos[queueIndex]; + } // During command submission, we need all the descriptor tables that get // used to come from a single heap (for each descriptor heap type). // // We will thus keep a single heap of each type that we hope will hold // all the descriptors that actually get needed in a frame. - // - // TODO: we need an allocation policy to reallocate and resize these - // if/when we run out of space during a frame. D3D12DescriptorHeap m_viewHeap; // Cbv, Srv, Uav D3D12DescriptorHeap m_samplerHeap; // Heap for samplers - ~ExecutionFrameResources() { CloseHandle(fenceEvent); } - Result init(ID3D12Device* device, uint32_t viewHeapSize, uint32_t samplerHeapSize) + ~TransientResourceHeapImpl() { - SLANG_RETURN_ON_FAIL(device->CreateCommandAllocator( + synchronizeAndReset(); + for (auto& waitInfo : m_waitInfos) + CloseHandle(waitInfo.fenceEvent); + } + + Result init( + const ITransientResourceHeap::Desc& desc, + D3D12Device* device, + uint32_t viewHeapSize, + uint32_t samplerHeapSize) + { + m_device = device; + auto d3dDevice = device->m_device; + SLANG_RETURN_ON_FAIL(d3dDevice->CreateCommandAllocator( D3D12_COMMAND_LIST_TYPE_DIRECT, IID_PPV_ARGS(m_commandAllocator.writeRef()))); - fenceEvent = CreateEventEx( - nullptr, - false, - CREATE_EVENT_INITIAL_SET | CREATE_EVENT_MANUAL_RESET, - EVENT_ALL_ACCESS); + SLANG_RETURN_ON_FAIL(m_viewHeap.init( - device, + d3dDevice, viewHeapSize, D3D12_DESCRIPTOR_HEAP_TYPE_CBV_SRV_UAV, D3D12_DESCRIPTOR_HEAP_FLAG_SHADER_VISIBLE)); SLANG_RETURN_ON_FAIL(m_samplerHeap.init( - device, + d3dDevice, samplerHeapSize, D3D12_DESCRIPTOR_HEAP_TYPE_SAMPLER, D3D12_DESCRIPTOR_HEAP_FLAG_SHADER_VISIBLE)); - return SLANG_OK; - } - void reset() - { - WaitForSingleObject(fenceEvent, INFINITE); - m_viewHeap.deallocateAll(); - m_samplerHeap.deallocateAll(); - m_commandListAllocId = 0; - m_commandAllocator->Reset(); - for (auto cmdBuffer : m_commandListPool) - cmdBuffer->Reset(m_commandAllocator, nullptr); - } - ComPtr<ID3D12GraphicsCommandList> createCommandList(ID3D12Device* device) - { - if (m_commandListAllocId == m_commandListPool.getCount()) + + if (desc.constantBufferSize != 0) { - ComPtr<ID3D12GraphicsCommandList> cmdList; - device->CreateCommandList( - 0, - D3D12_COMMAND_LIST_TYPE_DIRECT, - m_commandAllocator, + D3D12_RESOURCE_DESC resourceDesc; + _initBufferResourceDesc(desc.constantBufferSize, resourceDesc); + device->createBuffer( + resourceDesc, nullptr, - IID_PPV_ARGS(cmdList.writeRef())); - - m_commandListPool.add(cmdList); + 0, + m_constantUploadBuffer, + D3D12_RESOURCE_STATE_VERTEX_AND_CONSTANT_BUFFER, + m_constantBuffer); } - assert((Index)m_commandListAllocId < m_commandListPool.getCount()); - auto& result = m_commandListPool[m_commandListAllocId]; - ++m_commandListAllocId; - return result; + return SLANG_OK; } + + virtual SLANG_NO_THROW Result SLANG_MCALL + createCommandBuffer(ICommandBuffer** outCommandBuffer) override; + + virtual SLANG_NO_THROW Result SLANG_MCALL synchronizeAndReset() override; }; class CommandBufferImpl; @@ -561,7 +607,7 @@ public: bool m_isOpen = false; bool m_bindingDirty = true; CommandBufferImpl* m_commandBuffer; - ExecutionFrameResources* m_frame; + TransientResourceHeapImpl* m_transientHeap; D3D12Device* m_renderer; ID3D12Device* m_device; ID3D12GraphicsCommandList* m_d3dCmdList; @@ -591,7 +637,7 @@ public: m_commandBuffer = commandBuffer; m_d3dCmdList = m_commandBuffer->m_cmdList; m_renderer = commandBuffer->m_renderer; - m_frame = commandBuffer->m_frame; + m_transientHeap = commandBuffer->m_transientHeap; } void endEncodingImpl() { m_isOpen = false; } @@ -659,7 +705,7 @@ public: struct RootBindingState { - ExecutionFrameResources* frame; + TransientResourceHeapImpl* transientHeap; D3D12Device* device; ArrayView<DescriptorTable> descriptorTables; BindingOffset offset; @@ -2339,17 +2385,20 @@ public: } public: ComPtr<ID3D12GraphicsCommandList> m_cmdList; - ExecutionFrameResources* m_frame; + TransientResourceHeapImpl* m_transientHeap; D3D12Device* m_renderer; - void init(D3D12Device* renderer, ExecutionFrameResources* frame) + void init( + D3D12Device* renderer, + ID3D12GraphicsCommandList* d3dCommandList, + TransientResourceHeapImpl* transientHeap) { - m_frame = frame; + m_transientHeap = transientHeap; m_renderer = renderer; - m_cmdList = m_frame->createCommandList(renderer->m_device); + m_cmdList = d3dCommandList; ID3D12DescriptorHeap* heaps[] = { - m_frame->m_viewHeap.getHeap(), - m_frame->m_samplerHeap.getHeap(), + m_transientHeap->m_viewHeap.getHeap(), + m_transientHeap->m_samplerHeap.getHeap(), }; m_cmdList->SetDescriptorHeaps(SLANG_COUNT_OF(heaps), heaps); } @@ -2392,7 +2441,7 @@ public: void init( D3D12Device* renderer, - ExecutionFrameResources* frame, + TransientResourceHeapImpl* transientHeap, CommandBufferImpl* cmdBuffer, RenderPassLayoutImpl* renderPass, FramebufferImpl* framebuffer) @@ -2402,7 +2451,7 @@ public: m_device = renderer->m_device; m_renderPass = renderPass; m_framebuffer = framebuffer; - m_frame = frame; + m_transientHeap = transientHeap; m_boundVertexBuffers.clear(); m_boundIndexBuffer = nullptr; m_primitiveTopologyType = D3D12_PRIMITIVE_TOPOLOGY_TYPE_TRIANGLE; @@ -2735,7 +2784,7 @@ public: { m_renderCommandEncoder.init( m_renderer, - m_frame, + m_transientHeap, this, static_cast<RenderPassLayoutImpl*>(renderPass), static_cast<FramebufferImpl*>(framebuffer)); @@ -2769,13 +2818,13 @@ public: } void init( D3D12Device* renderer, - ExecutionFrameResources* frame, + TransientResourceHeapImpl* transientHeap, CommandBufferImpl* cmdBuffer) { PipelineCommandEncoder::init(cmdBuffer); m_preCmdList = nullptr; m_device = renderer->m_device; - m_frame = frame; + m_transientHeap = transientHeap; m_currentPipeline = nullptr; } @@ -2805,7 +2854,7 @@ public: virtual SLANG_NO_THROW void SLANG_MCALL encodeComputeCommands(IComputeCommandEncoder** outEncoder) override { - m_computeCommandEncoder.init(m_renderer, m_frame, this); + m_computeCommandEncoder.init(m_renderer, m_transientHeap, this); *outEncoder = &m_computeCommandEncoder; } @@ -2892,32 +2941,6 @@ public: } public: - struct CommandBufferPool - { - List<RefPtr<CommandBufferImpl>> pool; - uint32_t allocIndex = 0; - RefPtr<CommandBufferImpl> allocCommandBuffer(D3D12Device* renderer, ExecutionFrameResources* frame) - { - if ((Index)allocIndex < pool.getCount()) - { - RefPtr<CommandBufferImpl> result = pool[allocIndex]; - result->init(renderer, frame); - allocIndex++; - return result; - } - RefPtr<CommandBufferImpl> cmdBuffer = new CommandBufferImpl(); - cmdBuffer->init(renderer, frame); - pool.add(cmdBuffer); - return cmdBuffer; - } - void reset() - { - allocIndex = 0; - } - }; - List<CommandBufferPool> m_commandBufferPools; - List<ExecutionFrameResources> m_frames; - uint32_t m_frameIndex = 0; D3D12Device* m_renderer; ComPtr<ID3D12Device> m_device; ComPtr<ID3D12CommandQueue> m_d3dQueue; @@ -2925,20 +2948,13 @@ public: uint64_t m_fenceValue = 0; HANDLE globalWaitHandle; Desc m_desc; - Result init( - D3D12Device* renderer, - uint32_t frameCount, - uint32_t viewHeapSize, - uint32_t samplerHeapSize) + uint32_t m_queueIndex = 0; + + Result init(D3D12Device* device, uint32_t queueIndex) { - m_renderer = renderer; - m_device = renderer->m_device; - m_frames.setCount(frameCount); - m_commandBufferPools.setCount(frameCount); - for (uint32_t i = 0; i < frameCount; i++) - { - SLANG_RETURN_ON_FAIL(m_frames[i].init(m_device, viewHeapSize, samplerHeapSize)); - } + m_queueIndex = queueIndex; + m_renderer = device; + m_device = device->m_device; D3D12_COMMAND_QUEUE_DESC queueDesc = {}; queueDesc.Type = D3D12_COMMAND_LIST_TYPE_DIRECT; SLANG_RETURN_ON_FAIL(m_device->CreateCommandQueue(&queueDesc, IID_PPV_ARGS(m_d3dQueue.writeRef()))); @@ -2955,20 +2971,12 @@ public: { wait(); CloseHandle(globalWaitHandle); + m_renderer->m_queueIndexAllocator.free((int)m_queueIndex, 1); } virtual SLANG_NO_THROW const Desc& SLANG_MCALL getDesc() override { return m_desc; } - virtual SLANG_NO_THROW Result SLANG_MCALL - createCommandBuffer(ICommandBuffer** outCommandBuffer) override - { - RefPtr<CommandBufferImpl> result = - m_commandBufferPools[m_frameIndex].allocCommandBuffer( - m_renderer, &m_frames[m_frameIndex]); - *outCommandBuffer = result.detach(); - return SLANG_OK; - } virtual SLANG_NO_THROW void SLANG_MCALL executeCommandBuffers(uint32_t count, ICommandBuffer* const* commandBuffers) override @@ -2981,21 +2989,21 @@ public: } m_d3dQueue->ExecuteCommandLists((UINT)count, commandLists.getArrayView().getBuffer()); - auto& frame = m_frames[m_frameIndex]; m_fenceValue++; + + for (uint32_t i = 0; i < count; i++) + { + if (i > 0 && commandBuffers[i] == commandBuffers[i - 1]) + continue; + auto cmdImpl = static_cast<CommandBufferImpl*>(commandBuffers[i]); + auto transientHeap = cmdImpl->m_transientHeap; + auto& waitInfo = transientHeap->getQueueWaitInfo(m_queueIndex); + waitInfo.waitValue = m_fenceValue; + ResetEvent(waitInfo.fenceEvent); + m_fence->SetEventOnCompletion(m_fenceValue, waitInfo.fenceEvent); + } m_d3dQueue->Signal(m_fence, m_fenceValue); - ResetEvent(frame.fenceEvent); ResetEvent(globalWaitHandle); - m_fence->SetEventOnCompletion(m_fenceValue, frame.fenceEvent); - swapExecutionFrame(); - } - - void swapExecutionFrame() - { - m_frameIndex = (m_frameIndex + 1) % m_frames.getCount(); - auto& frame = m_frames[m_frameIndex]; - frame.reset(); - m_commandBufferPools[m_frameIndex].reset(); } virtual SLANG_NO_THROW void SLANG_MCALL wait() override @@ -3083,11 +3091,13 @@ public: static PROC loadProc(HMODULE module, char const* name); - Result createCommandQueueImpl( - uint32_t frameCount, - uint32_t viewHeapSize, - uint32_t samplerHeapSize, - CommandQueueImpl** outQueue); + Result createCommandQueueImpl(CommandQueueImpl** outQueue); + + Result createTransientResourceHeapImpl( + size_t constantBufferSize, + uint32_t viewDescriptors, + uint32_t samplerDescriptors, + TransientResourceHeapImpl** outHeap); Result createBuffer( const D3D12_RESOURCE_DESC& resourceDesc, @@ -3118,7 +3128,7 @@ public: ResourceCommandRecordInfo encodeResourceCommands() { ResourceCommandRecordInfo info; - m_resourceCommandQueue->createCommandBuffer(info.commandBuffer.writeRef()); + m_resourceCommandTransientHeap->createCommandBuffer(info.commandBuffer.writeRef()); info.d3dCommandList = static_cast<CommandBufferImpl*>(info.commandBuffer.get())->m_cmdList; return info; } @@ -3126,7 +3136,7 @@ public: { info.commandBuffer->close(); m_resourceCommandQueue->executeCommandBuffer(info.commandBuffer); - m_resourceCommandQueue->wait(); + m_resourceCommandTransientHeap->synchronizeAndReset(); } // D3D12Device members. @@ -3143,7 +3153,10 @@ public: DeviceInfo m_deviceInfo; ID3D12Device* m_device = nullptr; + VirtualObjectPool m_queueIndexAllocator; + RefPtr<CommandQueueImpl> m_resourceCommandQueue; + RefPtr<TransientResourceHeapImpl> m_resourceCommandTransientHeap; D3D12HostVisibleDescriptorAllocator m_rtvAllocator; D3D12HostVisibleDescriptorAllocator m_dsvAllocator; @@ -3162,6 +3175,53 @@ public: bool m_nvapi = false; }; +SLANG_NO_THROW Result SLANG_MCALL D3D12Device::TransientResourceHeapImpl::synchronizeAndReset() +{ + Array<HANDLE, 16> waitHandles; + for (auto& waitInfo : m_waitInfos) + { + if (waitInfo.waitValue != 0) + waitHandles.add(waitInfo.fenceEvent); + } + WaitForMultipleObjects((DWORD)waitHandles.getCount(), waitHandles.getBuffer(), TRUE, INFINITE); + m_viewHeap.deallocateAll(); + m_samplerHeap.deallocateAll(); + m_commandListAllocId = 0; + SLANG_RETURN_ON_FAIL(m_commandAllocator->Reset()); + return SLANG_OK; +} + +Result D3D12Device::TransientResourceHeapImpl::createCommandBuffer(ICommandBuffer** outCmdBuffer) +{ + if ((Index)m_commandListAllocId < m_commandBufferPool.getCount()) + { + auto result = static_cast<D3D12Device::CommandBufferImpl*>( + m_commandBufferPool[m_commandListAllocId].get()); + m_d3dCommandListPool[m_commandListAllocId]->Reset(m_commandAllocator, nullptr); + result->init(m_device, m_d3dCommandListPool[m_commandListAllocId], this); + ++m_commandListAllocId; + result->addRef(); + *outCmdBuffer = result; + return SLANG_OK; + } + ComPtr<ID3D12GraphicsCommandList> cmdList; + m_device->m_device->CreateCommandList( + 0, + D3D12_COMMAND_LIST_TYPE_DIRECT, + m_commandAllocator, + nullptr, + IID_PPV_ARGS(cmdList.writeRef())); + + m_d3dCommandListPool.add(cmdList); + RefPtr<CommandBufferImpl> cmdBuffer = new CommandBufferImpl(); + cmdBuffer->init(m_device, cmdList, this); + ComPtr<ICommandBuffer> cmdBufferPtr; + *cmdBufferPtr.writeRef() = cmdBuffer.detach(); + m_commandBufferPool.add(cmdBufferPtr); + ++m_commandListAllocId; + *outCmdBuffer = cmdBufferPtr.detach(); + return SLANG_OK; +} Result D3D12Device::PipelineCommandEncoder::_bindRenderState(Submitter* submitter) { @@ -3186,21 +3246,23 @@ Result D3D12Device::PipelineCommandEncoder::_bindRenderState(Submitter* submitte if (descSet.resourceDescriptorCount) { DescriptorTable table; - table.heap = &m_frame->m_viewHeap; - table.table = m_frame->m_viewHeap.allocate((int)descSet.resourceDescriptorCount); + table.heap = &m_transientHeap->m_viewHeap; + table.table = + m_transientHeap->m_viewHeap.allocate((int)descSet.resourceDescriptorCount); descriptorTables.add(table); } if (descSet.samplerDescriptorCount) { DescriptorTable table; - table.heap = &m_frame->m_samplerHeap; - table.table = m_frame->m_samplerHeap.allocate((int)descSet.samplerDescriptorCount); + table.heap = &m_transientHeap->m_samplerHeap; + table.table = + m_transientHeap->m_samplerHeap.allocate((int)descSet.samplerDescriptorCount); descriptorTables.add(table); } } RootBindingState bindState = {}; bindState.device = m_renderer; - bindState.frame = m_frame; + bindState.transientHeap = m_transientHeap; auto descTablesView = descriptorTables.getArrayView(); bindState.descriptorTables = descTablesView.arrayView; SLANG_RETURN_ON_FAIL(rootObjectImpl->bindObject(this, &bindState)); @@ -3213,14 +3275,29 @@ Result D3D12Device::PipelineCommandEncoder::_bindRenderState(Submitter* submitte return SLANG_OK; } -Result D3D12Device::createCommandQueueImpl( - uint32_t frameCount, - uint32_t viewHeapSize, - uint32_t samplerHeapSize, - D3D12Device::CommandQueueImpl** outQueue) +Result D3D12Device::createTransientResourceHeapImpl( + size_t constantBufferSize, + uint32_t viewDescriptors, + uint32_t samplerDescriptors, + TransientResourceHeapImpl** outHeap) +{ + RefPtr<TransientResourceHeapImpl> result = new TransientResourceHeapImpl(); + ITransientResourceHeap::Desc desc = {}; + desc.constantBufferSize = constantBufferSize; + SLANG_RETURN_ON_FAIL(result->init(desc, this, viewDescriptors, samplerDescriptors)); + *outHeap = result.detach(); + return SLANG_OK; +} + +Result D3D12Device::createCommandQueueImpl(D3D12Device::CommandQueueImpl** outQueue) { + int queueIndex = m_queueIndexAllocator.alloc(1); + // If we run out of queue index space, then the user is requesting too many queues. + if (queueIndex == -1) + return SLANG_FAIL; + RefPtr<D3D12Device::CommandQueueImpl> queue = new D3D12Device::CommandQueueImpl(); - SLANG_RETURN_ON_FAIL(queue->init(this, frameCount, viewHeapSize, samplerHeapSize)); + SLANG_RETURN_ON_FAIL(queue->init(this, (uint32_t)queueIndex)); *outQueue = queue.detach(); return SLANG_OK; } @@ -3313,23 +3390,6 @@ static void _initSrvDesc(IResource::Type resourceType, const ITextureResource::D } } -static void _initBufferResourceDesc(size_t bufferSize, D3D12_RESOURCE_DESC& out) -{ - out = {}; - - out.Dimension = D3D12_RESOURCE_DIMENSION_BUFFER; - out.Alignment = 0; - out.Width = bufferSize; - out.Height = 1; - out.DepthOrArraySize = 1; - out.MipLevels = 1; - out.Format = DXGI_FORMAT_UNKNOWN; - out.SampleDesc.Count = 1; - out.SampleDesc.Quality = 0; - out.Layout = D3D12_TEXTURE_LAYOUT_ROW_MAJOR; - out.Flags = D3D12_RESOURCE_FLAG_NONE; -} - Result D3D12Device::createBuffer(const D3D12_RESOURCE_DESC& resourceDesc, const void* srcData, size_t srcDataSize, D3D12Resource& uploadResource, D3D12_RESOURCE_STATES finalState, D3D12Resource& resourceOut) { const size_t bufferSize = size_t(resourceDesc.Width); @@ -3590,6 +3650,10 @@ Result D3D12Device::initialize(const Desc& desc) SLANG_RETURN_ON_FAIL(RendererBase::initialize(desc)); + // Initialize queue index allocator. + // Support max 32 queues. + m_queueIndexAllocator.initPool(32); + // Initialize DeviceInfo { m_info.deviceType = DeviceType::DirectX12; @@ -3743,7 +3807,8 @@ Result D3D12Device::initialize(const Desc& desc) m_desc = desc; // Create a command queue for internal resource transfer operations. - SLANG_RETURN_ON_FAIL(createCommandQueueImpl(1, 32, 4, m_resourceCommandQueue.writeRef())); + SLANG_RETURN_ON_FAIL(createCommandQueueImpl(m_resourceCommandQueue.writeRef())); + SLANG_RETURN_ON_FAIL(createTransientResourceHeapImpl(0, 8, 4, m_resourceCommandTransientHeap.writeRef())); SLANG_RETURN_ON_FAIL(m_cpuViewHeap.init (m_device, 8192, D3D12_DESCRIPTOR_HEAP_TYPE_CBV_SRV_UAV)); SLANG_RETURN_ON_FAIL(m_cpuSamplerHeap.init(m_device, 1024, D3D12_DESCRIPTOR_HEAP_TYPE_SAMPLER)); @@ -3764,10 +3829,21 @@ Result D3D12Device::initialize(const Desc& desc) return SLANG_OK; } +Result D3D12Device::createTransientResourceHeap( + const ITransientResourceHeap::Desc& desc, + ITransientResourceHeap** outHeap) +{ + RefPtr<TransientResourceHeapImpl> heap; + SLANG_RETURN_ON_FAIL( + createTransientResourceHeapImpl(desc.constantBufferSize, 8192, 1024, heap.writeRef())); + *outHeap = heap.detach(); + return SLANG_OK; +} + Result D3D12Device::createCommandQueue(const ICommandQueue::Desc& desc, ICommandQueue** outQueue) { RefPtr<CommandQueueImpl> queue; - SLANG_RETURN_ON_FAIL(createCommandQueueImpl(8, 4096, 1024, queue.writeRef())); + SLANG_RETURN_ON_FAIL(createCommandQueueImpl(queue.writeRef())); *outQueue = queue.detach(); return SLANG_OK; } diff --git a/tools/gfx/d3d12/resource-d3d12.cpp b/tools/gfx/d3d12/resource-d3d12.cpp index 397eee665..3f91a12be 100644 --- a/tools/gfx/d3d12/resource-d3d12.cpp +++ b/tools/gfx/d3d12/resource-d3d12.cpp @@ -72,63 +72,6 @@ void D3D12ResourceBase::transition( } } -/* !!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!! D3D12CounterFence !!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!! */ - -D3D12CounterFence::~D3D12CounterFence() -{ - if (m_event) - { - CloseHandle(m_event); - } -} - -Result D3D12CounterFence::init(ID3D12Device* device, uint64_t initialValue) -{ - m_currentValue = initialValue; - - SLANG_RETURN_ON_FAIL(device->CreateFence(m_currentValue, D3D12_FENCE_FLAG_NONE, IID_PPV_ARGS(m_fence.writeRef()))); - // Create an event handle to use for frame synchronization. - m_event = ::CreateEvent(nullptr, FALSE, FALSE, nullptr); - if (m_event == nullptr) - { - Result res = HRESULT_FROM_WIN32(GetLastError()); - return SLANG_FAILED(res) ? res : SLANG_FAIL; - } - return SLANG_OK; -} - -UInt64 D3D12CounterFence::nextSignal(ID3D12CommandQueue* commandQueue) -{ - // Increment the fence value. Save on the frame - we'll know that frame is done when the fence value >= - m_currentValue++; - // Schedule a Signal command in the queue. - Result res = commandQueue->Signal(m_fence, m_currentValue); - if (SLANG_FAILED(res)) - { - assert(!"Signal failed"); - } - return m_currentValue; -} - -void D3D12CounterFence::waitUntilCompleted(uint64_t completedValue) -{ - // You can only wait for a value that is less than or equal to the current value - assert(completedValue <= m_currentValue); - - // Wait until the previous frame is finished. - while (m_fence->GetCompletedValue() < completedValue) - { - // Make it signal with the current value - SLANG_ASSERT_VOID_ON_FAIL(m_fence->SetEventOnCompletion(completedValue, m_event)); - WaitForSingleObject(m_event, INFINITE); - } -} - -void D3D12CounterFence::nextSignalAndWait(ID3D12CommandQueue* commandQueue) -{ - waitUntilCompleted(nextSignal(commandQueue)); -} - /* !!!!!!!!!!!!!!!!!!!!!!!!! D3D12Resource !!!!!!!!!!!!!!!!!!!!!!!! */ /* static */void D3D12Resource::setDebugName(ID3D12Resource* resource, const char* name) diff --git a/tools/gfx/d3d12/resource-d3d12.h b/tools/gfx/d3d12/resource-d3d12.h index 8b6c28114..39b6c13db 100644 --- a/tools/gfx/d3d12/resource-d3d12.h +++ b/tools/gfx/d3d12/resource-d3d12.h @@ -46,53 +46,6 @@ protected: D3D12_RESOURCE_BARRIER m_barriers[MAX_BARRIERS]; }; -/*! \brief A class to simplify using Dx12 fences. - -A fence is a mechanism to track GPU work. This is achieved by having a counter that the CPU holds -called the current value. Calling nextSignal will increase the CPU counter, and add a fence -with that value to the commandQueue. When the GPU has completed all the work before the fence it will -update the completed value. This is typically used when -the CPU needs to know the GPU has finished some piece of work has completed. To do this the CPU -can check the completed value, and when it is greater or equal to the value returned by nextSignal the -CPU will know that all the work prior to when the nextSignal was added to the queue will have completed. - -NOTE! This cannot be used across threads, as for amongst other reasons SetEventOnCompletion -only works with a single value. - -Signal on the CommandQueue updates the fence on the GPU side. Signal on the fence object changes -the value on the CPU side (not used here). - -Useful article describing how Dx12 synchronization works: -https://msdn.microsoft.com/en-us/library/windows/desktop/dn899217%28v=vs.85%29.aspx -*/ -class D3D12CounterFence -{ -public: - /// Must be called before used - SlangResult init(ID3D12Device* device, uint64_t initialValue = 0); - /// Increases the counter, signals the queue and waits for the signal to be hit - void nextSignalAndWait(ID3D12CommandQueue* queue); - /// Signals with next counter value. Returns the value the signal was called on - uint64_t nextSignal(ID3D12CommandQueue* commandQueue); - /// Get the current value - SLANG_FORCE_INLINE uint64_t getCurrentValue() const { return m_currentValue; } - /// Get the completed value - SLANG_FORCE_INLINE uint64_t getCompletedValue() const { return m_fence->GetCompletedValue(); } - - /// Waits for the the specified value - void waitUntilCompleted(uint64_t completedValue); - - /// Ctor - D3D12CounterFence() :m_event(nullptr), m_currentValue(0) {} - /// Dtor - ~D3D12CounterFence(); - -protected: - HANDLE m_event; - Slang::ComPtr<ID3D12Fence> m_fence; - UINT64 m_currentValue; -}; - /** The base class for resource types allows for tracking of state. It does not allow for setting of the resource though, such that an interface can return a D3D12ResourceBase, and a client cant manipulate it's state, but it cannot replace/change the actual resource */ struct D3D12ResourceBase diff --git a/tools/gfx/immediate-renderer-base.cpp b/tools/gfx/immediate-renderer-base.cpp index 9402f7834..97bc63634 100644 --- a/tools/gfx/immediate-renderer-base.cpp +++ b/tools/gfx/immediate-renderer-base.cpp @@ -1,5 +1,6 @@ #include "immediate-renderer-base.h" #include "simple-render-pass-layout.h" +#include "simple-transient-resource-heap.h" #include "command-writer.h" #include "core/slang-basic.h" #include "core/slang-blob.h" @@ -402,15 +403,6 @@ public: virtual SLANG_NO_THROW const Desc& SLANG_MCALL getDesc() override { return m_desc; } - virtual SLANG_NO_THROW Result SLANG_MCALL - createCommandBuffer(ICommandBuffer** outCommandBuffer) override - { - RefPtr<CommandBufferImpl> newCmdBuffer = new CommandBufferImpl(); - newCmdBuffer->init(m_renderer); - *outCommandBuffer = newCmdBuffer.detach(); - return SLANG_OK; - } - virtual SLANG_NO_THROW void SLANG_MCALL executeCommandBuffers(uint32_t count, ICommandBuffer* const* commandBuffers) override { @@ -425,12 +417,26 @@ public: m_renderer->waitForGpu(); } }; + +using TransientResourceHeapImpl = + SimpleTransientResourceHeap<ImmediateRendererBase, CommandBufferImpl>; + } ImmediateRendererBase::ImmediateRendererBase() { m_queue = new CommandQueueImpl(this); } +SLANG_NO_THROW Result SLANG_MCALL ImmediateRendererBase::createTransientResourceHeap( + const ITransientResourceHeap::Desc& desc, + ITransientResourceHeap** outHeap) +{ + RefPtr<TransientResourceHeapImpl> result = new TransientResourceHeapImpl(); + SLANG_RETURN_ON_FAIL(result->init(this, desc)); + *outHeap = result.detach(); + return SLANG_OK; +} + SLANG_NO_THROW Result SLANG_MCALL ImmediateRendererBase::createCommandQueue( const ICommandQueue::Desc& desc, ICommandQueue** outQueue) diff --git a/tools/gfx/immediate-renderer-base.h b/tools/gfx/immediate-renderer-base.h index a78671e49..296cd15cb 100644 --- a/tools/gfx/immediate-renderer-base.h +++ b/tools/gfx/immediate-renderer-base.h @@ -64,6 +64,9 @@ public: virtual SLANG_NO_THROW Result SLANG_MCALL createCommandQueue(const ICommandQueue::Desc& desc, ICommandQueue** outQueue) override; + virtual SLANG_NO_THROW Result SLANG_MCALL createTransientResourceHeap( + const ITransientResourceHeap::Desc& desc, + ITransientResourceHeap** outHeap) override; virtual SLANG_NO_THROW Result SLANG_MCALL createRenderPassLayout( const IRenderPassLayout::Desc& desc, IRenderPassLayout** outRenderPassLayout) override; diff --git a/tools/gfx/renderer-shared.cpp b/tools/gfx/renderer-shared.cpp index 2e6105793..1571e9abf 100644 --- a/tools/gfx/renderer-shared.cpp +++ b/tools/gfx/renderer-shared.cpp @@ -11,6 +11,7 @@ const Slang::Guid GfxGUID::IID_ISlangUnknown = SLANG_UUID_ISlangUnknown; const Slang::Guid GfxGUID::IID_IShaderProgram = SLANG_UUID_IShaderProgram; const Slang::Guid GfxGUID::IID_IInputLayout = SLANG_UUID_IInputLayout; const Slang::Guid GfxGUID::IID_IPipelineState = SLANG_UUID_IPipelineState; +const Slang::Guid GfxGUID::IID_ITransientResourceHeap = SLANG_UUID_ITransientResourceHeap; const Slang::Guid GfxGUID::IID_IResourceView = SLANG_UUID_IResourceView; const Slang::Guid GfxGUID::IID_IFramebuffer = SLANG_UUID_IFrameBuffer; const Slang::Guid GfxGUID::IID_IFramebufferLayout = SLANG_UUID_IFramebufferLayout; diff --git a/tools/gfx/renderer-shared.h b/tools/gfx/renderer-shared.h index 41b9a31c9..d1ecebfce 100644 --- a/tools/gfx/renderer-shared.h +++ b/tools/gfx/renderer-shared.h @@ -10,10 +10,8 @@ namespace gfx struct GfxGUID { static const Slang::Guid IID_ISlangUnknown; - static const Slang::Guid IID_IDescriptorSetLayout; - static const Slang::Guid IID_IDescriptorSet; static const Slang::Guid IID_IShaderProgram; - static const Slang::Guid IID_IPipelineLayout; + static const Slang::Guid IID_ITransientResourceHeap; static const Slang::Guid IID_IPipelineState; static const Slang::Guid IID_IResourceView; static const Slang::Guid IID_IFramebuffer; diff --git a/tools/gfx/simple-transient-resource-heap.h b/tools/gfx/simple-transient-resource-heap.h new file mode 100644 index 000000000..5f6c32451 --- /dev/null +++ b/tools/gfx/simple-transient-resource-heap.h @@ -0,0 +1,52 @@ +// simple-render-pass-layout.h +#pragma once + +// Provide a simple no-op implementation for `ITransientResourceHeap` for targets that +// already support version management. + +#include "slang-gfx.h" + +namespace gfx +{ +template<typename TDevice, typename TCommandBuffer> +class SimpleTransientResourceHeap + : public ITransientResourceHeap + , public Slang::RefObject +{ +public: + SLANG_REF_OBJECT_IUNKNOWN_ALL + ITransientResourceHeap* getInterface(const Slang::Guid& guid) + { + if (guid == GfxGUID::IID_ISlangUnknown || guid == GfxGUID::IID_ITransientResourceHeap) + return static_cast<ITransientResourceHeap*>(this); + return nullptr; + } + +public: + TDevice* m_device; + ComPtr<IBufferResource> m_constantBuffer; + +public: + Result init(TDevice* device, const ITransientResourceHeap::Desc& desc) + { + m_device = device; + IBufferResource::Desc bufferDesc = {}; + bufferDesc.setDefaults(IResource::Usage::ConstantBuffer); + bufferDesc.sizeInBytes = desc.constantBufferSize; + bufferDesc.cpuAccessFlags = IResource::AccessFlag::Write; + SLANG_RETURN_ON_FAIL(device->createBufferResource( + IResource::Usage::ConstantBuffer, bufferDesc, nullptr, m_constantBuffer.writeRef())); + return SLANG_OK; + } + virtual SLANG_NO_THROW Result SLANG_MCALL + createCommandBuffer(ICommandBuffer** outCommandBuffer) override + { + Slang::RefPtr<TCommandBuffer> newCmdBuffer = new TCommandBuffer(); + newCmdBuffer->init(m_device); + *outCommandBuffer = newCmdBuffer.detach(); + return SLANG_OK; + } + + virtual SLANG_NO_THROW Result SLANG_MCALL synchronizeAndReset() override { return SLANG_OK; } +}; +} diff --git a/tools/gfx/vulkan/render-vk.cpp b/tools/gfx/vulkan/render-vk.cpp index d4396429b..96a0d1047 100644 --- a/tools/gfx/vulkan/render-vk.cpp +++ b/tools/gfx/vulkan/render-vk.cpp @@ -50,7 +50,10 @@ public: }; // Renderer implementation Result initVulkanInstanceAndDevice(bool useValidationLayer); - virtual SLANG_NO_THROW SlangResult SLANG_MCALL initialize(const Desc& desc) override; + virtual SLANG_NO_THROW Result SLANG_MCALL initialize(const Desc& desc) override; + virtual SLANG_NO_THROW Result SLANG_MCALL createTransientResourceHeap( + const ITransientResourceHeap::Desc& desc, + ITransientResourceHeap** outHeap) override; virtual SLANG_NO_THROW Result SLANG_MCALL createCommandQueue(const ICommandQueue::Desc& desc, ICommandQueue** outQueue) override; virtual SLANG_NO_THROW Result SLANG_MCALL createSwapchain( @@ -795,7 +798,6 @@ public: auto descriptorSetIndex = findOrAddDescriptorSet(typeLayout->getDescriptorSetSpaceOffset(s)); auto& descriptorSetInfo = m_descriptorSetBuildInfos[descriptorSetIndex]; - for (SlangInt r = 0; r < descriptorRangeCount; ++r) { auto slangBindingType = @@ -812,7 +814,6 @@ public: } auto vkDescriptorType = _mapDescriptorType(slangBindingType); - VkDescriptorSetLayoutBinding vkBindingRangeDesc = {}; vkBindingRangeDesc.binding = (uint32_t)typeLayout->getDescriptorSetDescriptorRangeIndexOffset(s, r); @@ -829,14 +830,6 @@ public: } descriptorSetInfo.vkBindings.add(vkBindingRangeDesc); } - VkDescriptorSetLayoutCreateInfo createInfo = {}; - createInfo.sType = VK_STRUCTURE_TYPE_DESCRIPTOR_SET_LAYOUT_CREATE_INFO; - createInfo.pBindings = descriptorSetInfo.vkBindings.getBuffer(); - createInfo.bindingCount = (uint32_t)descriptorSetInfo.vkBindings.getCount(); - VkDescriptorSetLayout vkDescSetLayout; - SLANG_RETURN_ON_FAIL(m_renderer->m_api.vkCreateDescriptorSetLayout( - m_renderer->m_api.m_device, &createInfo, nullptr, &vkDescSetLayout)); - descriptorSetInfo.descriptorSetLayout = vkDescSetLayout; } return SLANG_OK; } @@ -1019,6 +1012,19 @@ public: m_combinedTextureSamplerCount = builder->m_combinedTextureSamplerCount; m_subObjectCount = builder->m_subObjectCount; m_subObjectRanges = builder->m_subObjectRanges; + + // Create VkDescriptorSetLayout for all descriptor sets. + for (auto& descriptorSetInfo : m_descriptorSetInfos) + { + VkDescriptorSetLayoutCreateInfo createInfo = {}; + createInfo.sType = VK_STRUCTURE_TYPE_DESCRIPTOR_SET_LAYOUT_CREATE_INFO; + createInfo.pBindings = descriptorSetInfo.vkBindings.getBuffer(); + createInfo.bindingCount = (uint32_t)descriptorSetInfo.vkBindings.getCount(); + VkDescriptorSetLayout vkDescSetLayout; + SLANG_RETURN_ON_FAIL(renderer->m_api.vkCreateDescriptorSetLayout( + renderer->m_api.m_device, &createInfo, nullptr, &vkDescSetLayout)); + descriptorSetInfo.descriptorSetLayout = vkDescSetLayout; + } return SLANG_OK; } @@ -1145,7 +1151,8 @@ public: auto slangEntryPointLayout = entryPointLayout->getSlangLayout(); _addDescriptorSets( - slangEntryPointLayout->getTypeLayout(), slangEntryPointLayout->getVarLayout()); + _unwrapParameterGroups(slangEntryPointLayout->getTypeLayout()), + slangEntryPointLayout->getVarLayout()); m_entryPoints.add(info); } @@ -1210,7 +1217,7 @@ public: m_program = builder->m_program; m_programLayout = builder->m_programLayout; - m_entryPoints = builder->m_entryPoints; + m_entryPoints = _Move(builder->m_entryPoints); m_renderer = renderer; if (m_program->getSpecializationParamCount() != 0) @@ -2709,8 +2716,10 @@ public: VkCommandBuffer m_commandBuffer; VkCommandBuffer m_preCommandBuffer = VK_NULL_HANDLE; VkCommandPool m_pool; + VkFence m_fence; VKDevice* m_renderer; DescriptorSetAllocator* m_transientDescSetAllocator; + bool m_isPreCommandBufferEmpty = true; // Command buffers are deallocated by its command pool, // so no need to free individually. ~CommandBufferImpl() = default; @@ -2718,11 +2727,13 @@ public: Result init( VKDevice* renderer, VkCommandPool pool, + VkFence fence, DescriptorSetAllocator* transientDescSetAllocator) { m_renderer = renderer; m_transientDescSetAllocator = transientDescSetAllocator; m_pool = pool; + m_fence = fence; auto& api = renderer->m_api; VkCommandBufferAllocateInfo allocInfo = {}; @@ -2733,12 +2744,23 @@ public: SLANG_VK_RETURN_ON_FAIL( api.vkAllocateCommandBuffers(api.m_device, &allocInfo, &m_commandBuffer)); + beginCommandBuffer(); + return SLANG_OK; + } + + void beginCommandBuffer() + { + auto& api = m_renderer->m_api; VkCommandBufferBeginInfo beginInfo = { VK_STRUCTURE_TYPE_COMMAND_BUFFER_BEGIN_INFO, nullptr, VK_COMMAND_BUFFER_USAGE_ONE_TIME_SUBMIT_BIT}; api.vkBeginCommandBuffer(m_commandBuffer, &beginInfo); - return SLANG_OK; + if (m_preCommandBuffer) + { + api.vkBeginCommandBuffer(m_preCommandBuffer, &beginInfo); + } + m_isPreCommandBufferEmpty = true; } Result createPreCommandBuffer() @@ -2761,6 +2783,7 @@ public: VkCommandBuffer getPreCommandBuffer() { + m_isPreCommandBufferEmpty = false; if (m_preCommandBuffer) return m_preCommandBuffer; createPreCommandBuffer(); @@ -3191,7 +3214,7 @@ public: virtual SLANG_NO_THROW void SLANG_MCALL close() override { auto& vkAPI = m_renderer->m_api; - if (m_preCommandBuffer != VK_NULL_HANDLE) + if (!m_isPreCommandBufferEmpty) { // `preCmdBuffer` contains buffer transfer commands for shader object // uniform buffers, and we need a memory barrier here to ensure the @@ -3231,56 +3254,34 @@ public: public: Desc m_desc; - uint32_t m_poolIndex; RefPtr<VKDevice> m_renderer; VkQueue m_queue; uint32_t m_queueFamilyIndex; VkSemaphore m_pendingWaitSemaphore = VK_NULL_HANDLE; List<VkCommandBuffer> m_submitCommandBuffers; - static const int kCommandPoolCount = 8; - VkCommandPool m_commandPools[kCommandPoolCount]; - DescriptorSetAllocator m_descSetAllocators[kCommandPoolCount]; - VkFence m_fences[kCommandPoolCount]; - VkSemaphore m_semaphores[kCommandPoolCount]; + static const int kSemaphoreCount = 2; + uint32_t m_currentSemaphoreIndex; + VkSemaphore m_semaphores[kSemaphoreCount]; ~CommandQueueImpl() { m_renderer->m_api.vkQueueWaitIdle(m_queue); m_renderer->m_queueAllocCount--; - for (int i = 0; i < kCommandPoolCount; i++) + for (int i = 0; i < kSemaphoreCount; i++) { - m_renderer->m_api.vkDestroyCommandPool( - m_renderer->m_api.m_device, m_commandPools[i], nullptr); - m_renderer->m_api.vkDestroyFence(m_renderer->m_api.m_device, m_fences[i], nullptr); m_renderer->m_api.vkDestroySemaphore( m_renderer->m_api.m_device, m_semaphores[i], nullptr); - m_descSetAllocators[i].close(); } } void init(VKDevice* renderer, VkQueue queue, uint32_t queueFamilyIndex) { m_renderer = renderer; - m_poolIndex = 0; + m_currentSemaphoreIndex = 0; m_queue = queue; m_queueFamilyIndex = queueFamilyIndex; - for (int i = 0; i < kCommandPoolCount; i++) + for (int i = 0; i < kSemaphoreCount; i++) { - m_descSetAllocators[i].m_api = &m_renderer->m_api; - - VkCommandPoolCreateInfo poolCreateInfo = {}; - poolCreateInfo.sType = VK_STRUCTURE_TYPE_COMMAND_POOL_CREATE_INFO; - poolCreateInfo.flags = VK_COMMAND_POOL_CREATE_RESET_COMMAND_BUFFER_BIT; - poolCreateInfo.queueFamilyIndex = queueFamilyIndex; - m_renderer->m_api.vkCreateCommandPool( - m_renderer->m_api.m_device, &poolCreateInfo, nullptr, &m_commandPools[i]); - - VkFenceCreateInfo fenceCreateInfo = {}; - fenceCreateInfo.sType = VK_STRUCTURE_TYPE_FENCE_CREATE_INFO; - fenceCreateInfo.flags = VK_FENCE_CREATE_SIGNALED_BIT; - m_renderer->m_api.vkCreateFence( - m_renderer->m_api.m_device, &fenceCreateInfo, nullptr, &m_fences[i]); - VkSemaphoreCreateInfo semaphoreCreateInfo = {}; semaphoreCreateInfo.sType = VK_STRUCTURE_TYPE_SEMAPHORE_CREATE_INFO; semaphoreCreateInfo.flags = 0; @@ -3289,24 +3290,6 @@ public: } } - // Swaps to and resets the next command pool. - // Wait if command lists in the next pool are still in flight. - Result swapPools() - { - auto& vkAPI = m_renderer->m_api; - m_poolIndex++; - m_poolIndex = m_poolIndex % kCommandPoolCount; - - if (vkAPI.vkWaitForFences(vkAPI.m_device, 1, &m_fences[m_poolIndex], 1, UINT64_MAX) != - VK_SUCCESS) - { - return SLANG_FAIL; - } - vkAPI.vkResetCommandPool(vkAPI.m_device, m_commandPools[m_poolIndex], 0); - m_descSetAllocators[m_poolIndex].reset(); - return SLANG_OK; - } - virtual SLANG_NO_THROW void SLANG_MCALL wait() override { auto& vkAPI = m_renderer->m_api; @@ -3318,33 +3301,26 @@ public: return m_desc; } - virtual SLANG_NO_THROW Result SLANG_MCALL - createCommandBuffer(ICommandBuffer** result) override - { - RefPtr<CommandBufferImpl> commandBuffer = new CommandBufferImpl(); - SLANG_RETURN_ON_FAIL(commandBuffer->init( - m_renderer, m_commandPools[m_poolIndex], &m_descSetAllocators[m_poolIndex])); - *result = commandBuffer.detach(); - return SLANG_OK; - } - virtual SLANG_NO_THROW void SLANG_MCALL executeCommandBuffers( uint32_t count, ICommandBuffer* const* commandBuffers) override { + if (count == 0) + return; + auto& vkAPI = m_renderer->m_api; m_submitCommandBuffers.clear(); for (uint32_t i = 0; i < count; i++) { auto cmdBufImpl = static_cast<CommandBufferImpl*>(commandBuffers[i]); - if (cmdBufImpl->m_preCommandBuffer != VK_NULL_HANDLE) + if (!cmdBufImpl->m_isPreCommandBufferEmpty) m_submitCommandBuffers.add(cmdBufImpl->m_preCommandBuffer); auto vkCmdBuf = cmdBufImpl->m_commandBuffer; m_submitCommandBuffers.add(vkCmdBuf); } VkSemaphore waitSemaphore = m_pendingWaitSemaphore; - VkSemaphore signalSemaphore = m_semaphores[m_poolIndex]; + VkSemaphore signalSemaphore = m_semaphores[m_currentSemaphoreIndex]; VkSubmitInfo submitInfo = {}; submitInfo.sType = VK_STRUCTURE_TYPE_SUBMIT_INFO; VkPipelineStageFlags stageFlag = VK_PIPELINE_STAGE_BOTTOM_OF_PIPE_BIT; @@ -3358,11 +3334,50 @@ public: } submitInfo.signalSemaphoreCount = 1; submitInfo.pSignalSemaphores = &signalSemaphore; - vkAPI.vkResetFences(vkAPI.m_device, 1, &m_fences[m_poolIndex]); - vkAPI.vkQueueSubmit(m_queue, 1, &submitInfo, m_fences[m_poolIndex]); + + auto fence = static_cast<CommandBufferImpl*>(commandBuffers[0])->m_fence; + vkAPI.vkResetFences(vkAPI.m_device, 1, &fence); + vkAPI.vkQueueSubmit(m_queue, 1, &submitInfo, fence); m_pendingWaitSemaphore = signalSemaphore; - swapPools(); + + m_currentSemaphoreIndex++; + m_currentSemaphoreIndex = m_currentSemaphoreIndex % kSemaphoreCount; + } + }; + + class TransientResourceHeapImpl + : public ITransientResourceHeap + , public RefObject + { + public: + SLANG_REF_OBJECT_IUNKNOWN_ALL + ITransientResourceHeap* getInterface(const Slang::Guid& guid) + { + if (guid == GfxGUID::IID_ISlangUnknown || guid == GfxGUID::IID_ITransientResourceHeap) + return static_cast<ITransientResourceHeap*>(this); + return nullptr; + } + + public: + VkCommandPool m_commandPool; + DescriptorSetAllocator m_descSetAllocator; + VkFence m_fence; + List<RefPtr<CommandBufferImpl>> m_commandBufferPool; + uint32_t m_commandBufferAllocId = 0; + RefPtr<BufferResourceImpl> m_constantBuffer; + RefPtr<VKDevice> m_device; + + Result init(const ITransientResourceHeap::Desc& desc, VKDevice* device); + ~TransientResourceHeapImpl() + { + m_device->m_api.vkDestroyCommandPool(m_device->m_api.m_device, m_commandPool, nullptr); + m_device->m_api.vkDestroyFence(m_device->m_api.m_device, m_fence, nullptr); + m_descSetAllocator.close(); } + public: + virtual SLANG_NO_THROW Result SLANG_MCALL + createCommandBuffer(ICommandBuffer** outCommandBuffer) override; + virtual SLANG_NO_THROW Result SLANG_MCALL synchronizeAndReset() override; }; class SwapchainImpl @@ -3742,6 +3757,15 @@ public: void _transitionImageLayout(VkImage image, VkFormat format, const TextureResource::Desc& desc, VkImageLayout oldLayout, VkImageLayout newLayout); + uint32_t getQueueFamilyIndex(ICommandQueue::QueueType queueType) + { + switch (queueType) + { + case ICommandQueue::QueueType::Graphics: + default: + return m_queueFamilyIndex; + } + } public: // VKDevice members. @@ -4282,6 +4306,71 @@ void VKDevice::waitForGpu() m_deviceQueue.flushAndWait(); } +Result VKDevice::TransientResourceHeapImpl::init( + const ITransientResourceHeap::Desc& desc, + VKDevice* device) +{ + m_device = device; + m_descSetAllocator.m_api = &device->m_api; + + VkCommandPoolCreateInfo poolCreateInfo = {}; + poolCreateInfo.sType = VK_STRUCTURE_TYPE_COMMAND_POOL_CREATE_INFO; + poolCreateInfo.flags = VK_COMMAND_POOL_CREATE_RESET_COMMAND_BUFFER_BIT; + poolCreateInfo.queueFamilyIndex = + device->getQueueFamilyIndex(ICommandQueue::QueueType::Graphics); + device->m_api.vkCreateCommandPool( + device->m_api.m_device, &poolCreateInfo, nullptr, &m_commandPool); + + VkFenceCreateInfo fenceCreateInfo = {}; + fenceCreateInfo.sType = VK_STRUCTURE_TYPE_FENCE_CREATE_INFO; + fenceCreateInfo.flags = VK_FENCE_CREATE_SIGNALED_BIT; + device->m_api.vkCreateFence(device->m_api.m_device, &fenceCreateInfo, nullptr, &m_fence); + return SLANG_OK; +} + +Result VKDevice::TransientResourceHeapImpl::createCommandBuffer(ICommandBuffer** outCmdBuffer) +{ + if (m_commandBufferAllocId < (uint32_t)m_commandBufferPool.getCount()) + { + auto result = m_commandBufferPool[m_commandBufferAllocId]; + result->beginCommandBuffer(); + m_commandBufferAllocId++; + *outCmdBuffer = result.detach(); + return SLANG_OK; + } + + RefPtr<CommandBufferImpl> commandBuffer = new CommandBufferImpl(); + SLANG_RETURN_ON_FAIL(commandBuffer->init( + m_device, m_commandPool, m_fence, &m_descSetAllocator)); + m_commandBufferPool.add(commandBuffer); + m_commandBufferAllocId++; + *outCmdBuffer = commandBuffer.detach(); + return SLANG_OK; +} + +Result VKDevice::TransientResourceHeapImpl::synchronizeAndReset() +{ + m_commandBufferAllocId = 0; + auto& api = m_device->m_api; + if (api.vkWaitForFences(api.m_device, 1, &m_fence, 1, UINT64_MAX) != VK_SUCCESS) + { + return SLANG_FAIL; + } + api.vkResetCommandPool(api.m_device, m_commandPool, 0); + m_descSetAllocator.reset(); + return SLANG_OK; +} + +Result VKDevice::createTransientResourceHeap( + const ITransientResourceHeap::Desc& desc, + ITransientResourceHeap** outHeap) +{ + RefPtr<TransientResourceHeapImpl> result = new TransientResourceHeapImpl(); + SLANG_RETURN_ON_FAIL(result->init(desc, this)); + *outHeap = result.detach(); + return SLANG_OK; +} + Result VKDevice::createCommandQueue(const ICommandQueue::Desc& desc, ICommandQueue** outQueue) { // Only support one queue for now. @@ -4430,23 +4519,6 @@ static VkBufferUsageFlagBits _calcBufferUsageFlags(int bindFlags) return VkBufferUsageFlagBits(dstFlags); } -static VkBufferUsageFlags _calcBufferUsageFlags(int bindFlags, int cpuAccessFlags, const void* initData) -{ - VkBufferUsageFlags usage = _calcBufferUsageFlags(bindFlags); - - if (cpuAccessFlags & IResource::AccessFlag::Read) - { - // If it can be read from, set this - usage |= VK_BUFFER_USAGE_TRANSFER_SRC_BIT; - } - if ((cpuAccessFlags & IResource::AccessFlag::Write) || initData) - { - usage |= VK_BUFFER_USAGE_TRANSFER_DST_BIT; - } - - return usage; -} - static VkImageUsageFlagBits _calcImageUsageFlags(IResource::BindFlag::Enum bind) { typedef IResource::BindFlag BindFlag; @@ -4890,7 +4962,8 @@ Result VKDevice::createBufferResource(IResource::Usage initialUsage, const IBuff VkMemoryPropertyFlags reqMemoryProperties = 0; - VkBufferUsageFlags usage = _calcBufferUsageFlags(desc.bindFlags, desc.cpuAccessFlags, initData); + VkBufferUsageFlags usage = _calcBufferUsageFlags(desc.bindFlags) | + VK_BUFFER_USAGE_TRANSFER_SRC_BIT | VK_BUFFER_USAGE_TRANSFER_DST_BIT; switch (initialUsage) { diff --git a/tools/platform/gui.cpp b/tools/platform/gui.cpp index e33a504bb..f78a52ce4 100644 --- a/tools/platform/gui.cpp +++ b/tools/platform/gui.cpp @@ -188,7 +188,7 @@ void GUI::beginFrame() ImGui::NewFrame(); } -void GUI::endFrame(IFramebuffer* framebuffer) +void GUI::endFrame(ITransientResourceHeap* transientHeap, IFramebuffer* framebuffer) { ImGui::Render(); @@ -217,7 +217,7 @@ void GUI::endFrame(IFramebuffer* framebuffer) auto indexBuffer = device->createBufferResource( IResource::Usage::IndexBuffer, indexBufferDesc); - auto cmdBuf = queue->createCommandBuffer(); + auto cmdBuf = transientHeap->createCommandBuffer(); auto encoder = cmdBuf->encodeResourceCommands(); { for(int ii = 0; ii < commandListCount; ++ii) diff --git a/tools/platform/gui.h b/tools/platform/gui.h index ef5310cd4..e3975f3ff 100644 --- a/tools/platform/gui.h +++ b/tools/platform/gui.h @@ -19,7 +19,7 @@ struct GUI : Slang::RefObject ~GUI(); void beginFrame(); - void endFrame(gfx::IFramebuffer* framebuffer); + void endFrame(gfx::ITransientResourceHeap* transientHeap, gfx::IFramebuffer* framebuffer); private: Slang::ComPtr<gfx::IDevice> device; diff --git a/tools/render-test/render-test-main.cpp b/tools/render-test/render-test-main.cpp index 87d9a246d..27311d67a 100644 --- a/tools/render-test/render-test-main.cpp +++ b/tools/render-test/render-test-main.cpp @@ -119,6 +119,7 @@ protected: ComPtr<IDevice> m_device; ComPtr<ICommandQueue> m_queue; + ComPtr<ITransientResourceHeap> m_transientHeap; ComPtr<IRenderPassLayout> m_renderPass; ComPtr<IInputLayout> m_inputLayout; ComPtr<IBufferResource> m_vertexBuffer; @@ -530,9 +531,13 @@ Result RenderTestApp::_initializeShaders( void RenderTestApp::_initializeRenderPass() { + ITransientResourceHeap::Desc transientHeapDesc = {}; + transientHeapDesc.constantBufferSize = 4096 * 1024; + m_transientHeap = m_device->createTransientResourceHeap(transientHeapDesc); + ICommandQueue::Desc queueDesc = {ICommandQueue::QueueType::Graphics}; m_queue = m_device->createCommandQueue(queueDesc); - + gfx::ITextureResource::Desc depthBufferDesc; depthBufferDesc.setDefaults(gfx::IResource::Usage::DepthWrite); depthBufferDesc.init2D( @@ -695,7 +700,8 @@ Result RenderTestApp::writeBindingOutput(const char* fileName) SLANG_RETURN_ON_FAIL(m_device->createBufferResource(IResource::Usage::CopyDest, stagingBufferDesc, nullptr, stagingBuffer.writeRef())); ComPtr<ICommandBuffer> commandBuffer; - SLANG_RETURN_ON_FAIL(m_queue->createCommandBuffer(commandBuffer.writeRef())); + SLANG_RETURN_ON_FAIL( + m_transientHeap->createCommandBuffer(commandBuffer.writeRef())); ComPtr<IResourceCommandEncoder> encoder; commandBuffer->encodeResourceCommands(encoder.writeRef()); @@ -704,7 +710,7 @@ Result RenderTestApp::writeBindingOutput(const char* fileName) commandBuffer->close(); m_queue->executeCommandBuffer(commandBuffer); - m_queue->wait(); + m_transientHeap->synchronizeAndReset(); m_device->readBufferResource(stagingBuffer, 0, bufferSize, blob.writeRef()); } @@ -743,7 +749,7 @@ Result RenderTestApp::writeScreen(const char* filename) Result RenderTestApp::update() { - auto commandBuffer = m_queue->createCommandBuffer(); + auto commandBuffer = m_transientHeap->createCommandBuffer(); if (m_options.shaderType == Options::ShaderProgramType::Compute) { auto encoder = commandBuffer->encodeComputeCommands(); |
