diff options
Diffstat (limited to 'tools/render-test')
| -rw-r--r-- | tools/render-test/main.cpp | 53 | ||||
| -rw-r--r-- | tools/render-test/options.cpp | 28 | ||||
| -rw-r--r-- | tools/render-test/options.h | 23 | ||||
| -rw-r--r-- | tools/render-test/render-d3d11.cpp | 6 | ||||
| -rw-r--r-- | tools/render-test/render-d3d12.cpp | 337 | ||||
| -rw-r--r-- | tools/render-test/render-d3d12.h | 10 | ||||
| -rw-r--r-- | tools/render-test/render-gl.cpp | 6 | ||||
| -rw-r--r-- | tools/render-test/render-test.vcxproj | 18 | ||||
| -rw-r--r-- | tools/render-test/render-test.vcxproj.filters | 12 | ||||
| -rw-r--r-- | tools/render-test/render-vk.cpp | 1033 | ||||
| -rw-r--r-- | tools/render-test/render-vk.h | 10 | ||||
| -rw-r--r-- | tools/render-test/render.h | 9 | ||||
| -rw-r--r-- | tools/render-test/slang-support.cpp | 29 |
13 files changed, 1513 insertions, 61 deletions
diff --git a/tools/render-test/main.cpp b/tools/render-test/main.cpp index 51a96436f..3255f4b9b 100644 --- a/tools/render-test/main.cpp +++ b/tools/render-test/main.cpp @@ -4,6 +4,7 @@ #include "render.h" #include "render-d3d11.h" #include "render-gl.h" +#include "render-vk.h" #include "slang-support.h" #include "shader-input-layout.h" #include <stdio.h> @@ -98,7 +99,8 @@ Error initializeShaders( ShaderCompileRequest::SourceInfo sourceInfo; sourceInfo.path = sourcePath; - sourceInfo.text = sourceText; + sourceInfo.dataBegin = sourceText; + sourceInfo.dataEnd = sourceText + sourceSize; ShaderCompileRequest compileRequest; compileRequest.source = sourceInfo; @@ -322,18 +324,28 @@ int main( Renderer* renderer = nullptr; - switch( gOptions.mode ) + SlangSourceLanguage nativeLanguage = SLANG_SOURCE_LANGUAGE_UNKNOWN; + SlangCompileTarget slangTarget = SLANG_TARGET_NONE; + switch( gOptions.rendererID ) { - case Mode::Slang: - case Mode::HLSL: - case Mode::HLSLRewrite: + case RendererID::D3D11: renderer = createD3D11Renderer(); + slangTarget = SLANG_HLSL; + nativeLanguage = SLANG_SOURCE_LANGUAGE_HLSL; break; - case Mode::GLSL: - case Mode::GLSLRewrite: - case Mode::GLSLCrossCompile: + // TODO: `RendererID::D3D12` + + case RendererID::GL: renderer = createGLRenderer(); + slangTarget = SLANG_GLSL; + nativeLanguage = SLANG_SOURCE_LANGUAGE_GLSL; + break; + + case RendererID::VK: + renderer = createVKRenderer(); + slangTarget = SLANG_SPIRV; + nativeLanguage = SLANG_SOURCE_LANGUAGE_GLSL; break; default: @@ -345,22 +357,14 @@ int main( renderer->initialize(windowHandle); auto shaderCompiler = renderer->getShaderCompiler(); - switch( gOptions.mode ) + switch( gOptions.inputLanguageID ) { - case Mode::Slang: - shaderCompiler = createSlangShaderCompiler(shaderCompiler, SLANG_SOURCE_LANGUAGE_SLANG, SLANG_HLSL); - break; - - case Mode::HLSLRewrite: - shaderCompiler = createSlangShaderCompiler(shaderCompiler, SLANG_SOURCE_LANGUAGE_HLSL, SLANG_HLSL); - break; - - case Mode::GLSLRewrite: - shaderCompiler = createSlangShaderCompiler(shaderCompiler, SLANG_SOURCE_LANGUAGE_GLSL, SLANG_GLSL); + case InputLanguageID::Slang: + shaderCompiler = createSlangShaderCompiler(shaderCompiler, SLANG_SOURCE_LANGUAGE_SLANG, slangTarget); break; - case Mode::GLSLCrossCompile: - shaderCompiler = createSlangShaderCompiler(shaderCompiler, SLANG_SOURCE_LANGUAGE_SLANG, SLANG_GLSL); + case InputLanguageID::NativeRewrite: + shaderCompiler = createSlangShaderCompiler(shaderCompiler, nativeLanguage, slangTarget); break; default: @@ -397,9 +401,6 @@ int main( { // Whenver we don't have Windows events to process, // we render a frame. - static const float kClearColor[] = { 0.25, 0.25, 0.25, 1.0 }; - renderer->setClearColor(kClearColor); - renderer->clearFrame(); if (gOptions.shaderType == ShaderProgramType::Compute) { @@ -407,6 +408,10 @@ int main( } else { + static const float kClearColor[] = { 0.25, 0.25, 0.25, 1.0 }; + renderer->setClearColor(kClearColor); + renderer->clearFrame(); + renderFrameInner(renderer); } // If we are in a mode where output is requested, we need to snapshot the back buffer here diff --git a/tools/render-test/options.cpp b/tools/render-test/options.cpp index 629016155..45a94e137 100644 --- a/tools/render-test/options.cpp +++ b/tools/render-test/options.cpp @@ -54,27 +54,33 @@ void parseOptions(int* argc, char** argv) } else if( strcmp(arg, "-hlsl") == 0 ) { - gOptions.mode = Mode::HLSL; + gOptions.rendererID = RendererID::D3D11; + gOptions.inputLanguageID = InputLanguageID::Native; } else if( strcmp(arg, "-glsl") == 0 ) { - gOptions.mode = Mode::GLSL; + gOptions.rendererID = RendererID::GL; + gOptions.inputLanguageID = InputLanguageID::Native; } else if( strcmp(arg, "-hlsl-rewrite") == 0 ) { - gOptions.mode = Mode::HLSLRewrite; + gOptions.rendererID = RendererID::D3D11; + gOptions.inputLanguageID = InputLanguageID::NativeRewrite; } else if( strcmp(arg, "-glsl-rewrite") == 0 ) { - gOptions.mode = Mode::GLSLRewrite; + gOptions.rendererID = RendererID::GL; + gOptions.inputLanguageID = InputLanguageID::NativeRewrite; } else if( strcmp(arg, "-slang") == 0 ) { - gOptions.mode = Mode::Slang; + gOptions.rendererID = RendererID::D3D11; + gOptions.inputLanguageID = InputLanguageID::Slang; } else if( strcmp(arg, "-glsl-cross") == 0 ) { - gOptions.mode = Mode::GLSLCrossCompile; + gOptions.rendererID = RendererID::GL; + gOptions.inputLanguageID = InputLanguageID::Slang; } else if( strcmp(arg, "-xslang") == 0 ) { @@ -104,6 +110,16 @@ void parseOptions(int* argc, char** argv) { gOptions.shaderType = ShaderProgramType::GraphicsCompute; } + else if (strcmp(arg, "-vk") == 0 + || strcmp(arg, "-vulkan") == 0) + { + gOptions.rendererID = RendererID::VK; + } + else if (strcmp(arg, "-d3d12") == 0 + || strcmp(arg, "-dx12") == 0) + { + gOptions.rendererID = RendererID::D3D12; + } else { fprintf(stderr, "unknown option '%s'\n", arg); diff --git a/tools/render-test/options.h b/tools/render-test/options.h index 2c48c4ceb..b48295878 100644 --- a/tools/render-test/options.h +++ b/tools/render-test/options.h @@ -8,21 +8,24 @@ namespace renderer_test { typedef intptr_t Int; typedef uintptr_t UInt; -enum class Mode +enum class RendererID +{ + D3D11, + D3D12, + GL, + VK, +}; + +enum class InputLanguageID { // Slang being used as an HLSL-ish compiler Slang, // Raw HLSL or GLSL input, bypassing Slang - HLSL, - GLSL, + Native, // Raw HLSL or GLSL input, passed through the Slang rewriter - HLSLRewrite, - GLSLRewrite, - - // Slang/HLSL input -> GLSL output - GLSLCrossCompile, + NativeRewrite }; enum @@ -44,7 +47,9 @@ struct Options char const* sourcePath = nullptr; char const* outputPath = nullptr; ShaderProgramType shaderType = ShaderProgramType::Graphics; - Mode mode = Mode::Slang; + + RendererID rendererID; + InputLanguageID inputLanguageID = InputLanguageID::Slang; char const* slangArgs[kMaxSlangArgs]; int slangArgCount = 0; diff --git a/tools/render-test/render-d3d11.cpp b/tools/render-test/render-d3d11.cpp index 7d5c79b19..690e6299f 100644 --- a/tools/render-test/render-d3d11.cpp +++ b/tools/render-test/render-d3d11.cpp @@ -719,7 +719,7 @@ public: { if (request.computeShader.name) { - auto dxComputeShaderBlob = compileHLSLShader(request.computeShader.source.path, request.computeShader.source.text, request.computeShader.name, request.computeShader.profile); + auto dxComputeShaderBlob = compileHLSLShader(request.computeShader.source.path, request.computeShader.source.dataBegin, request.computeShader.name, request.computeShader.profile); if (!dxComputeShaderBlob) return nullptr; @@ -737,10 +737,10 @@ public: } else { - auto dxVertexShaderBlob = compileHLSLShader(request.vertexShader.source.path, request.vertexShader.source.text, request.vertexShader.name, request.vertexShader.profile); + auto dxVertexShaderBlob = compileHLSLShader(request.vertexShader.source.path, request.vertexShader.source.dataBegin, request.vertexShader.name, request.vertexShader.profile); if (!dxVertexShaderBlob) return nullptr; - auto dxFragmentShaderBlob = compileHLSLShader(request.fragmentShader.source.path, request.fragmentShader.source.text, request.fragmentShader.name, request.fragmentShader.profile); + auto dxFragmentShaderBlob = compileHLSLShader(request.fragmentShader.source.path, request.fragmentShader.source.dataBegin, request.fragmentShader.name, request.fragmentShader.profile); if (!dxFragmentShaderBlob) return nullptr; ID3D11VertexShader* dxVertexShader; diff --git a/tools/render-test/render-d3d12.cpp b/tools/render-test/render-d3d12.cpp new file mode 100644 index 000000000..2308c601a --- /dev/null +++ b/tools/render-test/render-d3d12.cpp @@ -0,0 +1,337 @@ +// render-d3d12.cpp +#include "render-d3d12.h" + +#include "options.h" +#include "render.h" + +// In order to use the Slang API, we need to include its header + +#include <slang.h> + +// We will be rendering with Direct3D 12, so we need to include +// the Windows and D3D12 headers + +#define WIN32_LEAN_AND_MEAN +#define NOMINMAX +#include <Windows.h> +#undef WIN32_LEAN_AND_MEAN +#undef NOMINMAX + +#include <dxgi1_4.h> +#include <d3d12.h> +#include <d3dcompiler.h> + +// We will use the C standard library just for printing error messages. +#include <stdio.h> + +#ifdef _MSC_VER +#include <stddef.h> +#if (_MSC_VER < 1900) +#define snprintf sprintf_s +#endif +#endif +// +using namespace Slang; + +#define ENABLE_DEBUG_LAYER 1 + +namespace renderer_test { + +// The Slang compiler currently generates HLSL source, so we'll need a utility +// routine (defined later) to translate that into D3D11 shader bytecode. +ID3DBlob* compileHLSLShader( + char const* sourcePath, + char const* source, + char const* entryPointName, + char const* dxProfileName); + +static char const* vertexProfileName = "vs_4_0"; +static char const* fragmentProfileName = "ps_4_0"; + +// + +class D3D12Renderer : public Renderer, public ShaderCompiler +{ +public: + IDXGISwapChain* dxSwapChain = NULL; + + ID3D12Device* dxDevice = NULL; + + virtual PROC loadProc( + HMODULE module, + char const* name) + { + PROC proc = GetProcAddress(module, name); + if( !proc ) + { + fprintf(stderr, + "error: failed load symbol '%s'\n", name); + exit(1); + } + return proc; + } + + void checkResult(HRESULT result) + { + assert(SUCCEEDED(result)); + } + + virtual void initialize(void* inWindowHandle) override + { + auto windowHandle = (HWND) inWindowHandle; + // Rather than statically link against D3D, we load it dynamically. + + HMODULE d3d12 = LoadLibraryA("d3d12.dll"); + if(!d3d12) + { + fprintf(stderr, "error: failed load 'd3d12.dll'\n"); + exit(1); + } + +#define LOAD_PROC(TYPE, NAME) \ + TYPE NAME##_ = (TYPE) loadProc(d3d12, #NAME) + + + UINT dxgiFactoryFlags = 0; + +#if ENABLE_DEBUG_LAYER + LOAD_PROC(PFN_D3D12_GET_DEBUG_INTERFACE, D3D12GetDebugInterface); + + ID3D12Debug* debugController; + if(SUCCEEDED(D3D12GetDebugInterface_(IID_PPV_ARGS(&debugController)))) + { + debugController->EnableDebugLayer(); + dxgiFactoryFlags |= DXGI_CREATE_FACTORY_DEBUG; + } +#endif + + typedef HRESULT (WINAPI *PFN_DXGI_CREATE_FACTORY_2)(UINT Flags, REFIID riid, _COM_Outptr_ void **ppFactory); + + + LOAD_PROC(PFN_DXGI_CREATE_FACTORY_2, CreateDXGIFactory2); + + IDXGIFactory4* dxgiFactory; + checkResult(CreateDXGIFactory2_(dxgiFactoryFlags, IID_PPV_ARGS(&dxgiFactory))); + + D3D_FEATURE_LEVEL featureLevel = D3D_FEATURE_LEVEL_11_0; + + // Search for an adapter that meets our requirements + IDXGIAdapter* adapter = nullptr; + + LOAD_PROC(PFN_D3D12_CREATE_DEVICE, D3D12CreateDevice); + + UINT adapterCounter = 0; + for(;;) + { + UINT adapterIndex = adapterCounter++; + IDXGIAdapter1* candidateAdapter = nullptr; + if(dxgiFactory->EnumAdapters1(adapterIndex, &candidateAdapter) == DXGI_ERROR_NOT_FOUND) + break; + + DXGI_ADAPTER_DESC1 desc; + candidateAdapter->GetDesc1(&desc); + + if( desc.Flags & DXGI_ADAPTER_FLAG_SOFTWARE ) + { + // TODO: may want to allow software driver as fallback + } + else if( SUCCEEDED(D3D12CreateDevice_( + candidateAdapter, featureLevel, IID_PPV_ARGS(&dxDevice))) ) + { + // We found one! + adapter = candidateAdapter; + break; + } + + candidateAdapter->Release(); + } + + if(!adapter) + { + return; + } + + // Command Queue + + D3D12_COMMAND_QUEUE_DESC queueDesc = {}; + queueDesc.Type = D3D12_COMMAND_LIST_TYPE_DIRECT; + + ID3D12CommandQueue* commandQueue; + checkResult(dxDevice->CreateCommandQueue(&queueDesc, IID_PPV_ARGS(&commandQueue))); + + // Swap Chain + + + UINT frameCount = 2; // TODO: configure + + DXGI_SWAP_CHAIN_DESC1 swapChainDesc = {}; + swapChainDesc.BufferCount = frameCount; + swapChainDesc.Width = gWindowWidth; + swapChainDesc.Height = gWindowHeight; + swapChainDesc.Format = DXGI_FORMAT_R8G8B8A8_UNORM; + swapChainDesc.BufferUsage = DXGI_USAGE_RENDER_TARGET_OUTPUT; + swapChainDesc.SwapEffect = DXGI_SWAP_EFFECT_DISCARD; + swapChainDesc.SampleDesc.Count = 1; + + IDXGISwapChain1* swapChain; + checkResult(dxgiFactory->CreateSwapChainForHwnd( + commandQueue, + windowHandle, + &swapChainDesc, + nullptr, + nullptr, + &swapChain)); + + // Is this needed? + dxgiFactory->MakeWindowAssociation( + windowHandle, DXGI_MWA_NO_ALT_ENTER); + + IDXGISwapChain3* swapChainEx; + swapChain->QueryInterface(IID_PPV_ARGS(&swapChainEx)); + + UINT frameIndex = swapChainEx->GetCurrentBackBufferIndex(); + + // Descriptor heaps + + D3D12_DESCRIPTOR_HEAP_DESC rtvHeapDesc = {}; + rtvHeapDesc.NumDescriptors = frameCount; + rtvHeapDesc.Type = D3D12_DESCRIPTOR_HEAP_TYPE_RTV; + + ID3D12DescriptorHeap* rtvHeap; + checkResult(dxDevice->CreateDescriptorHeap(&rtvHeapDesc, IID_PPV_ARGS(&rtvHeap))); + + UINT rtvDescriptorSize = dxDevice->GetDescriptorHandleIncrementSize( + D3D12_DESCRIPTOR_HEAP_TYPE_RTV); + + D3D12_CPU_DESCRIPTOR_HANDLE rtvHandle = rtvHeap->GetCPUDescriptorHandleForHeapStart(); + + // Create per-frame RTVs + ID3D12Resource* backBufferResources[2]; + for( UINT ff = 0; ff < frameCount; ++ff ) + { + checkResult(swapChainEx->GetBuffer(ff, IID_PPV_ARGS(&backBufferResources[ff]))); + dxDevice->CreateRenderTargetView( + backBufferResources[ff], + nullptr, + rtvHandle); + rtvHandle.ptr += rtvDescriptorSize; + } + + ID3D12CommandAllocator* commandAllocator; + checkResult(dxDevice->CreateCommandAllocator( + D3D12_COMMAND_LIST_TYPE_DIRECT, + IID_PPV_ARGS(&commandAllocator))); + } + + float clearColor[4] = { 0, 0, 0, 0 }; + virtual void setClearColor(float const* color) override + { + memcpy(clearColor, color, sizeof(clearColor)); + } + + virtual void clearFrame() override + { + } + + virtual void presentFrame() override + { + } + + virtual void captureScreenShot(char const* outputPath) override + { + } + + virtual ShaderCompiler* getShaderCompiler() override + { + return this; + } + + virtual Buffer* createBuffer(BufferDesc const& desc) override + { + return nullptr; + } + + static DXGI_FORMAT mapFormat(Format format) + { + switch( format ) + { + case Format::RGB_Float32: + return DXGI_FORMAT_R32G32B32_FLOAT; + case Format::RG_Float32: + return DXGI_FORMAT_R32G32_FLOAT; + default: + return DXGI_FORMAT_UNKNOWN; + } + } + + virtual InputLayout* createInputLayout(InputElementDesc const* inputElements, UInt inputElementCount) override + { + return nullptr; + } + + virtual void* map(Buffer* buffer, MapFlavor flavor) override + { + return nullptr; + } + + virtual void unmap(Buffer* buffer) override + { + } + + virtual void setInputLayout(InputLayout* inputLayout) override + { + } + + virtual void setPrimitiveTopology(PrimitiveTopology topology) override + { + } + + virtual void setVertexBuffers(UInt startSlot, UInt slotCount, Buffer* const* buffers, UInt const* strides, UInt const* offsets) override + { + } + + virtual void setShaderProgram(ShaderProgram* inProgram) override + { + } + + virtual void setConstantBuffers(UInt startSlot, UInt slotCount, Buffer* const* buffers, UInt const* offsets) override + { + } + + virtual void draw(UInt vertexCount, UInt startVertex) override + { + } + + + virtual void dispatchCompute(int x, int y, int z) override + { + } + + virtual BindingState * createBindingState(const ShaderInputLayout & layout) + { + return nullptr; + } + + virtual void setBindingState(BindingState * state) + { + } + + virtual void serializeOutput(BindingState* state, const char * fileName) + { + } + + // ShaderCompiler interface + + virtual ShaderProgram* compileProgram(ShaderCompileRequest const& request) override + { + return nullptr; + } + +}; + +Renderer* createD3D12Renderer() +{ + return new D3D12Renderer(); +} + +} // renderer_test diff --git a/tools/render-test/render-d3d12.h b/tools/render-test/render-d3d12.h new file mode 100644 index 000000000..259af7f7b --- /dev/null +++ b/tools/render-test/render-d3d12.h @@ -0,0 +1,10 @@ +// render-d3d12.h +#pragma once + +namespace renderer_test { + +class Renderer; + +Renderer* createD3D12Renderer(); + +} // renderer_test diff --git a/tools/render-test/render-gl.cpp b/tools/render-test/render-gl.cpp index f07e27985..99b61c03c 100644 --- a/tools/render-test/render-gl.cpp +++ b/tools/render-test/render-gl.cpp @@ -453,7 +453,7 @@ public: auto programID = glCreateProgram(); if (request.computeShader.name) { - auto computeShaderID = loadShader(GL_COMPUTE_SHADER, request.computeShader.source.text); + auto computeShaderID = loadShader(GL_COMPUTE_SHADER, request.computeShader.source.dataBegin); glAttachShader(programID, computeShaderID); @@ -464,8 +464,8 @@ public: } else { - auto vertexShaderID = loadShader(GL_VERTEX_SHADER, request.vertexShader.source.text); - auto fragmentShaderID = loadShader(GL_FRAGMENT_SHADER, request.fragmentShader.source.text); + auto vertexShaderID = loadShader(GL_VERTEX_SHADER, request.vertexShader.source.dataBegin); + auto fragmentShaderID = loadShader(GL_FRAGMENT_SHADER, request.fragmentShader.source.dataBegin); glAttachShader(programID, vertexShaderID); glAttachShader(programID, fragmentShaderID); diff --git a/tools/render-test/render-test.vcxproj b/tools/render-test/render-test.vcxproj index a175d3973..1b022f913 100644 --- a/tools/render-test/render-test.vcxproj +++ b/tools/render-test/render-test.vcxproj @@ -22,7 +22,7 @@ <ProjectGuid>{96610759-07B9-4EEB-A974-5C634A2E742B}</ProjectGuid> <Keyword>Win32Proj</Keyword> <RootNamespace>rendertest</RootNamespace> - <WindowsTargetPlatformVersion>8.1</WindowsTargetPlatformVersion> + <WindowsTargetPlatformVersion>10.0.14393.0</WindowsTargetPlatformVersion> </PropertyGroup> <Import Project="$(VCTargetsPath)\Microsoft.Cpp.Default.props" /> <PropertyGroup Condition="'$(Configuration)|$(Platform)'=='Debug|Win32'" Label="Configuration"> @@ -75,19 +75,23 @@ <PropertyGroup Label="UserMacros" /> <PropertyGroup Condition="'$(Configuration)|$(Platform)'=='Debug|Win32'"> <LinkIncremental>true</LinkIncremental> - <IncludePath>$(SolutionDir);$(IncludePath)</IncludePath> + <IncludePath>$(SolutionDir)external\;$(SolutionDir);$(IncludePath)</IncludePath> + <LibraryPath>$(SolutionDir)external\vulkan\lib\windows-$(PlatformShortName)\;$(LibraryPath)</LibraryPath> </PropertyGroup> <PropertyGroup Condition="'$(Configuration)|$(Platform)'=='Debug|x64'"> <LinkIncremental>true</LinkIncremental> - <IncludePath>$(SolutionDir);$(IncludePath)</IncludePath> + <IncludePath>$(SolutionDir)external\;$(SolutionDir);$(IncludePath)</IncludePath> + <LibraryPath>$(SolutionDir)external\vulkan\lib\windows-$(PlatformShortName)\;$(LibraryPath)</LibraryPath> </PropertyGroup> <PropertyGroup Condition="'$(Configuration)|$(Platform)'=='Release|Win32'"> <LinkIncremental>false</LinkIncremental> - <IncludePath>$(SolutionDir);$(IncludePath)</IncludePath> + <IncludePath>$(SolutionDir)external\;$(SolutionDir);$(IncludePath)</IncludePath> + <LibraryPath>$(SolutionDir)external\vulkan\lib\windows-$(PlatformShortName)\;$(LibraryPath)</LibraryPath> </PropertyGroup> <PropertyGroup Condition="'$(Configuration)|$(Platform)'=='Release|x64'"> <LinkIncremental>false</LinkIncremental> - <IncludePath>$(SolutionDir);$(IncludePath)</IncludePath> + <IncludePath>$(SolutionDir)external\;$(SolutionDir);$(IncludePath)</IncludePath> + <LibraryPath>$(SolutionDir)external\vulkan\lib\windows-$(PlatformShortName)\;$(LibraryPath)</LibraryPath> </PropertyGroup> <ItemDefinitionGroup Condition="'$(Configuration)|$(Platform)'=='Debug|Win32'"> <ClCompile> @@ -165,14 +169,18 @@ <ClCompile Include="main.cpp" /> <ClCompile Include="options.cpp" /> <ClCompile Include="render-d3d11.cpp" /> + <ClCompile Include="render-d3d12.cpp" /> <ClCompile Include="render-gl.cpp" /> + <ClCompile Include="render-vk.cpp" /> <ClCompile Include="shader-input-layout.cpp" /> <ClCompile Include="slang-support.cpp" /> </ItemGroup> <ItemGroup> <ClInclude Include="options.h" /> <ClInclude Include="render-d3d11.h" /> + <ClInclude Include="render-d3d12.h" /> <ClInclude Include="render-gl.h" /> + <ClInclude Include="render-vk.h" /> <ClInclude Include="render.h" /> <ClInclude Include="shader-input-layout.h" /> <ClInclude Include="slang-support.h" /> diff --git a/tools/render-test/render-test.vcxproj.filters b/tools/render-test/render-test.vcxproj.filters index 985e24b8b..e1077e4e8 100644 --- a/tools/render-test/render-test.vcxproj.filters +++ b/tools/render-test/render-test.vcxproj.filters @@ -33,6 +33,12 @@ <ClCompile Include="shader-input-layout.cpp"> <Filter>Source Files</Filter> </ClCompile> + <ClCompile Include="render-vk.cpp"> + <Filter>Source Files</Filter> + </ClCompile> + <ClCompile Include="render-d3d12.cpp"> + <Filter>Source Files</Filter> + </ClCompile> </ItemGroup> <ItemGroup> <ClInclude Include="options.h"> @@ -56,5 +62,11 @@ <ClInclude Include="shader-input-layout.h"> <Filter>Header Files</Filter> </ClInclude> + <ClInclude Include="render-vk.h"> + <Filter>Header Files</Filter> + </ClInclude> + <ClInclude Include="render-d3d12.h"> + <Filter>Header Files</Filter> + </ClInclude> </ItemGroup> </Project>
\ No newline at end of file diff --git a/tools/render-test/render-vk.cpp b/tools/render-test/render-vk.cpp new file mode 100644 index 000000000..fd080931d --- /dev/null +++ b/tools/render-test/render-vk.cpp @@ -0,0 +1,1033 @@ +// render-vk.cpp +#include "render-vk.h" + +#include "options.h" +#include "render.h" + +#ifdef _WIN32 +#define VK_USE_PLATFORM_WIN32_KHR 1 +#endif + +#define VK_NO_PROTOTYPES +#include <vulkan/vulkan.h> + +#define ENABLE_VALIDATION_LAYER 1 + + +#ifdef _MSC_VER + +#include <stddef.h> + +#pragma warning(disable: 4996) + +#if (_MSC_VER < 1900) +#define snprintf sprintf_s +#endif +#endif + +#define FOREACH_GLOBAL_PROC(M) \ + M(vkCreateInstance) \ + /* */ + +#define FOREACH_INSTANCE_PROC(M) \ + M(vkCreateDevice) \ + M(vkCreateDebugReportCallbackEXT) \ + M(vkDestroyDebugReportCallbackEXT) \ + M(vkDebugReportMessageEXT) \ + M(vkEnumeratePhysicalDevices) \ + M(vkGetPhysicalDeviceProperties) \ + M(vkGetPhysicalDeviceFeatures) \ + M(vkGetPhysicalDeviceMemoryProperties) \ + M(vkGetPhysicalDeviceQueueFamilyProperties) \ + M(vkGetDeviceProcAddr) \ + /* */ + +#define FOREACH_DEVICE_PROC(M) \ + M(vkCreateDescriptorPool) \ + M(vkCreateCommandPool) \ + M(vkGetDeviceQueue) \ + M(vkAllocateCommandBuffers) \ + M(vkBeginCommandBuffer) \ + M(vkEndCommandBuffer) \ + M(vkQueueSubmit) \ + M(vkQueueWaitIdle) \ + M(vkFreeCommandBuffers) \ + M(vkCreateBuffer) \ + M(vkGetBufferMemoryRequirements) \ + M(vkAllocateMemory) \ + M(vkBindBufferMemory) \ + M(vkMapMemory) \ + M(vkUnmapMemory) \ + M(vkCmdCopyBuffer) \ + M(vkDestroyBuffer) \ + M(vkFreeMemory) \ + M(vkCreateDescriptorSetLayout) \ + M(vkAllocateDescriptorSets) \ + M(vkUpdateDescriptorSets) \ + M(vkCreatePipelineLayout) \ + M(vkCreateComputePipelines) \ + M(vkCmdBindPipeline) \ + M(vkCmdBindDescriptorSets) \ + M(vkCmdDispatch) \ + M(vkDestroyPipeline) \ + M(vkCreateShaderModule) \ + /* */ + +namespace renderer_test { + +class VKRenderer : public Renderer, public ShaderCompiler +{ +public: + + VkInstance instance; + VkPhysicalDevice physicalDevice; + VkPhysicalDeviceProperties deviceProperties; + VkPhysicalDeviceFeatures deviceFeatures; + VkPhysicalDeviceMemoryProperties deviceMemoryProperties; + VkPhysicalDeviceFeatures enabledFeatures; + VkDevice device; + VkQueue queue; + VkCommandPool commandPool; + VkSubmitInfo submitInfo; + VkDebugReportCallbackEXT debugReportCallback; + + +#define DECLARE_PROC(NAME) PFN_##NAME NAME; + DECLARE_PROC(vkGetInstanceProcAddr); + FOREACH_GLOBAL_PROC(DECLARE_PROC) + FOREACH_INSTANCE_PROC(DECLARE_PROC) + FOREACH_DEVICE_PROC(DECLARE_PROC) +#undef DECLARE_PROC + + // Renderer interface + + void checkResult(VkResult result) + { + assert(result == VK_SUCCESS); + } + + VkBool32 handleDebugMessage( + VkDebugReportFlagsEXT flags, + VkDebugReportObjectTypeEXT objType, + uint64_t srcObject, + size_t location, + int32_t msgCode, + const char* pLayerPrefix, + const char* pMsg) + { + char const* severity = "message"; + if(flags & VK_DEBUG_REPORT_WARNING_BIT_EXT) + severity = "warning"; + if(flags & VK_DEBUG_REPORT_ERROR_BIT_EXT) + severity = "error"; + + char buffer[1024]; + sprintf_s(buffer, + "%s: %s %d: %s\n", + pLayerPrefix, + severity, + msgCode, + pMsg); + + fprintf(stderr, "%s", buffer); + fflush(stderr); + + OutputDebugStringA(buffer); + + return VK_FALSE; + } + + static VKAPI_ATTR VkBool32 VKAPI_CALL debugMessageCallback( + VkDebugReportFlagsEXT flags, + VkDebugReportObjectTypeEXT objType, + uint64_t srcObject, + size_t location, + int32_t msgCode, + const char* pLayerPrefix, + const char* pMsg, + void* pUserData) + { + return ((VKRenderer*) pUserData)->handleDebugMessage( + flags, objType, srcObject, location, msgCode, pLayerPrefix, pMsg); + } + + virtual void initialize(void* inWindowHandle) override + { + char const* dynamicLibraryName = "vulkan-1.dll"; + HMODULE vulkan = LoadLibraryA(dynamicLibraryName); + if(!vulkan) + { + fprintf(stderr, "error: failed load '%s'\n", dynamicLibraryName); + exit(1); + } + + vkGetInstanceProcAddr = (PFN_vkGetInstanceProcAddr) GetProcAddress(vulkan, "vkGetInstanceProcAddr"); + if(!vkGetInstanceProcAddr) + { + fprintf(stderr, + "error: failed load symbol 'vkGetInstanceProcAddr'\n"); + exit(1); + } + + VkApplicationInfo applicationInfo = { VK_STRUCTURE_TYPE_APPLICATION_INFO }; + applicationInfo.pApplicationName = "slang-render-test"; + applicationInfo.pEngineName = "slang-render-test"; + applicationInfo.apiVersion = VK_API_VERSION_1_0; + + char const* instanceExtensions[] = { + VK_KHR_SURFACE_EXTENSION_NAME, +#ifdef _WIN32 + VK_KHR_WIN32_SURFACE_EXTENSION_NAME, +#else +#endif + +#if ENABLE_VALIDATION_LAYER + VK_EXT_DEBUG_REPORT_EXTENSION_NAME, +#endif + }; + + VkInstanceCreateInfo instanceCreateInfo = { VK_STRUCTURE_TYPE_INSTANCE_CREATE_INFO }; + instanceCreateInfo.pApplicationInfo = &applicationInfo; + + instanceCreateInfo.enabledExtensionCount = sizeof(instanceExtensions) / sizeof(instanceExtensions[0]); + instanceCreateInfo.ppEnabledExtensionNames = &instanceExtensions[0]; + +#if ENABLE_VALIDATION_LAYER + + uint32_t layerCount = 1; + const char *layerNames[] = { + "VK_LAYER_LUNARG_standard_validation" + }; + + instanceCreateInfo.enabledLayerCount = layerCount; + instanceCreateInfo.ppEnabledLayerNames = layerNames; +#endif + + instance = 0; + +#define LOAD_INSTANCE_PROC(NAME) NAME = (PFN_##NAME) vkGetInstanceProcAddr(instance, #NAME); + + FOREACH_GLOBAL_PROC(LOAD_INSTANCE_PROC); + + checkResult(vkCreateInstance( + &instanceCreateInfo, + nullptr, + &instance)); + + FOREACH_INSTANCE_PROC(LOAD_INSTANCE_PROC); + +#undef LOAD_INSTANCE_PROC + + +#if ENABLE_VALIDATION_LAYER + VkDebugReportFlagsEXT debugFlags = VK_DEBUG_REPORT_ERROR_BIT_EXT | VK_DEBUG_REPORT_WARNING_BIT_EXT; + + VkDebugReportCallbackCreateInfoEXT debugCreateInfo = { VK_STRUCTURE_TYPE_DEBUG_REPORT_CREATE_INFO_EXT }; + debugCreateInfo.pfnCallback = &debugMessageCallback; + debugCreateInfo.pUserData = this; + debugCreateInfo.flags = debugFlags; + + checkResult(vkCreateDebugReportCallbackEXT( + instance, &debugCreateInfo, nullptr, &debugReportCallback)); + +#endif + + uint32_t physicalDeviceCount = 0; + checkResult(vkEnumeratePhysicalDevices( + instance, &physicalDeviceCount, nullptr)); + + VkPhysicalDevice* physicalDevices = (VkPhysicalDevice*)alloca( + physicalDeviceCount * sizeof(VkPhysicalDevice)); + checkResult(vkEnumeratePhysicalDevices( + instance, &physicalDeviceCount, physicalDevices)); + + uint32_t selectedDeviceIndex = 0; + // TODO: allow override of selected device + physicalDevice = physicalDevices[selectedDeviceIndex]; + + vkGetPhysicalDeviceProperties(physicalDevice, &deviceProperties); + vkGetPhysicalDeviceFeatures(physicalDevice, &deviceFeatures); + vkGetPhysicalDeviceMemoryProperties(physicalDevice, &deviceMemoryProperties); + + uint32_t queueFamilyCount = 0; + vkGetPhysicalDeviceQueueFamilyProperties( + physicalDevice, &queueFamilyCount, nullptr); + + VkQueueFamilyProperties* queueFamilies = (VkQueueFamilyProperties*)alloca( + queueFamilyCount * sizeof(VkQueueFamilyProperties)); + vkGetPhysicalDeviceQueueFamilyProperties( + physicalDevice, &queueFamilyCount, queueFamilies); + + // Find a queue that can service our needs + VkQueueFlags reqQueueFlags = + VK_QUEUE_GRAPHICS_BIT + | VK_QUEUE_COMPUTE_BIT; + + uint32_t queueFamilyIndex = uint32_t(-1); + for (uint32_t qq = 0; qq < queueFamilyCount; ++qq) + { + if ((queueFamilies[qq].queueFlags & reqQueueFlags) == reqQueueFlags) + { + queueFamilyIndex = qq; + break; + } + } + assert(queueFamilyIndex < queueFamilyCount); + + float queuePriority = 0.0f; + VkDeviceQueueCreateInfo queueCreateInfo = { VK_STRUCTURE_TYPE_DEVICE_QUEUE_CREATE_INFO }; + queueCreateInfo.queueFamilyIndex = queueFamilyIndex; + queueCreateInfo.queueCount = 1; + queueCreateInfo.pQueuePriorities = &queuePriority; + + char const* const deviceExtensions[] = + { + VK_KHR_SWAPCHAIN_EXTENSION_NAME, + }; + + VkDeviceCreateInfo deviceCreateInfo = { VK_STRUCTURE_TYPE_DEVICE_CREATE_INFO }; + deviceCreateInfo.queueCreateInfoCount = 1; + deviceCreateInfo.pQueueCreateInfos = &queueCreateInfo; + deviceCreateInfo.pEnabledFeatures = &enabledFeatures; + + deviceCreateInfo.enabledExtensionCount = sizeof(deviceExtensions) / sizeof(deviceExtensions[0]); + deviceCreateInfo.ppEnabledExtensionNames = &deviceExtensions[0]; + + checkResult(vkCreateDevice( + physicalDevice, &deviceCreateInfo, nullptr, &device)); + +#define LOAD_DEVICE_PROC(NAME) NAME = (PFN_##NAME) vkGetDeviceProcAddr(device, #NAME); + FOREACH_DEVICE_PROC(LOAD_DEVICE_PROC) +#undef LOAD_DEVICE_PROC + + // Create a command pool + + VkCommandPoolCreateInfo commandPoolCreateInfo = { VK_STRUCTURE_TYPE_COMMAND_POOL_CREATE_INFO }; + commandPoolCreateInfo.queueFamilyIndex = queueFamilyIndex; + commandPoolCreateInfo.flags = VK_COMMAND_POOL_CREATE_RESET_COMMAND_BUFFER_BIT; + + checkResult(vkCreateCommandPool( + device, &commandPoolCreateInfo, nullptr, &commandPool)); + + vkGetDeviceQueue( + device, + queueFamilyIndex, + 0, + &queue); + + // set up swap chain + + // create command buffers + + // depth/stencil? + + // render pass? + + // pipeline cache + + // frame buffer + + + +// create semaphores for sync + + + } + + float clearColor[4]; + virtual void setClearColor(float const* color) override + { + for(int ii = 0; ii < 4; ++ii) + clearColor[ii] = color[ii]; + } + + virtual void clearFrame() override + { + } + + virtual void presentFrame() override + { + } + + virtual void captureScreenShot(char const* outputPath) override + { + } + + virtual ShaderCompiler* getShaderCompiler() override + { + return this; + } + + VkCommandBuffer getCommandBuffer() + { + VkCommandBufferAllocateInfo info = { VK_STRUCTURE_TYPE_COMMAND_BUFFER_ALLOCATE_INFO }; + info.commandPool = commandPool; + info.level = VK_COMMAND_BUFFER_LEVEL_PRIMARY; + info.commandBufferCount = 1; + + VkCommandBuffer commandBuffer; + checkResult(vkAllocateCommandBuffers( + device, &info, &commandBuffer)); + + return commandBuffer; + } + + VkCommandBuffer beginCommandBuffer() + { + VkCommandBuffer commandBuffer = getCommandBuffer(); + + VkCommandBufferBeginInfo beginInfo = { VK_STRUCTURE_TYPE_COMMAND_BUFFER_BEGIN_INFO }; + checkResult(vkBeginCommandBuffer(commandBuffer, &beginInfo)); + + return commandBuffer; + } + + void flushCommandBuffer(VkCommandBuffer commandBuffer) + { + checkResult(vkEndCommandBuffer(commandBuffer)); + + VkSubmitInfo submitInfo = { VK_STRUCTURE_TYPE_SUBMIT_INFO }; + submitInfo.commandBufferCount = 1; + submitInfo.pCommandBuffers = &commandBuffer; + + checkResult(vkQueueSubmit(queue, 1, &submitInfo, VK_NULL_HANDLE)); + checkResult(vkQueueWaitIdle(queue)); + + vkFreeCommandBuffers(device, commandPool, 1, &commandBuffer); + } + + struct BufferImpl + { + VkBuffer buffer; + VkDeviceMemory memory; + }; + + BufferImpl createBufferImpl( + size_t bufferSize, + VkBufferUsageFlags usage, + VkMemoryPropertyFlags reqMemoryProperties, + void const* initData = nullptr) + { + if( initData ) + { + // TODO: what if we are allocating it as CPU-writable anyway? + usage |= VK_BUFFER_USAGE_TRANSFER_DST_BIT; + } + + VkBufferCreateInfo bufferCreateInfo = { VK_STRUCTURE_TYPE_BUFFER_CREATE_INFO }; + bufferCreateInfo.size = bufferSize; + bufferCreateInfo.usage = usage; + + VkBuffer buffer; + checkResult(vkCreateBuffer( + device, &bufferCreateInfo, nullptr, &buffer)); + + VkMemoryRequirements memoryReqs = {}; + vkGetBufferMemoryRequirements(device, buffer, &memoryReqs); + + uint32_t memoryTypeIndex = getMemoryTypeIndex( + memoryReqs.memoryTypeBits, reqMemoryProperties); + + VkMemoryPropertyFlags actualMemoryProperites = deviceMemoryProperties.memoryTypes[memoryTypeIndex].propertyFlags; + + VkMemoryAllocateInfo allocateInfo = { VK_STRUCTURE_TYPE_MEMORY_ALLOCATE_INFO }; + allocateInfo.allocationSize = memoryReqs.size; + allocateInfo.memoryTypeIndex = memoryTypeIndex; + + VkDeviceMemory memory; + checkResult(vkAllocateMemory( + device, &allocateInfo, nullptr, &memory)); + + checkResult(vkBindBufferMemory( + device, buffer, memory, 0)); + + if( initData ) + { + // TODO: only create staging buffer if the memory type + // used for the buffer doesn't let us fill things in + // directly. + + BufferImpl staging = createBufferImpl( + bufferSize, + VK_BUFFER_USAGE_TRANSFER_SRC_BIT, + VK_MEMORY_PROPERTY_HOST_VISIBLE_BIT | VK_MEMORY_PROPERTY_HOST_COHERENT_BIT); + + // Copy into staging buffer + void* mappedData = nullptr; + checkResult(vkMapMemory(device, staging.memory, 0, bufferSize, 0, &mappedData)); + memcpy(mappedData, initData, bufferSize); + vkUnmapMemory(device, staging.memory); + + // Copy from staging buffer to real buffer + VkCommandBuffer commandBuffer = beginCommandBuffer(); + + VkBufferCopy copyInfo = {}; + copyInfo.size = bufferSize; + vkCmdCopyBuffer( + commandBuffer, + staging.buffer, + buffer, + 1, + ©Info); + + flushCommandBuffer(commandBuffer); + + // Now destroy the staging buffer + vkDestroyBuffer(device, staging.buffer, nullptr); + vkFreeMemory(device, staging.memory, nullptr); + } + + BufferImpl impl; + impl.buffer = buffer; + impl.memory = memory; + return impl; + } + + virtual Buffer* createBuffer(BufferDesc const& desc) override + { + size_t bufferSize = desc.size; + + VkBufferUsageFlags usage = 0; + VkMemoryPropertyFlags reqMemoryProperties = 0; + + switch( desc.flavor ) + { + case BufferFlavor::Constant: + usage = VK_BUFFER_USAGE_UNIFORM_BUFFER_BIT; + reqMemoryProperties = VK_MEMORY_PROPERTY_HOST_VISIBLE_BIT | VK_MEMORY_PROPERTY_HOST_COHERENT_BIT; + break; + + case BufferFlavor::Vertex: + usage = VK_BUFFER_USAGE_VERTEX_BUFFER_BIT; + reqMemoryProperties = 0; + break; + } + + BufferImpl bufferImpl = createBufferImpl( + bufferSize, + usage, + reqMemoryProperties, + desc.initData); + + BufferImpl* bufferPtr = new BufferImpl(); + *bufferPtr = bufferImpl; + return (Buffer*) bufferPtr; + } + + struct InputLayoutImpl + { + }; + + virtual InputLayout* createInputLayout(InputElementDesc const* inputElements, UInt inputElementCount) override + { + InputLayoutImpl* impl = new InputLayoutImpl(); + + // TODO: actually initialize things + + return (InputLayout*) impl; + } + + virtual void* map(Buffer* buffer, MapFlavor flavor) override + { + return nullptr; + } + + virtual void unmap(Buffer* buffer) override + { + } + + virtual void setInputLayout(InputLayout* inputLayout) override + { + } + + virtual void setPrimitiveTopology(PrimitiveTopology topology) override + { + } + + virtual void setVertexBuffers(UInt startSlot, UInt slotCount, Buffer* const* buffers, UInt const* strides, UInt const* offsets) override + { + } + + struct ShaderProgramImpl + { + VkPipelineShaderStageCreateInfo compute; + VkPipelineShaderStageCreateInfo vertex; + VkPipelineShaderStageCreateInfo fragment; + }; + + ShaderProgramImpl* currentProgram = nullptr; + virtual void setShaderProgram(ShaderProgram* program) override + { + currentProgram = (ShaderProgramImpl*) program; + } + + virtual void setConstantBuffers(UInt startSlot, UInt slotCount, Buffer* const* buffers, UInt const* offsets) override + { + } + + virtual void draw(UInt vertexCount, UInt startVertex = 0) override + { + } + + struct BindingImpl + { + ShaderInputType type; + InputBufferType bufferType; // Only valid if `type` is `Buffer` + + VkImageView srv; + VkBufferView uav; + VkBuffer buffer; + VkSampler samplerState; + + int binding = 0; + bool isOutput = false; + int bufferLength = 0; + }; + + struct BindingStateImpl + { + Slang::List<BindingImpl> bindings; + int numRenderTargets; + }; + + uint32_t getMemoryTypeIndex( + uint32_t inTypeBits, + VkMemoryPropertyFlags properties) + { + uint32_t typeBits = inTypeBits; + uint32_t typeIndex = 0; + while( typeBits ) + { + if((deviceMemoryProperties.memoryTypes[typeIndex].propertyFlags & properties) == properties) + { + return typeIndex; + } + typeIndex++; + typeBits >>= 1; + } + + assert(!"failed to find a usable memory type"); + return uint32_t(-1); + } + + void createInputBuffer( + ShaderInputLayoutEntry const& entry, + InputBufferDesc const& bufferDesc, + Slang::List<unsigned int> const& bufferData, + VkBuffer &bufferOut, + VkBufferView &uavOut, + VkImageView &srvOut) + { + size_t bufferSize = bufferData.Count() * sizeof(unsigned int); + void const* initData = bufferData.Buffer(); + + VkBufferUsageFlags usage = 0; + VkMemoryPropertyFlags reqMemoryProperties = 0; + + switch( bufferDesc.type ) + { + case InputBufferType::ConstantBuffer: + usage = VK_BUFFER_USAGE_UNIFORM_BUFFER_BIT; + reqMemoryProperties = VK_MEMORY_PROPERTY_HOST_VISIBLE_BIT | VK_MEMORY_PROPERTY_HOST_COHERENT_BIT; + break; + + case InputBufferType::StorageBuffer: + usage = VK_BUFFER_USAGE_STORAGE_BUFFER_BIT; + reqMemoryProperties = 0; + break; + } + + // If we are going to read back from the buffer, be sure to request + // the required access. + if(entry.isOutput) + { + usage |= VK_BUFFER_USAGE_TRANSFER_SRC_BIT; + } + + BufferImpl bufferImpl = createBufferImpl( + bufferSize, + usage, + reqMemoryProperties, + initData); + + // TODO: need to hang onto the `memory` field so + // that we can release it when we are done. + + bufferOut = bufferImpl.buffer; + + // Fill in any views needed + switch( bufferDesc.type ) + { + case InputBufferType::ConstantBuffer: + break; + + case InputBufferType::StorageBuffer: + { + } + break; + } + } + + void createInputTexture( + InputTextureDesc const& inputDesc, + VkImageView& viewOut) + { + TextureData texData; + generateTextureData(texData, inputDesc); + assert(!"unimplemented"); + } + + void createInputSampler( + InputSamplerDesc const& inputDesc, + VkSampler& stateOut) + { + assert(!"unimplemented"); + } + + virtual BindingState* createBindingState(const ShaderInputLayout & layout) + { + BindingStateImpl* bindingState = new BindingStateImpl(); + bindingState->numRenderTargets = layout.numRenderTargets; + for (auto & entry : layout.entries) + { + BindingImpl binding; + binding.type = entry.type; + binding.binding = entry.hlslBinding; + binding.isOutput = entry.isOutput; + switch (entry.type) + { + case ShaderInputType::Buffer: + { + createInputBuffer(entry, entry.bufferDesc, entry.bufferData, binding.buffer, binding.uav, binding.srv); + binding.bufferLength = (int)(entry.bufferData.Count() * sizeof(unsigned int)); + binding.bufferType = entry.bufferDesc.type; + } + break; + case ShaderInputType::Texture: + { + createInputTexture(entry.textureDesc, binding.srv); + } + break; + case ShaderInputType::Sampler: + { + createInputSampler(entry.samplerDesc, binding.samplerState); + } + break; + case ShaderInputType::CombinedTextureSampler: + { + throw "not implemented"; + } + break; + } + bindingState->bindings.Add(binding); + } + + return (BindingState*) bindingState; + } + + BindingStateImpl* currentBindingState = nullptr; + virtual void setBindingState(BindingState * state) + { + currentBindingState = (BindingStateImpl*) state; + } + + virtual void serializeOutput(BindingState* s, const char * fileName) + { + auto state = (BindingStateImpl*) s; + + FILE * f = fopen(fileName, "wb"); + int id = 0; + for (auto& bb: state->bindings) + { + if (bb.isOutput) + { + if (bb.buffer) + { + // create staging buffer + size_t bufferSize = bb.bufferLength; + BufferImpl staging = createBufferImpl( + bufferSize, + VK_BUFFER_USAGE_TRANSFER_DST_BIT, + VK_MEMORY_PROPERTY_HOST_VISIBLE_BIT | VK_MEMORY_PROPERTY_HOST_COHERENT_BIT); + + // Copy from real buffer to staging buffer + VkCommandBuffer commandBuffer = beginCommandBuffer(); + + VkBufferCopy copyInfo = {}; + copyInfo.size = bufferSize; + vkCmdCopyBuffer( + commandBuffer, + bb.buffer, + staging.buffer, + 1, + ©Info); + + flushCommandBuffer(commandBuffer); + + // Write out the data from the buffer + void* mappedData = nullptr; + checkResult(vkMapMemory(device, staging.memory, 0, bufferSize, 0, &mappedData)); + + auto ptr = (unsigned int *) mappedData; + for (auto i = 0u; i < bufferSize / sizeof(unsigned int); i++) + fprintf(f, "%X\n", ptr[i]); + + vkUnmapMemory(device, staging.memory); + + // Now destroy the staging buffer + vkDestroyBuffer(device, staging.buffer, nullptr); + vkFreeMemory(device, staging.memory, nullptr); + } + else + { + printf("invalid output type at %d.\n", id); + } + } + id++; + } + fclose(f); + + + } + + virtual void dispatchCompute(int x, int y, int z) override + { + + + // HACK: create a new pipeline for every call + + // First create a pipeline layout based on what is bound + + Slang::List<VkDescriptorSetLayoutBinding> bindings; + + for( auto bb : currentBindingState->bindings ) + { + switch( bb.type ) + { + case ShaderInputType::Buffer: + { + switch(bb.bufferType) + { + case InputBufferType::StorageBuffer: + { + VkDescriptorSetLayoutBinding binding = {}; + binding.binding = bb.binding; + binding.descriptorCount = 1; + binding.descriptorType = VK_DESCRIPTOR_TYPE_STORAGE_BUFFER; + binding.stageFlags = VK_SHADER_STAGE_ALL; + + bindings.Add(binding); + } + break; + + default: + // handle other cases + break; + } + } + break; + + default: + // TODO: handle the other cases + break; + } + } + + VkDescriptorSetLayoutCreateInfo descriptorSetLayoutInfo = { VK_STRUCTURE_TYPE_DESCRIPTOR_SET_LAYOUT_CREATE_INFO }; + descriptorSetLayoutInfo.bindingCount = uint32_t(bindings.Count()); + descriptorSetLayoutInfo.pBindings = bindings.Buffer(); + + VkDescriptorSetLayout descriptorSetLayout = 0; + checkResult(vkCreateDescriptorSetLayout( + device, &descriptorSetLayoutInfo, nullptr, &descriptorSetLayout)); + + // Create a descriptor pool for allocating sets + + VkDescriptorPoolSize poolSizes[] = + { + { VK_DESCRIPTOR_TYPE_UNIFORM_BUFFER, 128 }, + { VK_DESCRIPTOR_TYPE_STORAGE_BUFFER, 128 }, + { VK_DESCRIPTOR_TYPE_SAMPLED_IMAGE, 128 }, + }; + + VkDescriptorPoolCreateInfo descriptorPoolInfo = { VK_STRUCTURE_TYPE_DESCRIPTOR_POOL_CREATE_INFO }; + descriptorPoolInfo.maxSets = 128; // TODO: actually pick a size + descriptorPoolInfo.poolSizeCount = sizeof(poolSizes) / sizeof(poolSizes[0]); + descriptorPoolInfo.pPoolSizes = poolSizes; + + VkDescriptorPool descriptorPool; + checkResult(vkCreateDescriptorPool( + device, &descriptorPoolInfo, nullptr, &descriptorPool)); + + // Create a descriptor set based on our layout + + VkDescriptorSetAllocateInfo descriptorSetAllocInfo = { VK_STRUCTURE_TYPE_DESCRIPTOR_SET_ALLOCATE_INFO }; + descriptorSetAllocInfo.descriptorPool = descriptorPool; + descriptorSetAllocInfo.descriptorSetCount = 1; + descriptorSetAllocInfo.pSetLayouts = &descriptorSetLayout; + + VkDescriptorSet descriptorSet; + checkResult(vkAllocateDescriptorSets( + device, &descriptorSetAllocInfo, &descriptorSet)); + + // Fill in the descritpor set, using our binding information + for( auto bb : currentBindingState->bindings ) + { + switch( bb.type ) + { + case ShaderInputType::Buffer: + { + switch(bb.bufferType) + { + case InputBufferType::StorageBuffer: + { + VkDescriptorBufferInfo bufferInfo; + bufferInfo.buffer = bb.buffer; + bufferInfo.offset = 0; + bufferInfo.range = bb.bufferLength; + + VkWriteDescriptorSet writeInfo = { VK_STRUCTURE_TYPE_WRITE_DESCRIPTOR_SET }; + writeInfo.descriptorCount = 1; + writeInfo.descriptorType = VK_DESCRIPTOR_TYPE_STORAGE_BUFFER; + writeInfo.dstSet = descriptorSet; + writeInfo.dstBinding = bb.binding; + writeInfo.dstArrayElement = 0; + writeInfo.pBufferInfo = &bufferInfo; + + vkUpdateDescriptorSets( + device, + 1, + &writeInfo, + 0, + nullptr); + } + break; + + default: + // handle other cases + break; + } + } + break; + + default: + // TODO: handle the other cases + break; + } + } + + + // Create a pipeline layout based on our descriptor set layout(s) + + VkPipelineLayoutCreateInfo pipelineLayoutInfo = { VK_STRUCTURE_TYPE_PIPELINE_LAYOUT_CREATE_INFO }; + pipelineLayoutInfo.setLayoutCount = 1; + pipelineLayoutInfo.pSetLayouts = &descriptorSetLayout; + + VkPipelineLayout pipelineLayout = 0; + checkResult(vkCreatePipelineLayout( + device, &pipelineLayoutInfo, nullptr, &pipelineLayout)); + + // Then create a pipeline to use that layout + + VkComputePipelineCreateInfo computePipelineInfo = { VK_STRUCTURE_TYPE_COMPUTE_PIPELINE_CREATE_INFO }; + computePipelineInfo.stage = currentProgram->compute; + computePipelineInfo.layout = pipelineLayout; + + VkPipelineCache pipelineCache = 0; + + VkPipeline pipeline; + checkResult(vkCreateComputePipelines( + device, pipelineCache, 1, &computePipelineInfo, nullptr, &pipeline)); + + // Also create descriptor sets based on the given pipeline layout + + VkCommandBuffer commandBuffer = beginCommandBuffer(); + + vkCmdBindPipeline( + commandBuffer, VK_PIPELINE_BIND_POINT_COMPUTE, pipeline); + + vkCmdBindDescriptorSets( + commandBuffer, + VK_PIPELINE_BIND_POINT_COMPUTE, + pipelineLayout, + 0, 1, + &descriptorSet, + 0, + nullptr); + + vkCmdDispatch(commandBuffer, x, y, z); + + flushCommandBuffer(commandBuffer); + + vkDestroyPipeline(device, pipeline, nullptr); + + // TODO: need to free up the other resources too... + } + + // ShaderCompiler interface + + VkPipelineShaderStageCreateInfo compileEntryPoint( + ShaderCompileRequest::EntryPoint const& entryPointRequest, + VkShaderStageFlagBits stage) + { + char const* dataBegin = entryPointRequest.source.dataBegin; + char const* dataEnd = entryPointRequest.source.dataEnd; + + // We need to make a copy of the code, since the Slang compiler + // will free the memory after a compile request is closed. + size_t codeSize = dataEnd - dataBegin; + char* codeBegin = (char*) malloc(codeSize); + memcpy(codeBegin, dataBegin, codeSize); + + VkShaderModuleCreateInfo moduleCreateInfo = { VK_STRUCTURE_TYPE_SHADER_MODULE_CREATE_INFO }; + moduleCreateInfo.pCode = (uint32_t*) codeBegin; + moduleCreateInfo.codeSize = codeSize; + + VkShaderModule module; + checkResult(vkCreateShaderModule( + device, + &moduleCreateInfo, + nullptr, + &module)); + + VkPipelineShaderStageCreateInfo shaderStageCreateInfo = { VK_STRUCTURE_TYPE_PIPELINE_SHADER_STAGE_CREATE_INFO }; + shaderStageCreateInfo.stage = stage; + + shaderStageCreateInfo.module = module; + shaderStageCreateInfo.pName = "main"; + return shaderStageCreateInfo; + } + + virtual ShaderProgram* compileProgram(ShaderCompileRequest const& request) override + { + ShaderProgramImpl* impl = new ShaderProgramImpl(); + + if( request.computeShader.name ) + { + impl->compute = compileEntryPoint( + request.computeShader, + VK_SHADER_STAGE_COMPUTE_BIT); + } + else + { + impl->vertex = compileEntryPoint( + request.vertexShader, + VK_SHADER_STAGE_VERTEX_BIT); + + impl->fragment = compileEntryPoint( + request.fragmentShader, + VK_SHADER_STAGE_FRAGMENT_BIT); + } + + return (ShaderProgram*) impl; + } +}; + + + +Renderer* createVKRenderer() +{ + return new VKRenderer(); +} + +} // renderer_test diff --git a/tools/render-test/render-vk.h b/tools/render-test/render-vk.h new file mode 100644 index 000000000..7f6d789d0 --- /dev/null +++ b/tools/render-test/render-vk.h @@ -0,0 +1,10 @@ +// render-vk.h +#pragma once + +namespace renderer_test { + +class Renderer; + +Renderer* createVKRenderer(); + +} // renderer_test diff --git a/tools/render-test/render.h b/tools/render-test/render.h index 174ba0b7b..5eb0c967d 100644 --- a/tools/render-test/render.h +++ b/tools/render-test/render.h @@ -16,7 +16,14 @@ struct ShaderCompileRequest struct SourceInfo { char const* path; - char const* text; + + // The data may either be source text (in which + // case it can be assumed to be nul-terminated with + // `dataEnd` pointing at the terminator), or + // raw binary data (in which case `dataEnd` points + // at the end of the buffer). + char const* dataBegin; + char const* dataEnd; }; struct EntryPoint diff --git a/tools/render-test/slang-support.cpp b/tools/render-test/slang-support.cpp index 9263aa41b..a75e35bd4 100644 --- a/tools/render-test/slang-support.cpp +++ b/tools/render-test/slang-support.cpp @@ -63,24 +63,24 @@ struct SlangShaderCompilerWrapper : public ShaderCompiler // active in each case. vertexTranslationUnit = spAddTranslationUnit(slangRequest, sourceLanguage, nullptr); - spAddTranslationUnitSourceString(slangRequest, vertexTranslationUnit, request.source.path, request.source.text); + spAddTranslationUnitSourceString(slangRequest, vertexTranslationUnit, request.source.path, request.source.dataBegin); spTranslationUnit_addPreprocessorDefine(slangRequest, vertexTranslationUnit, "__GLSL_VERTEX__", "1"); vertexEntryPointName = "main"; fragmentTranslationUnit = spAddTranslationUnit(slangRequest, sourceLanguage, nullptr); - spAddTranslationUnitSourceString(slangRequest, fragmentTranslationUnit, request.source.path, request.source.text); + spAddTranslationUnitSourceString(slangRequest, fragmentTranslationUnit, request.source.path, request.source.dataBegin); spTranslationUnit_addPreprocessorDefine(slangRequest, fragmentTranslationUnit, "__GLSL_FRAGMENT__", "1"); fragmentEntryPointName = "main"; computeTranslationUnit = spAddTranslationUnit(slangRequest, sourceLanguage, nullptr); - spAddTranslationUnitSourceString(slangRequest, computeTranslationUnit, request.source.path, request.source.text); + spAddTranslationUnitSourceString(slangRequest, computeTranslationUnit, request.source.path, request.source.dataBegin); spTranslationUnit_addPreprocessorDefine(slangRequest, computeTranslationUnit, "__GLSL_COMPUTE__", "1"); computeEntryPointName = "main"; } else { int translationUnit = spAddTranslationUnit(slangRequest, sourceLanguage, nullptr); - spAddTranslationUnitSourceString(slangRequest, translationUnit, request.source.path, request.source.text); + spAddTranslationUnitSourceString(slangRequest, translationUnit, request.source.path, request.source.dataBegin); vertexTranslationUnit = translationUnit; fragmentTranslationUnit = translationUnit; @@ -108,8 +108,11 @@ struct SlangShaderCompilerWrapper : public ShaderCompiler if (!compileErr) { ShaderCompileRequest innerRequest = request; - char const* computeCode = spGetEntryPointSource(slangRequest, computeEntryPoint); - innerRequest.computeShader.source.text = computeCode; + + size_t codeSize = 0; + char const* code = (char const*) spGetEntryPointCode(slangRequest, computeEntryPoint, &codeSize); + innerRequest.computeShader.source.dataBegin = code; + innerRequest.computeShader.source.dataEnd = code + codeSize; result = innerCompiler->compileProgram(innerRequest); } } @@ -129,11 +132,17 @@ struct SlangShaderCompilerWrapper : public ShaderCompiler { ShaderCompileRequest innerRequest = request; - char const* vertexCode = spGetEntryPointSource(slangRequest, vertexEntryPoint); - char const* fragmentCode = spGetEntryPointSource(slangRequest, fragmentEntryPoint); + size_t vertexCodeSize = 0; + char const* vertexCode = (char const*) spGetEntryPointCode(slangRequest, vertexEntryPoint, &vertexCodeSize); + + size_t fragmentCodeSize = 0; + char const* fragmentCode = (char const*) spGetEntryPointCode(slangRequest, fragmentEntryPoint, &fragmentCodeSize); + + innerRequest.vertexShader.source.dataBegin = vertexCode; + innerRequest.vertexShader.source.dataEnd = vertexCode + vertexCodeSize; - innerRequest.vertexShader.source.text = vertexCode; - innerRequest.fragmentShader.source.text = fragmentCode; + innerRequest.fragmentShader.source.dataBegin = fragmentCode; + innerRequest.fragmentShader.source.dataEnd = fragmentCode + fragmentCodeSize; result = innerCompiler->compileProgram(innerRequest); } |
