From 1296c7bb55b14db24308f31cacdda7f7a71fc937 Mon Sep 17 00:00:00 2001 From: Yong He Date: Sun, 17 Jan 2021 22:00:49 -0800 Subject: Make `gfx` compile to a DLL. (#1660) * Make `gfx` compile to a DLL. * Fix cuda * Fix cuda build * Bug gl screen capture bug. --- .../cpu-hello-world/cpu-hello-world.vcxproj | 3 + build/visual-studio/gfx-util/gfx-util.vcxproj | 169 ++++++++++++++ .../gfx-util/gfx-util.vcxproj.filters | 21 ++ build/visual-studio/gfx/gfx.vcxproj | 46 ++-- build/visual-studio/gfx/gfx.vcxproj.filters | 18 -- .../gpu-printing/gpu-printing.vcxproj | 3 + .../visual-studio/hello-world/hello-world.vcxproj | 3 + .../heterogeneous-hello-world.vcxproj | 3 + .../model-viewer/model-viewer.vcxproj | 3 + .../render-test-tool/render-test-tool.vcxproj | 5 + .../render-test-tool.vcxproj.filters | 6 + build/visual-studio/shader-toy/shader-toy.vcxproj | 3 + examples/gpu-printing/main.cpp | 3 +- examples/hello-world/main.cpp | 3 +- examples/heterogeneous-hello-world/main.cpp | 3 +- examples/model-viewer/main.cpp | 3 +- examples/shader-toy/main.cpp | 3 +- premake5.lua | 25 ++- slang.sln | 11 + tools/gfx-util/shader-cursor.cpp | 249 +++++++++++++++++++++ tools/gfx-util/shader-cursor.h | 123 ++++++++++ tools/gfx/cuda/render-cuda.cpp | 10 +- tools/gfx/d3d11/render-d3d11.cpp | 75 +++++-- tools/gfx/d3d12/render-d3d12.cpp | 40 +++- tools/gfx/open-gl/render-gl.cpp | 39 +++- tools/gfx/render.h | 5 +- tools/gfx/shader-cursor.cpp | 249 --------------------- tools/gfx/shader-cursor.h | 123 ---------- tools/gfx/slang-gfx-helper.cpp | 6 - tools/gfx/slang-gfx-helper.h | 9 - tools/gfx/surface.cpp | 223 ------------------ tools/gfx/surface.h | 86 ------- tools/gfx/vulkan/render-vk.cpp | 39 +++- tools/render-test/render-test-main.cpp | 19 +- tools/render-test/surface.cpp | 223 ++++++++++++++++++ tools/render-test/surface.h | 86 +++++++ 36 files changed, 1137 insertions(+), 801 deletions(-) create mode 100644 build/visual-studio/gfx-util/gfx-util.vcxproj create mode 100644 build/visual-studio/gfx-util/gfx-util.vcxproj.filters create mode 100644 tools/gfx-util/shader-cursor.cpp create mode 100644 tools/gfx-util/shader-cursor.h delete mode 100644 tools/gfx/shader-cursor.cpp delete mode 100644 tools/gfx/shader-cursor.h delete mode 100644 tools/gfx/slang-gfx-helper.cpp delete mode 100644 tools/gfx/slang-gfx-helper.h delete mode 100644 tools/gfx/surface.cpp delete mode 100644 tools/gfx/surface.h create mode 100644 tools/render-test/surface.cpp create mode 100644 tools/render-test/surface.h 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 9d4c819cf..2fb93b42c 100644 --- a/build/visual-studio/cpu-hello-world/cpu-hello-world.vcxproj +++ b/build/visual-studio/cpu-hello-world/cpu-hello-world.vcxproj @@ -177,6 +177,9 @@ {222F7498-B40C-4F3F-A704-DDEB91A4484A} + + {F5ADB74E-02A7-44FB-AA3B-FC02F8AC7A4B} + {3565FE5E-4FA3-11EB-AE93-0242AC130002} diff --git a/build/visual-studio/gfx-util/gfx-util.vcxproj b/build/visual-studio/gfx-util/gfx-util.vcxproj new file mode 100644 index 000000000..d75105524 --- /dev/null +++ b/build/visual-studio/gfx-util/gfx-util.vcxproj @@ -0,0 +1,169 @@ + + + + + Debug + Win32 + + + Debug + x64 + + + Release + Win32 + + + Release + x64 + + + + {F5ADB74E-02A7-44FB-AA3B-FC02F8AC7A4B} + true + Win32Proj + gfx-util + + + + StaticLibrary + true + Unicode + v140 + + + StaticLibrary + true + Unicode + v140 + + + StaticLibrary + false + Unicode + v140 + + + StaticLibrary + false + Unicode + v140 + + + + + + + + + + + + + + + + + + + ..\..\..\bin\windows-x86\debug\ + ..\..\..\intermediate\windows-x86\debug\gfx-util\ + gfx-util + .lib + + + ..\..\..\bin\windows-x64\debug\ + ..\..\..\intermediate\windows-x64\debug\gfx-util\ + gfx-util + .lib + + + ..\..\..\bin\windows-x86\release\ + ..\..\..\intermediate\windows-x86\release\gfx-util\ + gfx-util + .lib + + + ..\..\..\bin\windows-x64\release\ + ..\..\..\intermediate\windows-x64\release\gfx-util\ + gfx-util + .lib + + + + NotUsing + Level3 + _DEBUG;%(PreprocessorDefinitions) + ..\..\..;..\..\..\source;%(AdditionalIncludeDirectories) + EditAndContinue + Disabled + MultiThreadedDebug + + + Windows + true + + + + + NotUsing + Level3 + _DEBUG;%(PreprocessorDefinitions) + ..\..\..;..\..\..\source;%(AdditionalIncludeDirectories) + EditAndContinue + Disabled + MultiThreadedDebug + + + Windows + true + + + + + NotUsing + Level3 + NDEBUG;%(PreprocessorDefinitions) + ..\..\..;..\..\..\source;%(AdditionalIncludeDirectories) + Full + true + true + false + true + MultiThreaded + + + Windows + true + true + + + + + NotUsing + Level3 + NDEBUG;%(PreprocessorDefinitions) + ..\..\..;..\..\..\source;%(AdditionalIncludeDirectories) + Full + true + true + false + true + MultiThreaded + + + Windows + true + true + + + + + + + + + + + + \ No newline at end of file diff --git a/build/visual-studio/gfx-util/gfx-util.vcxproj.filters b/build/visual-studio/gfx-util/gfx-util.vcxproj.filters new file mode 100644 index 000000000..23e932b8c --- /dev/null +++ b/build/visual-studio/gfx-util/gfx-util.vcxproj.filters @@ -0,0 +1,21 @@ + + + + + {21EB8090-0D4E-1035-B6D3-48EBA215DCB7} + + + {E9C7FDCE-D52A-8D73-7EB0-C5296AF258F6} + + + + + Header Files + + + + + Source Files + + + \ No newline at end of file diff --git a/build/visual-studio/gfx/gfx.vcxproj b/build/visual-studio/gfx/gfx.vcxproj index 789440fb7..0ed5ed233 100644 --- a/build/visual-studio/gfx/gfx.vcxproj +++ b/build/visual-studio/gfx/gfx.vcxproj @@ -27,25 +27,25 @@ - StaticLibrary + DynamicLibrary true Unicode v140 - StaticLibrary + DynamicLibrary true Unicode v140 - StaticLibrary + DynamicLibrary false Unicode v140 - StaticLibrary + DynamicLibrary false Unicode v140 @@ -67,34 +67,38 @@ + true ..\..\..\bin\windows-x86\debug\ ..\..\..\intermediate\windows-x86\debug\gfx\ gfx - .lib + .dll + true ..\..\..\bin\windows-x64\debug\ ..\..\..\intermediate\windows-x64\debug\gfx\ gfx - .lib + .dll + false ..\..\..\bin\windows-x86\release\ ..\..\..\intermediate\windows-x86\release\gfx\ gfx - .lib + .dll + false ..\..\..\bin\windows-x64\release\ ..\..\..\intermediate\windows-x64\release\gfx\ gfx - .lib + .dll NotUsing Level3 - _DEBUG;%(PreprocessorDefinitions) + _DEBUG;SLANG_GFX_DYNAMIC;SLANG_GFX_DYNAMIC_EXPORT;%(PreprocessorDefinitions) ..\..\..;..\..\..\external;..\..\..\source;%(AdditionalIncludeDirectories) EditAndContinue Disabled @@ -103,6 +107,7 @@ Windows true + ..\..\..\bin\windows-x86\debug\gfx.lib "$(SolutionDir)tools\copy-hlsl-libs.bat" "$(WindowsSdkDir)Redist/D3D/x86/" "../../../bin/windows-x86/debug/" @@ -112,7 +117,7 @@ NotUsing Level3 - _DEBUG;%(PreprocessorDefinitions) + _DEBUG;SLANG_GFX_DYNAMIC;SLANG_GFX_DYNAMIC_EXPORT;%(PreprocessorDefinitions) ..\..\..;..\..\..\external;..\..\..\source;%(AdditionalIncludeDirectories) EditAndContinue Disabled @@ -121,6 +126,7 @@ Windows true + ..\..\..\bin\windows-x64\debug\gfx.lib "$(SolutionDir)tools\copy-hlsl-libs.bat" "$(WindowsSdkDir)Redist/D3D/x64/" "../../../bin/windows-x64/debug/" @@ -130,7 +136,7 @@ NotUsing Level3 - NDEBUG;%(PreprocessorDefinitions) + NDEBUG;SLANG_GFX_DYNAMIC;SLANG_GFX_DYNAMIC_EXPORT;%(PreprocessorDefinitions) ..\..\..;..\..\..\external;..\..\..\source;%(AdditionalIncludeDirectories) Full true @@ -143,6 +149,7 @@ Windows true true + ..\..\..\bin\windows-x86\release\gfx.lib "$(SolutionDir)tools\copy-hlsl-libs.bat" "$(WindowsSdkDir)Redist/D3D/x86/" "../../../bin/windows-x86/release/" @@ -152,7 +159,7 @@ NotUsing Level3 - NDEBUG;%(PreprocessorDefinitions) + NDEBUG;SLANG_GFX_DYNAMIC;SLANG_GFX_DYNAMIC_EXPORT;%(PreprocessorDefinitions) ..\..\..;..\..\..\external;..\..\..\source;%(AdditionalIncludeDirectories) Full true @@ -165,6 +172,7 @@ Windows true true + ..\..\..\bin\windows-x64\release\gfx.lib "$(SolutionDir)tools\copy-hlsl-libs.bat" "$(WindowsSdkDir)Redist/D3D/x64/" "../../../bin/windows-x64/release/" @@ -185,9 +193,6 @@ - - - @@ -209,9 +214,6 @@ - - - @@ -219,6 +221,14 @@ + + + {F9BE7957-8399-899E-0C49-E714FDDD4B65} + + + {DB00DA62-0533-4AFD-B59F-A67D5B3A0808} + + diff --git a/build/visual-studio/gfx/gfx.vcxproj.filters b/build/visual-studio/gfx/gfx.vcxproj.filters index 90caef2f9..97658fffa 100644 --- a/build/visual-studio/gfx/gfx.vcxproj.filters +++ b/build/visual-studio/gfx/gfx.vcxproj.filters @@ -51,15 +51,6 @@ Header Files - - Header Files - - - Header Files - - - Header Files - Header Files @@ -119,15 +110,6 @@ Source Files - - Source Files - - - Source Files - - - Source Files - Source Files diff --git a/build/visual-studio/gpu-printing/gpu-printing.vcxproj b/build/visual-studio/gpu-printing/gpu-printing.vcxproj index 9b8834666..f7b783f39 100644 --- a/build/visual-studio/gpu-printing/gpu-printing.vcxproj +++ b/build/visual-studio/gpu-printing/gpu-printing.vcxproj @@ -183,6 +183,9 @@ {222F7498-B40C-4F3F-A704-DDEB91A4484A} + + {F5ADB74E-02A7-44FB-AA3B-FC02F8AC7A4B} + {3565FE5E-4FA3-11EB-AE93-0242AC130002} diff --git a/build/visual-studio/hello-world/hello-world.vcxproj b/build/visual-studio/hello-world/hello-world.vcxproj index 60dde7007..a7a51362b 100644 --- a/build/visual-studio/hello-world/hello-world.vcxproj +++ b/build/visual-studio/hello-world/hello-world.vcxproj @@ -177,6 +177,9 @@ {222F7498-B40C-4F3F-A704-DDEB91A4484A} + + {F5ADB74E-02A7-44FB-AA3B-FC02F8AC7A4B} + {3565FE5E-4FA3-11EB-AE93-0242AC130002} diff --git a/build/visual-studio/heterogeneous-hello-world/heterogeneous-hello-world.vcxproj b/build/visual-studio/heterogeneous-hello-world/heterogeneous-hello-world.vcxproj index 74bd9213b..78bdb1f3e 100644 --- a/build/visual-studio/heterogeneous-hello-world/heterogeneous-hello-world.vcxproj +++ b/build/visual-studio/heterogeneous-hello-world/heterogeneous-hello-world.vcxproj @@ -178,6 +178,9 @@ {222F7498-B40C-4F3F-A704-DDEB91A4484A} + + {F5ADB74E-02A7-44FB-AA3B-FC02F8AC7A4B} + {3565FE5E-4FA3-11EB-AE93-0242AC130002} diff --git a/build/visual-studio/model-viewer/model-viewer.vcxproj b/build/visual-studio/model-viewer/model-viewer.vcxproj index b7523fe4f..8c8044d48 100644 --- a/build/visual-studio/model-viewer/model-viewer.vcxproj +++ b/build/visual-studio/model-viewer/model-viewer.vcxproj @@ -177,6 +177,9 @@ {222F7498-B40C-4F3F-A704-DDEB91A4484A} + + {F5ADB74E-02A7-44FB-AA3B-FC02F8AC7A4B} + {3565FE5E-4FA3-11EB-AE93-0242AC130002} diff --git a/build/visual-studio/render-test-tool/render-test-tool.vcxproj b/build/visual-studio/render-test-tool/render-test-tool.vcxproj index b9841e122..e74392972 100644 --- a/build/visual-studio/render-test-tool/render-test-tool.vcxproj +++ b/build/visual-studio/render-test-tool/render-test-tool.vcxproj @@ -186,6 +186,7 @@ + @@ -197,6 +198,7 @@ + @@ -210,6 +212,9 @@ {222F7498-B40C-4F3F-A704-DDEB91A4484A} + + {F5ADB74E-02A7-44FB-AA3B-FC02F8AC7A4B} + {3565FE5E-4FA3-11EB-AE93-0242AC130002} diff --git a/build/visual-studio/render-test-tool/render-test-tool.vcxproj.filters b/build/visual-studio/render-test-tool/render-test-tool.vcxproj.filters index 1c0d82cdd..5223edb08 100644 --- a/build/visual-studio/render-test-tool/render-test-tool.vcxproj.filters +++ b/build/visual-studio/render-test-tool/render-test-tool.vcxproj.filters @@ -30,6 +30,9 @@ Header Files + + Header Files + Header Files @@ -59,6 +62,9 @@ Source Files + + Source Files + Source Files diff --git a/build/visual-studio/shader-toy/shader-toy.vcxproj b/build/visual-studio/shader-toy/shader-toy.vcxproj index 5c540113b..9fd7014a3 100644 --- a/build/visual-studio/shader-toy/shader-toy.vcxproj +++ b/build/visual-studio/shader-toy/shader-toy.vcxproj @@ -178,6 +178,9 @@ {222F7498-B40C-4F3F-A704-DDEB91A4484A} + + {F5ADB74E-02A7-44FB-AA3B-FC02F8AC7A4B} + {3565FE5E-4FA3-11EB-AE93-0242AC130002} diff --git a/examples/gpu-printing/main.cpp b/examples/gpu-printing/main.cpp index 03b20b969..57e096043 100644 --- a/examples/gpu-printing/main.cpp +++ b/examples/gpu-printing/main.cpp @@ -6,7 +6,6 @@ using Slang::ComPtr; #include "gfx/render.h" -#include "gfx/d3d11/render-d3d11.h" #include "tools/graphics-app-framework/window.h" #include "source/core/slang-basic.h" using namespace gfx; @@ -119,7 +118,7 @@ Result execute() windowDesc.height = gWindowHeight; gWindow = createWindow(windowDesc); - createD3D11Renderer(gRenderer.writeRef()); + gfxGetCreateFunc(gfx::RendererType::DirectX11)(gRenderer.writeRef()); IRenderer::Desc rendererDesc; rendererDesc.width = gWindowWidth; rendererDesc.height = gWindowHeight; diff --git a/examples/hello-world/main.cpp b/examples/hello-world/main.cpp index 77ca8a978..959aa2881 100644 --- a/examples/hello-world/main.cpp +++ b/examples/hello-world/main.cpp @@ -33,7 +33,6 @@ // design choices in their abstraction layer. // #include "gfx/render.h" -#include "gfx/d3d11/render-d3d11.h" #include "tools/graphics-app-framework/window.h" #include "slang-com-ptr.h" #include "source/core/slang-basic.h" @@ -261,7 +260,7 @@ Slang::Result initialize() // A future version of this example may support multiple // platforms/APIs. // - createD3D11Renderer(gRenderer.writeRef()); + gfxGetCreateFunc(gfx::RendererType::DirectX11)(gRenderer.writeRef()); IRenderer::Desc rendererDesc; rendererDesc.width = gWindowWidth; rendererDesc.height = gWindowHeight; diff --git a/examples/heterogeneous-hello-world/main.cpp b/examples/heterogeneous-hello-world/main.cpp index 0446ea1a7..8cf3894ed 100644 --- a/examples/heterogeneous-hello-world/main.cpp +++ b/examples/heterogeneous-hello-world/main.cpp @@ -34,7 +34,6 @@ // #include "slang-com-ptr.h" #include "gfx/render.h" -#include "gfx/d3d11/render-d3d11.h" #include "tools/graphics-app-framework/window.h" #include "../../prelude/slang-cpp-types.h" #include "source/core/slang-basic.h" @@ -124,7 +123,7 @@ gfx::IRenderer* createRenderer( // A future version of this example may support multiple // platforms/APIs. // - createD3D11Renderer(gRenderer.writeRef()); + gfxGetCreateFunc(gfx::RendererType::DirectX11)(gRenderer.writeRef()); IRenderer::Desc rendererDesc; rendererDesc.width = windowWidth; rendererDesc.height = windowHeight; diff --git a/examples/model-viewer/main.cpp b/examples/model-viewer/main.cpp index 387ec7293..720d421e2 100644 --- a/examples/model-viewer/main.cpp +++ b/examples/model-viewer/main.cpp @@ -18,7 +18,6 @@ // #include "graphics-app-framework/model.h" #include "gfx/render.h" -#include "gfx/d3d11/render-d3d11.h" #include "graphics-app-framework/vector-math.h" #include "graphics-app-framework/window.h" #include "graphics-app-framework/gui.h" @@ -2051,7 +2050,7 @@ Result initialize() windowDesc.userData = this; gWindow = createWindow(windowDesc); - createD3D11Renderer(gRenderer.writeRef()); + gfxGetCreateFunc(gfx::RendererType::DirectX11)(gRenderer.writeRef()); IRenderer::Desc rendererDesc; rendererDesc.width = gWindowWidth; rendererDesc.height = gWindowHeight; diff --git a/examples/shader-toy/main.cpp b/examples/shader-toy/main.cpp index e2efc325c..23193496b 100644 --- a/examples/shader-toy/main.cpp +++ b/examples/shader-toy/main.cpp @@ -20,7 +20,6 @@ using Slang::ComPtr; // compiler, and API. // #include "gfx/render.h" -#include "gfx/d3d11/render-d3d11.h" #include "tools/graphics-app-framework/window.h" #include "source/core/slang-basic.h" @@ -385,7 +384,7 @@ Result initialize() windowDesc.userData = this; gWindow = createWindow(windowDesc); - createD3D11Renderer(gRenderer.writeRef()); + gfxGetCreateFunc(gfx::RendererType::DirectX11)(gRenderer.writeRef()); IRenderer::Desc rendererDesc; rendererDesc.width = gWindowWidth; rendererDesc.height = gWindowHeight; diff --git a/premake5.lua b/premake5.lua index 36a7ead0a..211955ae1 100644 --- a/premake5.lua +++ b/premake5.lua @@ -525,7 +525,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", "graphics-app-framework" } + links { "slang", "core", "gfx", "gfx-util", "graphics-app-framework" } end -- @@ -698,7 +698,7 @@ toolSharedLibrary "render-test" uuid "61F7EB00-7281-4BF3-9470-7C2EA92620C3" includedirs { ".", "external", "source", "tools/gfx", "tools/graphics-app-framework" } - links { "core", "slang", "gfx", "graphics-app-framework" } + links { "core", "slang", "gfx", "gfx-util", "graphics-app-framework" } if isTargetWindows then addSourceDir "tools/render-test/windows" @@ -717,7 +717,7 @@ toolSharedLibrary "render-test" defines { "RENDER_TEST_CUDA" } includedirs { cudaPath .. "/include" } includedirs { cudaPath .. "/include", cudaPath .. "/common/inc" } - + links { "cuda", "cudart" } if optixPath then defines { "RENDER_TEST_OPTIX" } includedirs { optixPath .. "include/" } @@ -739,9 +739,12 @@ tool "gfx" uuid "222F7498-B40C-4F3F-A704-DDEB91A4484A" -- Unlike most of the code under `tools/`, this is a library -- rather than a stand-alone executable. - kind "StaticLib" + kind "SharedLib" + links { "core", "slang" } pic "On" - + + defines { "SLANG_GFX_DYNAMIC", "SLANG_GFX_DYNAMIC_EXPORT" } + includedirs { ".", "external", "source" } -- Will compile across targets @@ -809,6 +812,18 @@ tool "gfx" end -- +-- `gfx-util` is a static library containing utilities and helpers for using +-- the `gfx` library. +-- +tool "gfx-util" + uuid "F5ADB74E-02A7-44FB-AA3B-FC02F8AC7A4B" + kind "StaticLib" + pic "On" + + includedirs { ".", "source" } + + addSourceDir "tools/gfx-util" +-- -- `graphics-app-framework` contains all the utils for a simple graphics application. -- tool "graphics-app-framework" diff --git a/slang.sln b/slang.sln index c7cbb38bd..ae37105f0 100644 --- a/slang.sln +++ b/slang.sln @@ -47,6 +47,8 @@ Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "tools", "tools", "{FD47AE19 EndProject Project("{8BC9CEB8-8B4A-11D0-8D11-00A0C91BC942}") = "gfx", "build\visual-studio\gfx\gfx.vcxproj", "{222F7498-B40C-4F3F-A704-DDEB91A4484A}" EndProject +Project("{8BC9CEB8-8B4A-11D0-8D11-00A0C91BC942}") = "gfx-util", "build\visual-studio\gfx-util\gfx-util.vcxproj", "{F5ADB74E-02A7-44FB-AA3B-FC02F8AC7A4B}" +EndProject Project("{8BC9CEB8-8B4A-11D0-8D11-00A0C91BC942}") = "graphics-app-framework", "build\visual-studio\graphics-app-framework\graphics-app-framework.vcxproj", "{3565FE5E-4FA3-11EB-AE93-0242AC130002}" EndProject Project("{8BC9CEB8-8B4A-11D0-8D11-00A0C91BC942}") = "slang-cpp-extractor", "build\visual-studio\slang-cpp-extractor\slang-cpp-extractor.vcxproj", "{CA8A30D1-8FA9-4330-B7F7-84709246D8DC}" @@ -185,6 +187,14 @@ Global {222F7498-B40C-4F3F-A704-DDEB91A4484A}.Release|Win32.Build.0 = Release|Win32 {222F7498-B40C-4F3F-A704-DDEB91A4484A}.Release|x64.ActiveCfg = Release|x64 {222F7498-B40C-4F3F-A704-DDEB91A4484A}.Release|x64.Build.0 = Release|x64 + {F5ADB74E-02A7-44FB-AA3B-FC02F8AC7A4B}.Debug|Win32.ActiveCfg = Debug|Win32 + {F5ADB74E-02A7-44FB-AA3B-FC02F8AC7A4B}.Debug|Win32.Build.0 = Debug|Win32 + {F5ADB74E-02A7-44FB-AA3B-FC02F8AC7A4B}.Debug|x64.ActiveCfg = Debug|x64 + {F5ADB74E-02A7-44FB-AA3B-FC02F8AC7A4B}.Debug|x64.Build.0 = Debug|x64 + {F5ADB74E-02A7-44FB-AA3B-FC02F8AC7A4B}.Release|Win32.ActiveCfg = Release|Win32 + {F5ADB74E-02A7-44FB-AA3B-FC02F8AC7A4B}.Release|Win32.Build.0 = Release|Win32 + {F5ADB74E-02A7-44FB-AA3B-FC02F8AC7A4B}.Release|x64.ActiveCfg = Release|x64 + {F5ADB74E-02A7-44FB-AA3B-FC02F8AC7A4B}.Release|x64.Build.0 = Release|x64 {3565FE5E-4FA3-11EB-AE93-0242AC130002}.Debug|Win32.ActiveCfg = Debug|Win32 {3565FE5E-4FA3-11EB-AE93-0242AC130002}.Debug|Win32.Build.0 = Debug|Win32 {3565FE5E-4FA3-11EB-AE93-0242AC130002}.Debug|x64.ActiveCfg = Debug|x64 @@ -240,6 +250,7 @@ Global {61F7EB00-7281-4BF3-9470-7C2EA92620C3} = {57B5AA5E-C340-1823-CC51-9B17385C7423} {C5ACCA6E-C04D-4B36-8516-3752B3C13C2F} = {57B5AA5E-C340-1823-CC51-9B17385C7423} {222F7498-B40C-4F3F-A704-DDEB91A4484A} = {FD47AE19-69FD-260F-F2F1-20E65EA61D13} + {F5ADB74E-02A7-44FB-AA3B-FC02F8AC7A4B} = {FD47AE19-69FD-260F-F2F1-20E65EA61D13} {3565FE5E-4FA3-11EB-AE93-0242AC130002} = {FD47AE19-69FD-260F-F2F1-20E65EA61D13} {CA8A30D1-8FA9-4330-B7F7-84709246D8DC} = {FD47AE19-69FD-260F-F2F1-20E65EA61D13} {7F773DD9-EB8F-2403-B43C-B49C2014B99C} = {FD47AE19-69FD-260F-F2F1-20E65EA61D13} diff --git a/tools/gfx-util/shader-cursor.cpp b/tools/gfx-util/shader-cursor.cpp new file mode 100644 index 000000000..65cf2f8ac --- /dev/null +++ b/tools/gfx-util/shader-cursor.cpp @@ -0,0 +1,249 @@ +#include "shader-cursor.h" + +namespace gfx +{ + +Result gfx::ShaderCursor::getDereferenced(ShaderCursor& outCursor) const +{ + switch (m_typeLayout->getKind()) + { + default: + return SLANG_E_INVALID_ARG; + + case slang::TypeReflection::Kind::ConstantBuffer: + case slang::TypeReflection::Kind::ParameterBlock: + { + auto subObject = m_baseObject->getObject(m_offset); + outCursor = ShaderCursor(subObject); + return SLANG_OK; + } + } +} + +Result ShaderCursor::getField(Slang::UnownedStringSlice const& name, ShaderCursor& outCursor) +{ + // If this cursor is invalid, then can't possible fetch a field. + // + if (!isValid()) + return SLANG_E_INVALID_ARG; + + // If the cursor is valid, we want to consider the type of data + // it is referencing. + // + switch (m_typeLayout->getKind()) + { + // The easy/expected case is when the value has a structure type. + // + case slang::TypeReflection::Kind::Struct: + { + // We start by looking up the index of a field matching `name`. + // + // If there is no such field, we have an error. + // + SlangInt fieldIndex = m_typeLayout->findFieldIndexByName(name.begin(), name.end()); + if (fieldIndex == -1) + return SLANG_E_INVALID_ARG; + + // Once we know the index of the field being referenced, + // we create a cursor to point at the field, based on + // the offset information already in this cursor, plus + // offsets derived from the field's layout. + // + slang::VariableLayoutReflection* fieldLayout = + m_typeLayout->getFieldByIndex((unsigned int)fieldIndex); + ShaderCursor fieldCursor; + + // The field cursorwill point into the same parent object. + // + fieldCursor.m_baseObject = m_baseObject; + + // The type being pointed to is the tyep of the field. + // + fieldCursor.m_typeLayout = fieldLayout->getTypeLayout(); + + // The byte offset is the current offset plus the relative offset of the field. + // The offset in binding ranges is computed similarly. + // + fieldCursor.m_offset.uniformOffset = m_offset.uniformOffset + fieldLayout->getOffset(); + fieldCursor.m_offset.bindingRangeIndex = + m_offset.bindingRangeIndex + m_typeLayout->getFieldBindingRangeOffset(fieldIndex); + + // The index of the field within any binding ranges will be the same + // as the index computed for the parent structure. + // + // Note: this case would arise for an array of structures with texture-type + // fields. Suppose we have: + // + // struct S { Texture2D t; Texture2D u; } + // S g[4]; + // + // In this scenario, `g` holds two binding ranges: + // + // * Range #0 comprises 4 textures, representing `g[...].t` + // * Range #1 comprises 4 textures, representing `g[...].u` + // + // A cursor for `g[2]` would have a `bindingRangeIndex` of zero but + // a `bindingArrayIndex` of 2, iindicating that we could end up + // referencing either range, but no matter what we know the index + // is 2. Thus when we form a cursor for `g[2].u` we want to + // apply the binding range offset to get a `bindingRangeIndex` of + // 1, while the `bindingArrayIndex` is unmodified. + // + // The result is that `g[2].u` is stored in range #1 at array index 2. + // + fieldCursor.m_offset.bindingArrayIndex = m_offset.bindingArrayIndex; + + outCursor = fieldCursor; + return SLANG_OK; + } + break; + + // In some cases the user might be trying to acess a field by name + // from a cursor that references a constant buffer or parameter block, + // and in these cases we want the access to Just Work. + // + case slang::TypeReflection::Kind::ConstantBuffer: + case slang::TypeReflection::Kind::ParameterBlock: + { + // We basically need to "dereference" the current cursor + // to go from a pointer to a constant buffer to a pointer + // to the *contents* of the constant buffer. + // + ShaderCursor d = getDereferenced(); + return d.getField(name, outCursor); + } + break; + } + + return SLANG_E_INVALID_ARG; +} + +ShaderCursor ShaderCursor::getElement(Slang::Index index) +{ + // TODO: need to auto-dereference various buffer types... + + if (m_typeLayout->getKind() == slang::TypeReflection::Kind::Array) + { + ShaderCursor elementCursor; + elementCursor.m_baseObject = m_baseObject; + elementCursor.m_typeLayout = m_typeLayout->getElementTypeLayout(); + elementCursor.m_offset.uniformOffset = + m_offset.uniformOffset + + index * m_typeLayout->getElementStride(SLANG_PARAMETER_CATEGORY_UNIFORM); + elementCursor.m_offset.bindingRangeIndex = m_offset.bindingRangeIndex; + elementCursor.m_offset.bindingArrayIndex = + m_offset.bindingArrayIndex * m_typeLayout->getElementCount() + index; + return elementCursor; + } + + return ShaderCursor(); +} + + +static int _peek(Slang::UnownedStringSlice const& slice) +{ + const char* b = slice.begin(); + const char* e = slice.end(); + if (b == e) + return -1; + return *b; +} + +static int _get(Slang::UnownedStringSlice& slice) +{ + const char* b = slice.begin(); + const char* e = slice.end(); + if (b == e) + return -1; + auto result = *b++; + slice = Slang::UnownedStringSlice(b, e); + return result; +} + +Result ShaderCursor::followPath(Slang::UnownedStringSlice const& path, ShaderCursor& ioCursor) +{ + ShaderCursor cursor = ioCursor; + + enum + { + ALLOW_NAME = 0x1, + ALLOW_SUBSCRIPT = 0x2, + ALLOW_DOT = 0x4, + }; + int state = ALLOW_NAME | ALLOW_SUBSCRIPT; + + Slang::UnownedStringSlice rest = path; + for (;;) + { + int c = _peek(rest); + + if (c == -1) + break; + else if (c == '.') + { + if (!(state & ALLOW_DOT)) + return SLANG_E_INVALID_ARG; + + _get(rest); + state = ALLOW_NAME; + continue; + } + else if (c == '[') + { + if (!(state & ALLOW_SUBSCRIPT)) + return SLANG_E_INVALID_ARG; + + _get(rest); + Slang::Index index = 0; + while (_peek(rest) != ']') + { + int d = _get(rest); + if (d >= '0' && d <= '9') + { + index = index * 10 + (d - '0'); + } + else + { + return SLANG_E_INVALID_ARG; + } + } + + if (_peek(rest) != ']') + return SLANG_E_INVALID_ARG; + _get(rest); + + cursor = cursor.getElement(index); + state = ALLOW_DOT | ALLOW_SUBSCRIPT; + continue; + } + else + { + const char* nameBegin = rest.begin(); + for (;;) + { + switch (_peek(rest)) + { + default: + _get(rest); + continue; + + case -1: + case '.': + case '[': + break; + } + break; + } + char const* nameEnd = rest.begin(); + Slang::UnownedStringSlice name(nameBegin, nameEnd); + cursor = cursor.getField(name); + state = ALLOW_DOT | ALLOW_SUBSCRIPT; + continue; + } + } + + ioCursor = cursor; + return SLANG_OK; +} + +} // namespace gfx diff --git a/tools/gfx-util/shader-cursor.h b/tools/gfx-util/shader-cursor.h new file mode 100644 index 000000000..82794be9a --- /dev/null +++ b/tools/gfx-util/shader-cursor.h @@ -0,0 +1,123 @@ +#pragma once + +#include "tools/gfx/render.h" +#include "core/slang-basic.h" + +namespace gfx +{ + +/// Represents a "pointer" to the storage for a shader parameter of a (dynamically) known type. +/// +/// A `ShaderCursor` serves as a pointer-like type for things stored inside a `ShaderObject`. +/// +/// A cursor that points to the entire content of a shader object can be formed as +/// `ShaderCursor(someObject)`. A cursor pointing to a structure field or array element can be +/// formed from another cursor using `getField` or `getElement` respectively. +/// +/// Given a cursor pointing to a value of some "primitive" type, we can set or get the value +/// using operations like `setResource`, `getResource`, etc. +/// +/// Because type information for shader parameters is being reflected dynamically, all type +/// checking for shader cursors occurs at runtime, and errors may occur when attempting to +/// set a parameter using a value of an inappropriate type. As much as possible, `ShaderCursor` +/// attempts to protect against these cases and return an error `Result` or an invalid +/// cursor, rather than allowing operations to proceed with incorrect types. +/// +struct ShaderCursor +{ + IShaderObject* m_baseObject = nullptr; + slang::TypeLayoutReflection* m_typeLayout = nullptr; + ShaderOffset m_offset; + + /// Get the type (layout) of the value being pointed at by the cursor + slang::TypeLayoutReflection* getTypeLayout() const { return m_typeLayout; } + + /// Is this cursor valid (that is, does it seem to point to an actual location)? + /// + /// This check is equivalent to checking whether a pointer is null, so it is + /// a very weak sense of "valid." In particular, it is possible to form a + /// `ShaderCursor` for which `isValid()` is true, but attempting to get or + /// set the value would be an error (like dereferencing a garbage pointer). + /// + bool isValid() const { return m_baseObject != nullptr; } + + Result getDereferenced(ShaderCursor& outCursor) const; + + ShaderCursor getDereferenced() + { + ShaderCursor result; + getDereferenced(result); + return result; + } + + /// Form a cursor pointing to the field with the given `name` within the value this cursor + /// points at. + /// + /// If the operation succeeds, then the field cursor is written to `outCursor`. + Result getField(Slang::UnownedStringSlice const& name, ShaderCursor& outCursor); + + ShaderCursor getField(Slang::UnownedStringSlice const& name) + { + ShaderCursor cursor; + getField(name, cursor); + return cursor; + } + + ShaderCursor getField(Slang::String const& name) { return getField(name.getUnownedSlice()); } + + ShaderCursor getElement(Slang::Index index); + + static Result followPath(Slang::UnownedStringSlice const& path, ShaderCursor& ioCursor); + + static Result followPath(Slang::String const& path, ShaderCursor& ioCursor) + { + return followPath(path.getUnownedSlice(), ioCursor); + } + + ShaderCursor getPath(Slang::UnownedStringSlice const& path) + { + ShaderCursor result(*this); + followPath(path, result); + return result; + } + + ShaderCursor getPath(Slang::String const& path) + { + ShaderCursor result(*this); + followPath(path, result); + return result; + } + + ShaderCursor() {} + + ShaderCursor(IShaderObject* object) + : m_baseObject(object) + , m_typeLayout(object->getElementTypeLayout()) + {} + + SlangResult setData(void const* data, size_t size) + { + return m_baseObject->setData(m_offset, data, size); + } + + SlangResult setObject(IShaderObject* object) + { + return m_baseObject->setObject(m_offset, object); + } + + SlangResult setResource(IResourceView* resourceView) + { + return m_baseObject->setResource(m_offset, resourceView); + } + + SlangResult setSampler(ISamplerState* sampler) + { + return m_baseObject->setSampler(m_offset, sampler); + } + + SlangResult setCombinedTextureSampler(IResourceView* textureView, ISamplerState* sampler) + { + return m_baseObject->setCombinedTextureSampler(m_offset, textureView, sampler); + } +}; +} diff --git a/tools/gfx/cuda/render-cuda.cpp b/tools/gfx/cuda/render-cuda.cpp index bf316546e..0e4ee6c13 100644 --- a/tools/gfx/cuda/render-cuda.cpp +++ b/tools/gfx/cuda/render-cuda.cpp @@ -1493,10 +1493,14 @@ public: SLANG_UNUSED(outState); return SLANG_E_NOT_AVAILABLE; } - virtual SLANG_NO_THROW SlangResult SLANG_MCALL - captureScreenSurface(Surface& surfaceOut) override + virtual SLANG_NO_THROW SlangResult SLANG_MCALL captureScreenSurface( + void* buffer, size_t* inOutBufferSize, size_t* outRowPitch, size_t* outPixelSize) override { - SLANG_UNUSED(surfaceOut); + SLANG_UNUSED(buffer); + SLANG_UNUSED(inOutBufferSize); + SLANG_UNUSED(outRowPitch); + SLANG_UNUSED(outPixelSize); + return SLANG_E_NOT_AVAILABLE; } virtual SLANG_NO_THROW void SLANG_MCALL diff --git a/tools/gfx/d3d11/render-d3d11.cpp b/tools/gfx/d3d11/render-d3d11.cpp index 8bf11c39d..4ac90f47f 100644 --- a/tools/gfx/d3d11/render-d3d11.cpp +++ b/tools/gfx/d3d11/render-d3d11.cpp @@ -11,8 +11,6 @@ #include "../d3d/d3d-util.h" #include "../nvapi/nvapi-util.h" -#include "../surface.h" - // In order to use the Slang API, we need to include its header //#include @@ -106,8 +104,8 @@ public: virtual SLANG_NO_THROW Result SLANG_MCALL createComputePipelineState( const ComputePipelineStateDesc& desc, IPipelineState** outState) override; - virtual SLANG_NO_THROW SlangResult SLANG_MCALL - captureScreenSurface(Surface& surfaceOut) override; + virtual SLANG_NO_THROW SlangResult SLANG_MCALL captureScreenSurface( + void* buffer, size_t* inOutBufferSize, size_t* outRowPitch, size_t* outPixelSize) override; virtual SLANG_NO_THROW void* SLANG_MCALL map(IBufferResource* buffer, MapFlavor flavor) override; virtual SLANG_NO_THROW void SLANG_MCALL unmap(IBufferResource* buffer) override; @@ -491,7 +489,14 @@ public: }; /// Capture a texture to a file - static HRESULT captureTextureToSurface(ID3D11Device* device, ID3D11DeviceContext* context, ID3D11Texture2D* texture, Surface& surfaceOut); + static HRESULT captureTextureToSurface( + ID3D11Device* device, + ID3D11DeviceContext* context, + TextureResourceImpl* texture, + void* buffer, + size_t* inOutBufferSize, + size_t* outRowPitch, + size_t* outPixelSize); void _flushGraphicsState(); void _flushComputeState(); @@ -569,27 +574,50 @@ D3D11Renderer::ScopeNVAPI::~ScopeNVAPI() // !!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!D3D11Renderer !!!!!!!!!!!!!!!!!!!!!!!!!!!!! -/* static */HRESULT D3D11Renderer::captureTextureToSurface(ID3D11Device* device, ID3D11DeviceContext* context, ID3D11Texture2D* texture, Surface& surfaceOut) +/* static */ HRESULT D3D11Renderer::captureTextureToSurface( + ID3D11Device* device, + ID3D11DeviceContext* context, + TextureResourceImpl* texture, + void* buffer, + size_t* inOutBufferSize, + size_t* outRowPitch, + size_t* outPixelSize) { if (!context) return E_INVALIDARG; if (!texture) return E_INVALIDARG; - D3D11_TEXTURE2D_DESC textureDesc; - texture->GetDesc(&textureDesc); - // Don't bother supporting MSAA for right now - if (textureDesc.SampleDesc.Count > 1) + if (texture->getDesc()->sampleDesc.numSamples > 1) { fprintf(stderr, "ERROR: cannot capture multi-sample texture\n"); return E_INVALIDARG; } + size_t bytesPerPixel = sizeof(uint32_t); + size_t rowPitch = int(texture->getDesc()->size.width) * bytesPerPixel; + size_t bufferSize = rowPitch * int(texture->getDesc()->size.height); + if (outRowPitch) + *outRowPitch = rowPitch; + if (outPixelSize) + *outPixelSize = bytesPerPixel; + if (!buffer || *inOutBufferSize == 0) + { + *inOutBufferSize = bufferSize; + return S_OK; + } + if (*inOutBufferSize < bufferSize) + return SLANG_ERROR_INSUFFICIENT_BUFFER; + + D3D11_TEXTURE2D_DESC textureDesc; + auto d3d11Texture = ((ID3D11Texture2D*)texture->m_resource.get()); + d3d11Texture->GetDesc(&textureDesc); + HRESULT hr = S_OK; ComPtr stagingTexture; if (textureDesc.Usage == D3D11_USAGE_STAGING && (textureDesc.CPUAccessFlags & D3D11_CPU_ACCESS_READ)) { - stagingTexture = texture; + stagingTexture = d3d11Texture; } else { @@ -606,7 +634,7 @@ D3D11Renderer::ScopeNVAPI::~ScopeNVAPI() return hr; } - context->CopyResource(stagingTexture, texture); + context->CopyResource(stagingTexture, d3d11Texture); } // Now just read back texels from the staging textures @@ -614,11 +642,16 @@ D3D11Renderer::ScopeNVAPI::~ScopeNVAPI() D3D11_MAPPED_SUBRESOURCE mappedResource; SLANG_RETURN_ON_FAIL(context->Map(stagingTexture, 0, D3D11_MAP_READ, 0, &mappedResource)); - Result res = surfaceOut.set(textureDesc.Width, textureDesc.Height, Format::RGBA_Unorm_UInt8, mappedResource.RowPitch, mappedResource.pData, SurfaceAllocator::getMallocAllocator()); - + for (size_t y = 0; y < textureDesc.Height; y++) + { + memcpy( + (char*)buffer + y * (*outRowPitch), + (char*)mappedResource.pData + y * mappedResource.RowPitch, + *outRowPitch); + } // Make sure to unmap context->Unmap(stagingTexture, 0); - return res; + return SLANG_OK; } } @@ -891,9 +924,17 @@ TextureResource::Desc D3D11Renderer::getSwapChainTextureDesc() return desc; } -SlangResult D3D11Renderer::captureScreenSurface(Surface& surfaceOut) +SlangResult D3D11Renderer::captureScreenSurface( + void* buffer, size_t* inOutBufferSize, size_t* outRowPitch, size_t* outPixelSize) { - return captureTextureToSurface(m_device, m_immediateContext, (ID3D11Texture2D*) m_primaryRenderTargetTexture->m_resource.get(), surfaceOut); + return captureTextureToSurface( + m_device, + m_immediateContext, + m_primaryRenderTargetTexture.Ptr(), + buffer, + inOutBufferSize, + outRowPitch, + outPixelSize); } static D3D11_BIND_FLAG _calcResourceFlag(IResource::BindFlag::Enum bindFlag) diff --git a/tools/gfx/d3d12/render-d3d12.cpp b/tools/gfx/d3d12/render-d3d12.cpp index 6b66c5403..3db93df50 100644 --- a/tools/gfx/d3d12/render-d3d12.cpp +++ b/tools/gfx/d3d12/render-d3d12.cpp @@ -7,7 +7,6 @@ #include "../renderer-shared.h" #include "../render-graphics-common.h" -#include "../surface.h" #include "core/slang-basic.h" // In order to use the Slang API, we need to include its header @@ -112,8 +111,8 @@ public: virtual SLANG_NO_THROW Result SLANG_MCALL createComputePipelineState( const ComputePipelineStateDesc& desc, IPipelineState** outState) override; - virtual SLANG_NO_THROW SlangResult SLANG_MCALL - captureScreenSurface(Surface& surfaceOut) override; + virtual SLANG_NO_THROW SlangResult SLANG_MCALL captureScreenSurface( + void* buffer, size_t* inOutBufferSize, size_t* outRowPitch, size_t* outPixelSize) override; virtual SLANG_NO_THROW void* SLANG_MCALL map(IBufferResource* buffer, MapFlavor flavor) override; @@ -648,7 +647,12 @@ protected: void submitGpuWorkAndWait(); void _resetCommandList(); - Result captureTextureToSurface(D3D12Resource& resource, Surface& surfaceOut); + Result captureTextureToSurface( + D3D12Resource& resource, + void* buffer, + size_t* inOutBufferSize, + size_t* outRowPitch, + size_t* outPixelSize); FrameInfo& getFrame() { return m_frameInfos[m_frameIndex]; } const FrameInfo& getFrame() const { return m_frameInfos[m_frameIndex]; } @@ -1063,7 +1067,12 @@ void D3D12Renderer::submitGpuWorkAndWait() waitForGpu(); } -Result D3D12Renderer::captureTextureToSurface(D3D12Resource& resource, Surface& surfaceOut) +Result D3D12Renderer::captureTextureToSurface( + D3D12Resource& resource, + void* buffer, + size_t* inOutBufferSize, + size_t* outRowPitch, + size_t* outPixelSize) { const D3D12_RESOURCE_STATES initialState = resource.getState(); @@ -1079,7 +1088,17 @@ Result D3D12Renderer::captureTextureToSurface(D3D12Resource& resource, Surface& size_t bytesPerPixel = sizeof(uint32_t); size_t rowPitch = int(desc.Width) * bytesPerPixel; size_t bufferSize = rowPitch * int(desc.Height); - + if (outRowPitch) + *outRowPitch = rowPitch; + if (outPixelSize) + *outPixelSize = bytesPerPixel; + if (!buffer || *inOutBufferSize == 0) + { + *inOutBufferSize = bufferSize; + return SLANG_OK; + } + if (*inOutBufferSize < bufferSize) + return SLANG_ERROR_INSUFFICIENT_BUFFER; D3D12Resource stagingResource; { D3D12_RESOURCE_DESC stagingDesc; @@ -1136,10 +1155,10 @@ Result D3D12Renderer::captureTextureToSurface(D3D12Resource& resource, Surface& SLANG_RETURN_ON_FAIL(dxResource->Map(0, &readRange, reinterpret_cast(&data))); - Result res = surfaceOut.set(int(desc.Width), int(desc.Height), Format::RGBA_Unorm_UInt8, int(rowPitch), data, SurfaceAllocator::getMallocAllocator()); + memcpy(buffer, data, bufferSize); dxResource->Unmap(0, nullptr); - return res; + return SLANG_OK; } } @@ -1800,9 +1819,10 @@ TextureResource::Desc D3D12Renderer::getSwapChainTextureDesc() return desc; } -SlangResult D3D12Renderer::captureScreenSurface(Surface& surfaceOut) +SlangResult D3D12Renderer::captureScreenSurface( + void* buffer, size_t* inOutBufferSize, size_t* outRowPitch, size_t* outPixelSize) { - return captureTextureToSurface(*m_renderTargets[m_renderTargetIndex], surfaceOut); + return captureTextureToSurface(*m_renderTargets[m_renderTargetIndex], buffer, inOutBufferSize, outRowPitch, outPixelSize); } static D3D12_RESOURCE_STATES _calcResourceState(IResource::Usage usage) diff --git a/tools/gfx/open-gl/render-gl.cpp b/tools/gfx/open-gl/render-gl.cpp index 826192e8d..7ee73366b 100644 --- a/tools/gfx/open-gl/render-gl.cpp +++ b/tools/gfx/open-gl/render-gl.cpp @@ -13,8 +13,6 @@ #include "core/slang-secure-crt.h" #include "external/stb/stb_image_write.h" -#include "../surface.h" - // TODO(tfoley): eventually we should be able to run these // tests on non-Windows targets to confirm that cross-compilation // at least *works* on those platforms... @@ -125,8 +123,8 @@ public: virtual SLANG_NO_THROW Result SLANG_MCALL createComputePipelineState( const ComputePipelineStateDesc& desc, IPipelineState** outState) override; - virtual SLANG_NO_THROW SlangResult SLANG_MCALL - captureScreenSurface(Surface& surfaceOut) override; + virtual SLANG_NO_THROW SlangResult SLANG_MCALL captureScreenSurface( + void* buffer, size_t* inOutBufferSize, size_t* outRowPitch, size_t* outPixelSize) override; virtual SLANG_NO_THROW void* SLANG_MCALL map(IBufferResource* buffer, MapFlavor flavor) override; virtual SLANG_NO_THROW void SLANG_MCALL unmap(IBufferResource* buffer) override; @@ -882,11 +880,36 @@ SLANG_NO_THROW TextureResource::Desc SLANG_MCALL GLRenderer::getSwapChainTexture return desc; } -SLANG_NO_THROW Result SLANG_MCALL GLRenderer::captureScreenSurface(Surface& surfaceOut) +SLANG_NO_THROW Result SLANG_MCALL GLRenderer::captureScreenSurface( + void* buffer, size_t* inOutBufferSize, size_t* outRowPitch, size_t* outPixelSize) { - SLANG_RETURN_ON_FAIL(surfaceOut.allocate(m_desc.width, m_desc.height, Format::RGBA_Unorm_UInt8, 1, SurfaceAllocator::getMallocAllocator())); - glReadPixels(0, 0, m_desc.width, m_desc.height, GL_RGBA, GL_UNSIGNED_BYTE, surfaceOut.m_data); - surfaceOut.flipInplaceVertically(); + size_t requiredSize = m_desc.width * m_desc.height * sizeof(uint32_t); + if (outRowPitch) + *outRowPitch = m_desc.width * sizeof(uint32_t); + if (outPixelSize) + *outPixelSize = sizeof(uint32_t); + + if (!buffer || *inOutBufferSize == 0) + { + *inOutBufferSize = requiredSize; + return SLANG_OK; + } + if (*inOutBufferSize < requiredSize) + return SLANG_ERROR_INSUFFICIENT_BUFFER; + + glReadPixels(0, 0, m_desc.width, m_desc.height, GL_RGBA, GL_UNSIGNED_BYTE, buffer); + + // Flip pixels vertically in-place. + for (int y = 0; y < m_desc.height / 2; y++) + { + for (int x = 0; x < m_desc.width; x++) + { + std::swap( + *((uint32_t*)buffer + y * m_desc.width + x), + *((uint32_t*)buffer + (m_desc.height - y - 1) * m_desc.width + x)); + } + } + return SLANG_OK; } diff --git a/tools/gfx/render.h b/tools/gfx/render.h index 0bc8692eb..8e1a5d665 100644 --- a/tools/gfx/render.h +++ b/tools/gfx/render.h @@ -38,9 +38,6 @@ typedef SlangResult Result; typedef SlangInt Int; typedef SlangUInt UInt; -// pre declare types -class Surface; - // Declare opaque type class IInputLayout: public ISlangUnknown { @@ -1281,7 +1278,7 @@ public: } /// Captures the back buffer and stores the result in surfaceOut. If the surface contains data - it will either be overwritten (if same size and format), or freed and a re-allocated. - virtual SLANG_NO_THROW SlangResult SLANG_MCALL captureScreenSurface(Surface& surfaceOut) = 0; + virtual SLANG_NO_THROW SlangResult SLANG_MCALL captureScreenSurface(void* buffer, size_t *inOutBufferSize, size_t* outRowPitch, size_t* outPixelSize) = 0; virtual SLANG_NO_THROW void* SLANG_MCALL map(IBufferResource* buffer, MapFlavor flavor) = 0; virtual SLANG_NO_THROW void SLANG_MCALL unmap(IBufferResource* buffer) = 0; diff --git a/tools/gfx/shader-cursor.cpp b/tools/gfx/shader-cursor.cpp deleted file mode 100644 index 65cf2f8ac..000000000 --- a/tools/gfx/shader-cursor.cpp +++ /dev/null @@ -1,249 +0,0 @@ -#include "shader-cursor.h" - -namespace gfx -{ - -Result gfx::ShaderCursor::getDereferenced(ShaderCursor& outCursor) const -{ - switch (m_typeLayout->getKind()) - { - default: - return SLANG_E_INVALID_ARG; - - case slang::TypeReflection::Kind::ConstantBuffer: - case slang::TypeReflection::Kind::ParameterBlock: - { - auto subObject = m_baseObject->getObject(m_offset); - outCursor = ShaderCursor(subObject); - return SLANG_OK; - } - } -} - -Result ShaderCursor::getField(Slang::UnownedStringSlice const& name, ShaderCursor& outCursor) -{ - // If this cursor is invalid, then can't possible fetch a field. - // - if (!isValid()) - return SLANG_E_INVALID_ARG; - - // If the cursor is valid, we want to consider the type of data - // it is referencing. - // - switch (m_typeLayout->getKind()) - { - // The easy/expected case is when the value has a structure type. - // - case slang::TypeReflection::Kind::Struct: - { - // We start by looking up the index of a field matching `name`. - // - // If there is no such field, we have an error. - // - SlangInt fieldIndex = m_typeLayout->findFieldIndexByName(name.begin(), name.end()); - if (fieldIndex == -1) - return SLANG_E_INVALID_ARG; - - // Once we know the index of the field being referenced, - // we create a cursor to point at the field, based on - // the offset information already in this cursor, plus - // offsets derived from the field's layout. - // - slang::VariableLayoutReflection* fieldLayout = - m_typeLayout->getFieldByIndex((unsigned int)fieldIndex); - ShaderCursor fieldCursor; - - // The field cursorwill point into the same parent object. - // - fieldCursor.m_baseObject = m_baseObject; - - // The type being pointed to is the tyep of the field. - // - fieldCursor.m_typeLayout = fieldLayout->getTypeLayout(); - - // The byte offset is the current offset plus the relative offset of the field. - // The offset in binding ranges is computed similarly. - // - fieldCursor.m_offset.uniformOffset = m_offset.uniformOffset + fieldLayout->getOffset(); - fieldCursor.m_offset.bindingRangeIndex = - m_offset.bindingRangeIndex + m_typeLayout->getFieldBindingRangeOffset(fieldIndex); - - // The index of the field within any binding ranges will be the same - // as the index computed for the parent structure. - // - // Note: this case would arise for an array of structures with texture-type - // fields. Suppose we have: - // - // struct S { Texture2D t; Texture2D u; } - // S g[4]; - // - // In this scenario, `g` holds two binding ranges: - // - // * Range #0 comprises 4 textures, representing `g[...].t` - // * Range #1 comprises 4 textures, representing `g[...].u` - // - // A cursor for `g[2]` would have a `bindingRangeIndex` of zero but - // a `bindingArrayIndex` of 2, iindicating that we could end up - // referencing either range, but no matter what we know the index - // is 2. Thus when we form a cursor for `g[2].u` we want to - // apply the binding range offset to get a `bindingRangeIndex` of - // 1, while the `bindingArrayIndex` is unmodified. - // - // The result is that `g[2].u` is stored in range #1 at array index 2. - // - fieldCursor.m_offset.bindingArrayIndex = m_offset.bindingArrayIndex; - - outCursor = fieldCursor; - return SLANG_OK; - } - break; - - // In some cases the user might be trying to acess a field by name - // from a cursor that references a constant buffer or parameter block, - // and in these cases we want the access to Just Work. - // - case slang::TypeReflection::Kind::ConstantBuffer: - case slang::TypeReflection::Kind::ParameterBlock: - { - // We basically need to "dereference" the current cursor - // to go from a pointer to a constant buffer to a pointer - // to the *contents* of the constant buffer. - // - ShaderCursor d = getDereferenced(); - return d.getField(name, outCursor); - } - break; - } - - return SLANG_E_INVALID_ARG; -} - -ShaderCursor ShaderCursor::getElement(Slang::Index index) -{ - // TODO: need to auto-dereference various buffer types... - - if (m_typeLayout->getKind() == slang::TypeReflection::Kind::Array) - { - ShaderCursor elementCursor; - elementCursor.m_baseObject = m_baseObject; - elementCursor.m_typeLayout = m_typeLayout->getElementTypeLayout(); - elementCursor.m_offset.uniformOffset = - m_offset.uniformOffset + - index * m_typeLayout->getElementStride(SLANG_PARAMETER_CATEGORY_UNIFORM); - elementCursor.m_offset.bindingRangeIndex = m_offset.bindingRangeIndex; - elementCursor.m_offset.bindingArrayIndex = - m_offset.bindingArrayIndex * m_typeLayout->getElementCount() + index; - return elementCursor; - } - - return ShaderCursor(); -} - - -static int _peek(Slang::UnownedStringSlice const& slice) -{ - const char* b = slice.begin(); - const char* e = slice.end(); - if (b == e) - return -1; - return *b; -} - -static int _get(Slang::UnownedStringSlice& slice) -{ - const char* b = slice.begin(); - const char* e = slice.end(); - if (b == e) - return -1; - auto result = *b++; - slice = Slang::UnownedStringSlice(b, e); - return result; -} - -Result ShaderCursor::followPath(Slang::UnownedStringSlice const& path, ShaderCursor& ioCursor) -{ - ShaderCursor cursor = ioCursor; - - enum - { - ALLOW_NAME = 0x1, - ALLOW_SUBSCRIPT = 0x2, - ALLOW_DOT = 0x4, - }; - int state = ALLOW_NAME | ALLOW_SUBSCRIPT; - - Slang::UnownedStringSlice rest = path; - for (;;) - { - int c = _peek(rest); - - if (c == -1) - break; - else if (c == '.') - { - if (!(state & ALLOW_DOT)) - return SLANG_E_INVALID_ARG; - - _get(rest); - state = ALLOW_NAME; - continue; - } - else if (c == '[') - { - if (!(state & ALLOW_SUBSCRIPT)) - return SLANG_E_INVALID_ARG; - - _get(rest); - Slang::Index index = 0; - while (_peek(rest) != ']') - { - int d = _get(rest); - if (d >= '0' && d <= '9') - { - index = index * 10 + (d - '0'); - } - else - { - return SLANG_E_INVALID_ARG; - } - } - - if (_peek(rest) != ']') - return SLANG_E_INVALID_ARG; - _get(rest); - - cursor = cursor.getElement(index); - state = ALLOW_DOT | ALLOW_SUBSCRIPT; - continue; - } - else - { - const char* nameBegin = rest.begin(); - for (;;) - { - switch (_peek(rest)) - { - default: - _get(rest); - continue; - - case -1: - case '.': - case '[': - break; - } - break; - } - char const* nameEnd = rest.begin(); - Slang::UnownedStringSlice name(nameBegin, nameEnd); - cursor = cursor.getField(name); - state = ALLOW_DOT | ALLOW_SUBSCRIPT; - continue; - } - } - - ioCursor = cursor; - return SLANG_OK; -} - -} // namespace gfx diff --git a/tools/gfx/shader-cursor.h b/tools/gfx/shader-cursor.h deleted file mode 100644 index 82794be9a..000000000 --- a/tools/gfx/shader-cursor.h +++ /dev/null @@ -1,123 +0,0 @@ -#pragma once - -#include "tools/gfx/render.h" -#include "core/slang-basic.h" - -namespace gfx -{ - -/// Represents a "pointer" to the storage for a shader parameter of a (dynamically) known type. -/// -/// A `ShaderCursor` serves as a pointer-like type for things stored inside a `ShaderObject`. -/// -/// A cursor that points to the entire content of a shader object can be formed as -/// `ShaderCursor(someObject)`. A cursor pointing to a structure field or array element can be -/// formed from another cursor using `getField` or `getElement` respectively. -/// -/// Given a cursor pointing to a value of some "primitive" type, we can set or get the value -/// using operations like `setResource`, `getResource`, etc. -/// -/// Because type information for shader parameters is being reflected dynamically, all type -/// checking for shader cursors occurs at runtime, and errors may occur when attempting to -/// set a parameter using a value of an inappropriate type. As much as possible, `ShaderCursor` -/// attempts to protect against these cases and return an error `Result` or an invalid -/// cursor, rather than allowing operations to proceed with incorrect types. -/// -struct ShaderCursor -{ - IShaderObject* m_baseObject = nullptr; - slang::TypeLayoutReflection* m_typeLayout = nullptr; - ShaderOffset m_offset; - - /// Get the type (layout) of the value being pointed at by the cursor - slang::TypeLayoutReflection* getTypeLayout() const { return m_typeLayout; } - - /// Is this cursor valid (that is, does it seem to point to an actual location)? - /// - /// This check is equivalent to checking whether a pointer is null, so it is - /// a very weak sense of "valid." In particular, it is possible to form a - /// `ShaderCursor` for which `isValid()` is true, but attempting to get or - /// set the value would be an error (like dereferencing a garbage pointer). - /// - bool isValid() const { return m_baseObject != nullptr; } - - Result getDereferenced(ShaderCursor& outCursor) const; - - ShaderCursor getDereferenced() - { - ShaderCursor result; - getDereferenced(result); - return result; - } - - /// Form a cursor pointing to the field with the given `name` within the value this cursor - /// points at. - /// - /// If the operation succeeds, then the field cursor is written to `outCursor`. - Result getField(Slang::UnownedStringSlice const& name, ShaderCursor& outCursor); - - ShaderCursor getField(Slang::UnownedStringSlice const& name) - { - ShaderCursor cursor; - getField(name, cursor); - return cursor; - } - - ShaderCursor getField(Slang::String const& name) { return getField(name.getUnownedSlice()); } - - ShaderCursor getElement(Slang::Index index); - - static Result followPath(Slang::UnownedStringSlice const& path, ShaderCursor& ioCursor); - - static Result followPath(Slang::String const& path, ShaderCursor& ioCursor) - { - return followPath(path.getUnownedSlice(), ioCursor); - } - - ShaderCursor getPath(Slang::UnownedStringSlice const& path) - { - ShaderCursor result(*this); - followPath(path, result); - return result; - } - - ShaderCursor getPath(Slang::String const& path) - { - ShaderCursor result(*this); - followPath(path, result); - return result; - } - - ShaderCursor() {} - - ShaderCursor(IShaderObject* object) - : m_baseObject(object) - , m_typeLayout(object->getElementTypeLayout()) - {} - - SlangResult setData(void const* data, size_t size) - { - return m_baseObject->setData(m_offset, data, size); - } - - SlangResult setObject(IShaderObject* object) - { - return m_baseObject->setObject(m_offset, object); - } - - SlangResult setResource(IResourceView* resourceView) - { - return m_baseObject->setResource(m_offset, resourceView); - } - - SlangResult setSampler(ISamplerState* sampler) - { - return m_baseObject->setSampler(m_offset, sampler); - } - - SlangResult setCombinedTextureSampler(IResourceView* textureView, ISamplerState* sampler) - { - return m_baseObject->setCombinedTextureSampler(m_offset, textureView, sampler); - } -}; -} diff --git a/tools/gfx/slang-gfx-helper.cpp b/tools/gfx/slang-gfx-helper.cpp deleted file mode 100644 index 499e462b0..000000000 --- a/tools/gfx/slang-gfx-helper.cpp +++ /dev/null @@ -1,6 +0,0 @@ -#include "slang-gfx-helper.h" -#include "renderer-shared.h" - -namespace gfx -{ -} // namespace gfx diff --git a/tools/gfx/slang-gfx-helper.h b/tools/gfx/slang-gfx-helper.h deleted file mode 100644 index 1cd3fe64c..000000000 --- a/tools/gfx/slang-gfx-helper.h +++ /dev/null @@ -1,9 +0,0 @@ -#pragma once - -#include "render.h" - -namespace gfx -{ - - -} diff --git a/tools/gfx/surface.cpp b/tools/gfx/surface.cpp deleted file mode 100644 index 636881fca..000000000 --- a/tools/gfx/surface.cpp +++ /dev/null @@ -1,223 +0,0 @@ -// surface.cpp -#include "surface.h" - -#include -#include -#include - -#include "../../source/core/slang-list.h" - -namespace gfx { -using namespace Slang; - -class MallocSurfaceAllocator: public SurfaceAllocator -{ - public: - - virtual Slang::Result allocate(int width, int height, Format format, int alignment, Surface& surface) override; - virtual void deallocate(Surface& surface) override; -}; - -static MallocSurfaceAllocator s_mallocSurfaceAllocator; - -/// Get the malloc allocator -/* static */SurfaceAllocator* SurfaceAllocator::getMallocAllocator() -{ - return &s_mallocSurfaceAllocator; -} - -Slang::Result MallocSurfaceAllocator::allocate(int width, int height, Format format, int alignment, Surface& surface) -{ - assert(surface.m_data == nullptr); - - // Calculate row size - - const int rowSizeInBytes = Surface::calcRowSize(format, width); - const int numRows = Surface::calcNumRows(format, height); - - alignment = (alignment <= 0) ? int(sizeof(void*)) : alignment; - // It must be a power of 2 - assert( ((alignment - 1) & alignment) == 0); - - // Align rowSize - const int alignedRowSizeInBytes = (rowSizeInBytes + alignment - 1) & -alignment; - - size_t totalSize = numRows * alignedRowSizeInBytes; - - uint8_t* data = (uint8_t*)::malloc(totalSize); - if (!data) - { - return SLANG_E_OUT_OF_MEMORY; - } - - surface.m_data = data; - surface.m_width = width; - surface.m_height = height; - surface.m_format = format; - surface.m_numRows = numRows; - surface.m_rowStrideInBytes = alignedRowSizeInBytes; - - surface.m_allocator = this; - return SLANG_OK; -} - -void MallocSurfaceAllocator::deallocate(Surface& surface) -{ - assert(surface.m_data); - // Make sure it's not an inverted, cos otherwise m_data is not the start address - assert(surface.m_rowStrideInBytes > 0); - ::free(surface.m_data); -} - -// !!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!! Surface !!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!! - -/* static */int Surface::calcRowSize(Format format, int width) -{ - size_t pixelSize = gfxGetFormatSize(format); - if (pixelSize == 0) - { - return 0; - } - return int(pixelSize * width); -} - -/* static */int Surface::calcNumRows(Format format, int height) -{ - // Don't have any compressed types, so number of rows is same as the height - return height; -} - -void Surface::init() -{ - m_width = 0; - m_height = 0; - m_format = Format::Unknown; - m_data = nullptr; - m_numRows = 0; - m_rowStrideInBytes = 0; - // NOTE! does not clear the allocator. - // If called with an allocation memory will leak! -} - -Surface::~Surface() -{ - if (m_data && m_allocator) - { - m_allocator->deallocate(*this); - } -} - -void Surface::deallocate() -{ - if (m_data && m_allocator) - { - m_allocator->deallocate(*this); - init(); - } -} - -Result Surface::allocate(int width, int height, Format format, int alignment, SurfaceAllocator* allocator) -{ - deallocate(); - allocator = allocator ? allocator : m_allocator; - if (!allocator) - { - // An allocator needs to be set on the surface, or one passed in. - return SLANG_FAIL; - } - return allocator->allocate(width, height, format, alignment, *this); -} - -void Surface::setUnowned(int width, int height, Format format, int strideInBytes, void* data) -{ - deallocate(); - - // This is unowned - m_allocator = nullptr; - - m_width = width; - m_height = height; - m_format = format; - m_rowStrideInBytes = strideInBytes; - m_data = (uint8_t*)data; - - m_numRows = Surface::calcNumRows(format, height); - - const int rowSizeInBytes = Surface::calcRowSize(format, width); - assert((strideInBytes > 0 && rowSizeInBytes <= strideInBytes) || (strideInBytes < 0 && rowSizeInBytes <= -strideInBytes)); -} - -void Surface::zeroContents() -{ - const int rowSizeInBytes = Surface::calcRowSize(m_format, m_width); - - const int stride = m_rowStrideInBytes; - uint8_t* dst = m_data; - - for (int i = 0; i < m_numRows; i++, dst += stride) - { - ::memset(dst, 0, rowSizeInBytes); - } -} - -void Surface::flipInplaceVertically() -{ - // Can only flip when m_height matches number of rows - assert(m_numRows == m_height); - - const int rowSizeInBytes = Surface::calcRowSize(m_format, m_width); - if (rowSizeInBytes <= 0 || m_numRows <= 1) - { - return; - } - - uint8_t* top = m_data; - uint8_t* bottom = m_data + (m_numRows - 1) * m_rowStrideInBytes; - - List bufferList; - bufferList.setCount(rowSizeInBytes); - uint8_t* buffer = bufferList.getBuffer(); - - const int stride = m_rowStrideInBytes; - - const int num = m_height >> 1; - for (int i = 0; i < num; ++i, top += stride, bottom -= stride) - { - ::memcpy(buffer, top, rowSizeInBytes); - ::memcpy(top, bottom, rowSizeInBytes); - ::memcpy(bottom, buffer, rowSizeInBytes); - } -} - -SlangResult Surface::set(int width, int height, Format format, int srcRowStride, const void* data, SurfaceAllocator* allocator) -{ - if (hasContents() && m_width == width && m_height == height && m_format == format) - { - // I can just overwrite the contents that is there - } - else - { - SLANG_RETURN_ON_FAIL(allocate(width, height, format, 0, allocator)); - } - - // Okay just need to set the contents - - { - const size_t rowSize = calcRowSize(format, width); - - const uint8_t* srcRow = (const uint8_t*)data; - uint8_t* dstRow = (uint8_t*)m_data; - - for (int i = 0; i < m_numRows; i++) - { - ::memcpy(dstRow, srcRow, rowSize); - - srcRow += srcRowStride; - dstRow += m_rowStrideInBytes; - } - } - - return SLANG_OK; -} - -} // renderer_test diff --git a/tools/gfx/surface.h b/tools/gfx/surface.h deleted file mode 100644 index 3e0f6f0aa..000000000 --- a/tools/gfx/surface.h +++ /dev/null @@ -1,86 +0,0 @@ -// surface.h -#pragma once - -#include "render.h" - -namespace gfx { - -class Surface; - -class SurfaceAllocator -{ - public: - virtual Slang::Result allocate(int width, int height, Format format, int alignment, Surface& surface) = 0; - virtual void deallocate(Surface& surface) = 0; - - /// Get the malloc allocator - static SurfaceAllocator* getMallocAllocator(); -}; - -class Surface -{ - public: - - enum - { - kDefaultAlignment = sizeof(void*) - }; - - /// Allocate - Slang::Result allocate(int width, int height, Format format, int alignment = kDefaultAlignment, SurfaceAllocator* allocator = nullptr); - - /// Deallocate contents - void deallocate(); - /// Initialize contents (zero sized, no data). Note that the allocator pointer is left as is - void init(); - - /// Set unowned - void setUnowned(int width, int height, Format format, int strideInBytes, void* data); - - /// Set the contents - the memory will be owned by this surface (ie will be freed by the allocator when goes out of scope or is deallocated) - Slang::Result set(int width, int height, Format format, int strideInBytes, const void* data, SurfaceAllocator* allocator); - - template - T* calcNextRow(T* ptr) const { return (T*)calcNextRow((void*)ptr); } - template - const T* calcNextRow(const T* ptr) const { return (const T*)calcNextRow((const void*)ptr); } - - void* calcNextRow(void* ptr) const { return (void*)(((uint8_t*)ptr) + m_rowStrideInBytes); } - const void* calcNextRow(const void* ptr) const { return (const void*)(((const uint8_t*)ptr) + m_rowStrideInBytes); } - - /// Writes zero to all of the contents - void zeroContents(); - - /// Flips the contents vertically in place - void flipInplaceVertically(); - - /// True if has some contents - bool hasContents() const { return m_data != nullptr; } - - /// Ctor - Surface() : - m_allocator(nullptr) - { - init(); - } - /// Dtor - ~Surface(); - - /// Get the size of the row in bytes - static int calcRowSize(Format format, int width); - /// Calculates the number of rows - static int calcNumRows(Format format, int height); - - int m_width; - int m_height; - Format m_format; - - uint8_t* m_data; /// The data that makes up the image. If nullptr, has no data. Pointer to first 'row' of the image. - - int m_numRows; ///< Total amount of rows (typically same as height, but in compressed formats may be less) - int m_rowStrideInBytes; ///< The number of bytes between rows - - SurfaceAllocator* m_allocator; ///< Can be null if so contents is 'unowned', if set -}; - -} // renderer_test diff --git a/tools/gfx/vulkan/render-vk.cpp b/tools/gfx/vulkan/render-vk.cpp index c2a1e36bf..382f12f85 100644 --- a/tools/gfx/vulkan/render-vk.cpp +++ b/tools/gfx/vulkan/render-vk.cpp @@ -12,8 +12,6 @@ #include "vk-device-queue.h" #include "vk-swap-chain.h" -#include "../surface.h" - // Vulkan has a different coordinate system to ogl // http://anki3d.org/vulkan-coordinate-system/ @@ -88,7 +86,8 @@ public: const ComputePipelineStateDesc& desc, IPipelineState** outState) override; - virtual SLANG_NO_THROW SlangResult SLANG_MCALL captureScreenSurface(Surface& surface) override; + virtual SLANG_NO_THROW SlangResult SLANG_MCALL captureScreenSurface( + void* buffer, size_t* inOutBufferSize, size_t* outRowPitch, size_t* outPixelSize) override; virtual SLANG_NO_THROW void* SLANG_MCALL map(IBufferResource* buffer, MapFlavor flavor) override; virtual SLANG_NO_THROW void SLANG_MCALL unmap(IBufferResource* buffer) override; @@ -1439,8 +1438,13 @@ TextureResource::Desc VKRenderer::getSwapChainTextureDesc() return desc; } -SlangResult VKRenderer::captureScreenSurface(Surface& surfaceOut) +SlangResult VKRenderer::captureScreenSurface( + void* buffer, size_t* inOutBufferSize, size_t* outRowPitch, size_t* outPixelSize) { + SLANG_UNUSED(buffer); + SLANG_UNUSED(inOutBufferSize); + SLANG_UNUSED(outRowPitch); + SLANG_UNUSED(outPixelSize); return SLANG_FAIL; } @@ -1594,6 +1598,21 @@ void VKRenderer::_transitionImageLayout(VkImage image, VkFormat format, const Te m_api.vkCmdPipelineBarrier(commandBuffer, sourceStage, destinationStage, 0, 0, nullptr, 0, nullptr, 1, &barrier); } +size_t calcRowSize(Format format, int width) +{ + size_t pixelSize = gfxGetFormatSize(format); + if (pixelSize == 0) + { + return 0; + } + return size_t(pixelSize * width); +} + +size_t calcNumRows(Format format, int height) +{ + return (size_t)height; +} + Result VKRenderer::createTextureResource(IResource::Usage initialUsage, const ITextureResource::Desc& descIn, const ITextureResource::Data* initData, ITextureResource** outResource) { TextureResource::Desc desc(descIn); @@ -1704,8 +1723,8 @@ Result VKRenderer::createTextureResource(IResource::Usage initialUsage, const IT { const TextureResource::Size mipSize = desc.size.calcMipSize(j); - const int rowSizeInBytes = Surface::calcRowSize(desc.format, mipSize.width); - const int numRows = Surface::calcNumRows(desc.format, mipSize.height); + const int rowSizeInBytes = calcRowSize(desc.format, mipSize.width); + const int numRows = calcNumRows(desc.format, mipSize.height); mipSizes.add(mipSize); @@ -1735,8 +1754,8 @@ Result VKRenderer::createTextureResource(IResource::Usage initialUsage, const IT const auto& mipSize = mipSizes[j]; const ptrdiff_t srcRowStride = initData->mipRowStrides[j]; - const int dstRowSizeInBytes = Surface::calcRowSize(desc.format, mipSize.width); - const int numRows = Surface::calcNumRows(desc.format, mipSize.height); + const int dstRowSizeInBytes = calcRowSize(desc.format, mipSize.width); + const int numRows = calcNumRows(desc.format, mipSize.height); for (int k = 0; k < mipSize.depth; k++) { @@ -1768,8 +1787,8 @@ Result VKRenderer::createTextureResource(IResource::Usage initialUsage, const IT { const auto& mipSize = mipSizes[j]; - const int rowSizeInBytes = Surface::calcRowSize(desc.format, mipSize.width); - const int numRows = Surface::calcNumRows(desc.format, mipSize.height); + const int rowSizeInBytes = calcRowSize(desc.format, mipSize.width); + const int numRows = calcNumRows(desc.format, mipSize.height); // https://www.khronos.org/registry/vulkan/specs/1.1-extensions/man/html/VkBufferImageCopy.html // bufferRowLength and bufferImageHeight specify the data in buffer memory as a subregion of a larger two- or three-dimensional image, diff --git a/tools/render-test/render-test-main.cpp b/tools/render-test/render-test-main.cpp index 89bb25871..184373f0c 100644 --- a/tools/render-test/render-test-main.cpp +++ b/tools/render-test/render-test-main.cpp @@ -4,7 +4,7 @@ #include "options.h" #include "render.h" -#include "shader-cursor.h" +#include "tools/gfx-util/shader-cursor.h" #include "slang-support.h" #include "surface.h" #include "png-serialize-util.h" @@ -827,8 +827,23 @@ Result ShaderObjectRenderTestApp::writeBindingOutput(BindRoot* bindRoot, const c Result RenderTestApp::writeScreen(const char* filename) { + size_t rowPitch, bufferSize, pixelSize; + List buffer; + + SLANG_RETURN_ON_FAIL(m_renderer->captureScreenSurface(nullptr, &bufferSize, &rowPitch, &pixelSize)); + buffer.setCount(bufferSize); + SLANG_RETURN_ON_FAIL( + m_renderer->captureScreenSurface(buffer.getBuffer(), &bufferSize, &rowPitch, &pixelSize)); + Surface surface; - SLANG_RETURN_ON_FAIL(m_renderer->captureScreenSurface(surface)); + size_t width = rowPitch / pixelSize; + size_t height = bufferSize / rowPitch; + surface.setUnowned( + width, + height, + gfx::Format::RGBA_Unorm_UInt8, + rowPitch, + buffer.getBuffer()); return PngSerializeUtil::write(filename, surface); } diff --git a/tools/render-test/surface.cpp b/tools/render-test/surface.cpp new file mode 100644 index 000000000..636881fca --- /dev/null +++ b/tools/render-test/surface.cpp @@ -0,0 +1,223 @@ +// surface.cpp +#include "surface.h" + +#include +#include +#include + +#include "../../source/core/slang-list.h" + +namespace gfx { +using namespace Slang; + +class MallocSurfaceAllocator: public SurfaceAllocator +{ + public: + + virtual Slang::Result allocate(int width, int height, Format format, int alignment, Surface& surface) override; + virtual void deallocate(Surface& surface) override; +}; + +static MallocSurfaceAllocator s_mallocSurfaceAllocator; + +/// Get the malloc allocator +/* static */SurfaceAllocator* SurfaceAllocator::getMallocAllocator() +{ + return &s_mallocSurfaceAllocator; +} + +Slang::Result MallocSurfaceAllocator::allocate(int width, int height, Format format, int alignment, Surface& surface) +{ + assert(surface.m_data == nullptr); + + // Calculate row size + + const int rowSizeInBytes = Surface::calcRowSize(format, width); + const int numRows = Surface::calcNumRows(format, height); + + alignment = (alignment <= 0) ? int(sizeof(void*)) : alignment; + // It must be a power of 2 + assert( ((alignment - 1) & alignment) == 0); + + // Align rowSize + const int alignedRowSizeInBytes = (rowSizeInBytes + alignment - 1) & -alignment; + + size_t totalSize = numRows * alignedRowSizeInBytes; + + uint8_t* data = (uint8_t*)::malloc(totalSize); + if (!data) + { + return SLANG_E_OUT_OF_MEMORY; + } + + surface.m_data = data; + surface.m_width = width; + surface.m_height = height; + surface.m_format = format; + surface.m_numRows = numRows; + surface.m_rowStrideInBytes = alignedRowSizeInBytes; + + surface.m_allocator = this; + return SLANG_OK; +} + +void MallocSurfaceAllocator::deallocate(Surface& surface) +{ + assert(surface.m_data); + // Make sure it's not an inverted, cos otherwise m_data is not the start address + assert(surface.m_rowStrideInBytes > 0); + ::free(surface.m_data); +} + +// !!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!! Surface !!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!! + +/* static */int Surface::calcRowSize(Format format, int width) +{ + size_t pixelSize = gfxGetFormatSize(format); + if (pixelSize == 0) + { + return 0; + } + return int(pixelSize * width); +} + +/* static */int Surface::calcNumRows(Format format, int height) +{ + // Don't have any compressed types, so number of rows is same as the height + return height; +} + +void Surface::init() +{ + m_width = 0; + m_height = 0; + m_format = Format::Unknown; + m_data = nullptr; + m_numRows = 0; + m_rowStrideInBytes = 0; + // NOTE! does not clear the allocator. + // If called with an allocation memory will leak! +} + +Surface::~Surface() +{ + if (m_data && m_allocator) + { + m_allocator->deallocate(*this); + } +} + +void Surface::deallocate() +{ + if (m_data && m_allocator) + { + m_allocator->deallocate(*this); + init(); + } +} + +Result Surface::allocate(int width, int height, Format format, int alignment, SurfaceAllocator* allocator) +{ + deallocate(); + allocator = allocator ? allocator : m_allocator; + if (!allocator) + { + // An allocator needs to be set on the surface, or one passed in. + return SLANG_FAIL; + } + return allocator->allocate(width, height, format, alignment, *this); +} + +void Surface::setUnowned(int width, int height, Format format, int strideInBytes, void* data) +{ + deallocate(); + + // This is unowned + m_allocator = nullptr; + + m_width = width; + m_height = height; + m_format = format; + m_rowStrideInBytes = strideInBytes; + m_data = (uint8_t*)data; + + m_numRows = Surface::calcNumRows(format, height); + + const int rowSizeInBytes = Surface::calcRowSize(format, width); + assert((strideInBytes > 0 && rowSizeInBytes <= strideInBytes) || (strideInBytes < 0 && rowSizeInBytes <= -strideInBytes)); +} + +void Surface::zeroContents() +{ + const int rowSizeInBytes = Surface::calcRowSize(m_format, m_width); + + const int stride = m_rowStrideInBytes; + uint8_t* dst = m_data; + + for (int i = 0; i < m_numRows; i++, dst += stride) + { + ::memset(dst, 0, rowSizeInBytes); + } +} + +void Surface::flipInplaceVertically() +{ + // Can only flip when m_height matches number of rows + assert(m_numRows == m_height); + + const int rowSizeInBytes = Surface::calcRowSize(m_format, m_width); + if (rowSizeInBytes <= 0 || m_numRows <= 1) + { + return; + } + + uint8_t* top = m_data; + uint8_t* bottom = m_data + (m_numRows - 1) * m_rowStrideInBytes; + + List bufferList; + bufferList.setCount(rowSizeInBytes); + uint8_t* buffer = bufferList.getBuffer(); + + const int stride = m_rowStrideInBytes; + + const int num = m_height >> 1; + for (int i = 0; i < num; ++i, top += stride, bottom -= stride) + { + ::memcpy(buffer, top, rowSizeInBytes); + ::memcpy(top, bottom, rowSizeInBytes); + ::memcpy(bottom, buffer, rowSizeInBytes); + } +} + +SlangResult Surface::set(int width, int height, Format format, int srcRowStride, const void* data, SurfaceAllocator* allocator) +{ + if (hasContents() && m_width == width && m_height == height && m_format == format) + { + // I can just overwrite the contents that is there + } + else + { + SLANG_RETURN_ON_FAIL(allocate(width, height, format, 0, allocator)); + } + + // Okay just need to set the contents + + { + const size_t rowSize = calcRowSize(format, width); + + const uint8_t* srcRow = (const uint8_t*)data; + uint8_t* dstRow = (uint8_t*)m_data; + + for (int i = 0; i < m_numRows; i++) + { + ::memcpy(dstRow, srcRow, rowSize); + + srcRow += srcRowStride; + dstRow += m_rowStrideInBytes; + } + } + + return SLANG_OK; +} + +} // renderer_test diff --git a/tools/render-test/surface.h b/tools/render-test/surface.h new file mode 100644 index 000000000..3e0f6f0aa --- /dev/null +++ b/tools/render-test/surface.h @@ -0,0 +1,86 @@ +// surface.h +#pragma once + +#include "render.h" + +namespace gfx { + +class Surface; + +class SurfaceAllocator +{ + public: + virtual Slang::Result allocate(int width, int height, Format format, int alignment, Surface& surface) = 0; + virtual void deallocate(Surface& surface) = 0; + + /// Get the malloc allocator + static SurfaceAllocator* getMallocAllocator(); +}; + +class Surface +{ + public: + + enum + { + kDefaultAlignment = sizeof(void*) + }; + + /// Allocate + Slang::Result allocate(int width, int height, Format format, int alignment = kDefaultAlignment, SurfaceAllocator* allocator = nullptr); + + /// Deallocate contents + void deallocate(); + /// Initialize contents (zero sized, no data). Note that the allocator pointer is left as is + void init(); + + /// Set unowned + void setUnowned(int width, int height, Format format, int strideInBytes, void* data); + + /// Set the contents - the memory will be owned by this surface (ie will be freed by the allocator when goes out of scope or is deallocated) + Slang::Result set(int width, int height, Format format, int strideInBytes, const void* data, SurfaceAllocator* allocator); + + template + T* calcNextRow(T* ptr) const { return (T*)calcNextRow((void*)ptr); } + template + const T* calcNextRow(const T* ptr) const { return (const T*)calcNextRow((const void*)ptr); } + + void* calcNextRow(void* ptr) const { return (void*)(((uint8_t*)ptr) + m_rowStrideInBytes); } + const void* calcNextRow(const void* ptr) const { return (const void*)(((const uint8_t*)ptr) + m_rowStrideInBytes); } + + /// Writes zero to all of the contents + void zeroContents(); + + /// Flips the contents vertically in place + void flipInplaceVertically(); + + /// True if has some contents + bool hasContents() const { return m_data != nullptr; } + + /// Ctor + Surface() : + m_allocator(nullptr) + { + init(); + } + /// Dtor + ~Surface(); + + /// Get the size of the row in bytes + static int calcRowSize(Format format, int width); + /// Calculates the number of rows + static int calcNumRows(Format format, int height); + + int m_width; + int m_height; + Format m_format; + + uint8_t* m_data; /// The data that makes up the image. If nullptr, has no data. Pointer to first 'row' of the image. + + int m_numRows; ///< Total amount of rows (typically same as height, but in compressed formats may be less) + int m_rowStrideInBytes; ///< The number of bytes between rows + + SurfaceAllocator* m_allocator; ///< Can be null if so contents is 'unowned', if set +}; + +} // renderer_test -- cgit v1.2.3