diff options
| author | jsmall-nvidia <jsmall@nvidia.com> | 2018-05-03 14:25:13 -0400 |
|---|---|---|
| committer | GitHub <noreply@github.com> | 2018-05-03 14:25:13 -0400 |
| commit | 367f3a78a40731da45ee12b9a18c94707f1d1429 (patch) | |
| tree | 5993ef627e1a094ea1d401c31e6b00e3c63c308a | |
| parent | 78935493587ec65a199d844327613021667acc1b (diff) | |
Feature/vulkan first render (#545)
* First pass at InputLayout for Vulkan
Add support for RGBA_Float32
* Use VulkanModule and VulkanApi to handle accessing Vulkan types.
* First pass at Vulkan swap chain/Device queue.
* Added VulkanUtil for generic function functions.
* Move more functionality to VulkanApi and VulkanUtil.
Make Buffer able to initialize itself.
* More tidy up around VulkanDeviceQueue
* First pass use of VulkanDeviceQueue in VkRenderer
* First pass use of VulkanSwapChain on VkRenderer
* Added depth formats.
Binding for constant and vertex buffers for Vulkan.
* Setting up VkImageView on backbuffers.
* First pass support for setting up vkRenderPass.
* Fixes to work around Vulkan swap chain/verification issues.
* Added support for Pipeline and a pipeline cache.
* Working without waiting - because use of pipeline cache.
* Added support for VkFramebuffer in Vulkan.
* First pass at creating Vulkan graphics pipeline.
* More efforts to get Vulkan to render.
* Small improvement for checking of Binding flags.
* Removed setConstantBuffers from the Renderer interface - so that all resource binding takes place through the BindingState.
To make this work required a 'hack' in render-test main.cpp - so that the constant buffer binding that is needed in some tests is only added when it doesn't clash.
* RendererID -> unified into RendererType. Added getRendererType to Renderer interface.
Added ProjectionStyle, and function to get from RendererType.
Added getIdentityProjection to RendererUtil - to get projection that is the 'identity' - but hits the same pixels for all projection styles.
* Fix build problem on Win32 on Vulkan where should use VK_NULL_HANDLE.
* Improve naming, comments. Remove dead code.
* Remove unwanted comment.
23 files changed, 2574 insertions, 673 deletions
diff --git a/tools/render-test/d3d-util.cpp b/tools/render-test/d3d-util.cpp index 70ccadddb..5cc0dc69b 100644 --- a/tools/render-test/d3d-util.cpp +++ b/tools/render-test/d3d-util.cpp @@ -26,10 +26,15 @@ using namespace Slang; { switch (format) { + case Format::RGBA_Float32: return DXGI_FORMAT_R32G32B32A32_FLOAT; case Format::RGB_Float32: return DXGI_FORMAT_R32G32B32_FLOAT; case Format::RG_Float32: return DXGI_FORMAT_R32G32_FLOAT; case Format::R_Float32: return DXGI_FORMAT_R32_FLOAT; case Format::RGBA_Unorm_UInt8: return DXGI_FORMAT_R8G8B8A8_UNORM; + + case Format::D_Float32: return DXGI_FORMAT_D32_FLOAT; + case Format::D_Unorm24_S8: return DXGI_FORMAT_D24_UNORM_S8_UINT; + default: return DXGI_FORMAT_UNKNOWN; } } diff --git a/tools/render-test/main.cpp b/tools/render-test/main.cpp index 5c15c8037..7dcb9af95 100644 --- a/tools/render-test/main.cpp +++ b/tools/render-test/main.cpp @@ -103,26 +103,41 @@ SlangResult RenderTestApp::initialize(Renderer* renderer, ShaderCompiler* shader m_renderer = renderer; + // TODO(tfoley): use each API's reflection interface to query the constant-buffer size needed + { + m_constantBufferSize = 16 * sizeof(float); + + BufferResource::Desc constantBufferDesc; + constantBufferDesc.init(m_constantBufferSize); + constantBufferDesc.cpuAccessFlags = Resource::AccessFlag::Write; + + m_constantBuffer = renderer->createBufferResource(Resource::Usage::ConstantBuffer, constantBufferDesc); + if (!m_constantBuffer) + return SLANG_FAIL; + } + { BindingState::Desc bindingStateDesc; SLANG_RETURN_ON_FAIL(ShaderRendererUtil::createBindingStateDesc(m_shaderInputLayout, m_renderer, bindingStateDesc)); + //! Hack -> if bindings are specified, just set up the constant buffer binding + // Should probably be more sophisticated than this - with 'dynamic' constant buffer/s binding always being specified + // in the test file + + if ((gOptions.shaderType == Options::ShaderProgramType::Graphics || gOptions.shaderType == Options::ShaderProgramType::GraphicsCompute) + && bindingStateDesc.findBindingIndex(Resource::BindFlag::ConstantBuffer, -1, 0) < 0) + { + BindingState::ShaderBindSet shaderBindSet; + shaderBindSet.setAll(bindingStateDesc.makeCompactSlice(0)); + + bindingStateDesc.addResource(BindingType::Buffer, m_constantBuffer, shaderBindSet); + } + m_bindingState = m_renderer->createBindingState(bindingStateDesc); } // Do other initialization that doesn't depend on the source language. - // TODO(tfoley): use each API's reflection interface to query the constant-buffer size needed - m_constantBufferSize = 16 * sizeof(float); - - BufferResource::Desc constantBufferDesc; - constantBufferDesc.init(m_constantBufferSize); - constantBufferDesc.cpuAccessFlags = Resource::AccessFlag::Write; - - m_constantBuffer = renderer->createBufferResource(Resource::Usage::ConstantBuffer, constantBufferDesc); - if(!m_constantBuffer) - return SLANG_FAIL; - // Input Assembler (IA) const InputElementDesc inputElements[] = { @@ -177,7 +192,7 @@ Result RenderTestApp::initializeShaders(ShaderCompiler* shaderCompiler) ShaderCompileRequest compileRequest; compileRequest.source = sourceInfo; - if (gOptions.shaderType == ShaderProgramType::Graphics || gOptions.shaderType == ShaderProgramType::GraphicsCompute) + if (gOptions.shaderType == Options::ShaderProgramType::Graphics || gOptions.shaderType == Options::ShaderProgramType::GraphicsCompute) { compileRequest.vertexShader.source = sourceInfo; compileRequest.vertexShader.name = vertexEntryPointName; @@ -207,13 +222,9 @@ void RenderTestApp::renderFrame() auto mappedData = m_renderer->map(m_constantBuffer, MapFlavor::WriteDiscard); if(mappedData) { - static const float kIdentity[] = - { 1, 0, 0, 0, - 0, 1, 0, 0, - 0, 0, 1, 0, - 0, 0, 0, 1 }; - memcpy(mappedData, kIdentity, sizeof(kIdentity)); - + const ProjectionStyle projectionStyle = RendererUtil::getProjectionStyle(m_renderer->getRendererType()); + RendererUtil::getIdentityProjection(projectionStyle, (float*)mappedData); + m_renderer->unmap(m_constantBuffer); } @@ -228,7 +239,6 @@ void RenderTestApp::renderFrame() // Pixel Shader (PS) m_renderer->setShaderProgram(m_shaderProgram); - m_renderer->setConstantBuffer(0, m_constantBuffer); m_renderer->setBindingState(m_bindingState); // @@ -389,27 +399,27 @@ SlangResult innerMain(int argc, char** argv) SlangSourceLanguage nativeLanguage = SLANG_SOURCE_LANGUAGE_UNKNOWN; SlangCompileTarget slangTarget = SLANG_TARGET_NONE; - switch (gOptions.rendererID) + switch (gOptions.rendererType) { - case RendererID::D3D11: + case RendererType::DirectX11: renderer = createD3D11Renderer(); slangTarget = SLANG_HLSL; nativeLanguage = SLANG_SOURCE_LANGUAGE_HLSL; break; - case RendererID::D3D12: + case RendererType::DirectX12: renderer = createD3D12Renderer(); slangTarget = SLANG_HLSL; nativeLanguage = SLANG_SOURCE_LANGUAGE_HLSL; break; - case RendererID::GL: + case RendererType::OpenGl: renderer = createGLRenderer(); slangTarget = SLANG_GLSL; nativeLanguage = SLANG_SOURCE_LANGUAGE_GLSL; break; - case RendererID::VK: + case RendererType::Vulkan: renderer = createVKRenderer(); slangTarget = SLANG_SPIRV; nativeLanguage = SLANG_SOURCE_LANGUAGE_GLSL; @@ -425,11 +435,11 @@ SlangResult innerMain(int argc, char** argv) auto shaderCompiler = renderer->getShaderCompiler(); switch (gOptions.inputLanguageID) { - case InputLanguageID::Slang: + case Options::InputLanguageID::Slang: shaderCompiler = createSlangShaderCompiler(shaderCompiler, SLANG_SOURCE_LANGUAGE_SLANG, slangTarget); break; - case InputLanguageID::NativeRewrite: + case Options::InputLanguageID::NativeRewrite: shaderCompiler = createSlangShaderCompiler(shaderCompiler, nativeLanguage, slangTarget); break; @@ -464,7 +474,7 @@ SlangResult innerMain(int argc, char** argv) else { // Whenever we don't have Windows events to process, we render a frame. - if (gOptions.shaderType == ShaderProgramType::Compute) + if (gOptions.shaderType == Options::ShaderProgramType::Compute) { app.runCompute(); } @@ -484,7 +494,7 @@ SlangResult innerMain(int argc, char** argv) // Wait until everything is complete renderer->waitForGpu(); - if (gOptions.shaderType == ShaderProgramType::Compute || gOptions.shaderType == ShaderProgramType::GraphicsCompute) + if (gOptions.shaderType == Options::ShaderProgramType::Compute || gOptions.shaderType == Options::ShaderProgramType::GraphicsCompute) { SLANG_RETURN_ON_FAIL(app.writeBindingOutput(gOptions.outputPath)); } diff --git a/tools/render-test/options.cpp b/tools/render-test/options.cpp index bdf635620..1aa72c033 100644 --- a/tools/render-test/options.cpp +++ b/tools/render-test/options.cpp @@ -11,13 +11,17 @@ namespace renderer_test { Options gOptions; // Only set it, if the -void setDefaultRendererID(RendererID id) +void setDefaultRendererType(RendererType type) { - gOptions.rendererID = (gOptions.rendererID == RendererID::NONE) ? id : gOptions.rendererID; + gOptions.rendererType = (gOptions.rendererType == RendererType::Unknown) ? type : gOptions.rendererType; } SlangResult parseOptions(int* argc, char** argv) { + typedef Options::ShaderProgramType ShaderProgramType; + typedef Options::InputLanguageID InputLanguageID; + + int argCount = *argc; char const* const* argCursor = argv; char const* const* argEnd = argCursor + argCount; @@ -60,32 +64,32 @@ SlangResult parseOptions(int* argc, char** argv) } else if( strcmp(arg, "-hlsl") == 0 ) { - setDefaultRendererID( RendererID::D3D11); + setDefaultRendererType( RendererType::DirectX11); gOptions.inputLanguageID = InputLanguageID::Native; } else if( strcmp(arg, "-glsl") == 0 ) { - setDefaultRendererID(RendererID::GL); + setDefaultRendererType( RendererType::OpenGl); gOptions.inputLanguageID = InputLanguageID::Native; } else if( strcmp(arg, "-hlsl-rewrite") == 0 ) { - setDefaultRendererID(RendererID::D3D11); + setDefaultRendererType( RendererType::DirectX11); gOptions.inputLanguageID = InputLanguageID::NativeRewrite; } else if( strcmp(arg, "-glsl-rewrite") == 0 ) { - setDefaultRendererID(RendererID::GL); + setDefaultRendererType(RendererType::OpenGl); gOptions.inputLanguageID = InputLanguageID::NativeRewrite; } else if( strcmp(arg, "-slang") == 0 ) { - setDefaultRendererID(RendererID::D3D11); + setDefaultRendererType( RendererType::DirectX11); gOptions.inputLanguageID = InputLanguageID::Slang; } else if( strcmp(arg, "-glsl-cross") == 0 ) { - setDefaultRendererID(RendererID::GL); + setDefaultRendererType(RendererType::OpenGl); gOptions.inputLanguageID = InputLanguageID::Slang; } else if( strcmp(arg, "-xslang") == 0 ) @@ -97,9 +101,9 @@ SlangResult parseOptions(int* argc, char** argv) fprintf(stderr, "expected argument for '%s' option\n", arg); return SLANG_FAIL; } - if( gOptions.slangArgCount == kMaxSlangArgs ) + if( gOptions.slangArgCount == Options::kMaxSlangArgs ) { - fprintf(stderr, "maximum number of '%s' options exceeded (%d)\n", arg, kMaxSlangArgs); + fprintf(stderr, "maximum number of '%s' options exceeded (%d)\n", arg, Options::kMaxSlangArgs); return SLANG_FAIL; } gOptions.slangArgs[gOptions.slangArgCount++] = *argCursor++; @@ -119,21 +123,21 @@ SlangResult parseOptions(int* argc, char** argv) else if (strcmp(arg, "-vk") == 0 || strcmp(arg, "-vulkan") == 0) { - gOptions.rendererID = RendererID::VK; + gOptions.rendererType = RendererType::Vulkan; } else if (strcmp(arg, "-d3d12") == 0 || strcmp(arg, "-dx12") == 0) { - gOptions.rendererID = RendererID::D3D12; + gOptions.rendererType = RendererType::DirectX12; } else if(strcmp(arg, "-gl") == 0) { - gOptions.rendererID = RendererID::GL; + gOptions.rendererType = RendererType::OpenGl; } else if (strcmp(arg, "-d3d11") == 0 || strcmp(arg, "-dx11") == 0) { - gOptions.rendererID = RendererID::D3D11; + gOptions.rendererType = RendererType::DirectX11; } else { diff --git a/tools/render-test/options.h b/tools/render-test/options.h index 8dbff76ea..56bd638ef 100644 --- a/tools/render-test/options.h +++ b/tools/render-test/options.h @@ -5,53 +5,44 @@ #include "../../source/core/slang-result.h" -namespace renderer_test { - -typedef intptr_t Int; -typedef uintptr_t UInt; - -enum class RendererID -{ - NONE, - D3D11, - D3D12, - GL, - VK, -}; +#include "render.h" -enum class InputLanguageID -{ - // Slang being used as an HLSL-ish compiler - Slang, - - // Raw HLSL or GLSL input, bypassing Slang - Native, - - // Raw HLSL or GLSL input, passed through the Slang rewriter - NativeRewrite -}; - -enum -{ - // maximum number of command-line arguments to pass along to slang - kMaxSlangArgs = 16, -}; - -enum class ShaderProgramType -{ - Graphics, - Compute, - GraphicsCompute -}; +namespace renderer_test { struct Options { + enum + { + // maximum number of command-line arguments to pass along to slang + kMaxSlangArgs = 16, + }; + + enum class InputLanguageID + { + // Slang being used as an HLSL-ish compiler + Slang, + + // Raw HLSL or GLSL input, bypassing Slang + Native, + + // Raw HLSL or GLSL input, passed through the Slang rewriter + NativeRewrite + }; + + + enum class ShaderProgramType + { + Graphics, + Compute, + GraphicsCompute + }; + char const* appName = "render-test"; char const* sourcePath = nullptr; char const* outputPath = nullptr; ShaderProgramType shaderType = ShaderProgramType::Graphics; - RendererID rendererID = RendererID::NONE; + RendererType rendererType = RendererType::Unknown; InputLanguageID inputLanguageID = InputLanguageID::Slang; char const* slangArgs[kMaxSlangArgs]; diff --git a/tools/render-test/render-d3d11.cpp b/tools/render-test/render-d3d11.cpp index 7018c223a..e2bcf1659 100644 --- a/tools/render-test/render-d3d11.cpp +++ b/tools/render-test/render-d3d11.cpp @@ -64,11 +64,11 @@ public: virtual void setBindingState(BindingState * state); virtual void setVertexBuffers(UInt startSlot, UInt slotCount, BufferResource*const* buffers, const UInt* strides, const UInt* offsets) override; virtual void setShaderProgram(ShaderProgram* inProgram) override; - virtual void setConstantBuffers(UInt startSlot, UInt slotCount, BufferResource*const* buffers, const UInt* offsets) override; virtual void draw(UInt vertexCount, UInt startVertex) override; virtual void dispatchCompute(int x, int y, int z) override; virtual void submitGpuWork() override {} virtual void waitForGpu() override {} + virtual RendererType getRendererType() const override { return RendererType::DirectX11; } // ShaderCompiler implementation virtual ShaderProgram* compileProgram(ShaderCompileRequest const& request) override; @@ -144,7 +144,7 @@ public: /// Capture a texture to a file static HRESULT captureTextureToFile(ID3D11Device* device, ID3D11DeviceContext* context, ID3D11Texture2D* texture, char const* outputPath); - void applyBindingState(bool isCompute); + void _applyBindingState(bool isCompute); ComPtr<IDXGISwapChain> m_swapChain; ComPtr<ID3D11Device> m_device; @@ -669,6 +669,9 @@ InputLayout* D3D11Renderer::createInputLayout(const InputElementDesc* inputEleme char const* typeName = "Unknown"; switch (inputElementsIn[ii].format) { + case Format::RGBA_Float32: + typeName = "float4"; + break; case Format::RGB_Float32: typeName = "float3"; break; @@ -794,29 +797,9 @@ void D3D11Renderer::setShaderProgram(ShaderProgram* programIn) m_immediateContext->PSSetShader(program->m_pixelShader, nullptr, 0); } -void D3D11Renderer::setConstantBuffers(UInt startSlot, UInt slotCount, BufferResource*const* buffersIn, const UInt* offsetsIn) -{ - static const int kMaxConstantBuffers = 16; - assert(slotCount <= kMaxConstantBuffers); - - // TODO: actually use those offsets - - auto buffers = (BufferResourceImpl*const*)buffersIn; - - // Copy out the actual dx buffers - ID3D11Buffer* dxBuffers[kMaxConstantBuffers]; - for (UInt i = 0; i < slotCount; i++) - { - dxBuffers[i] = buffers[i]->m_buffer; - } - - m_immediateContext->VSSetConstantBuffers((UINT)startSlot, (UINT)slotCount, dxBuffers); - m_immediateContext->VSSetConstantBuffers((UINT)startSlot, (UINT)slotCount, dxBuffers); -} - void D3D11Renderer::draw(UInt vertexCount, UInt startVertex) { - applyBindingState(false); + _applyBindingState(false); m_immediateContext->Draw((UINT)vertexCount, (UINT)startVertex); } @@ -855,7 +838,7 @@ ShaderProgram* D3D11Renderer::compileProgram(const ShaderCompileRequest& request void D3D11Renderer::dispatchCompute(int x, int y, int z) { - applyBindingState(true); + _applyBindingState(true); m_immediateContext->Dispatch(x, y, z); } @@ -1048,7 +1031,7 @@ BindingState* D3D11Renderer::createBindingState(const BindingState::Desc& bindin return bindingState.detach(); } -void D3D11Renderer::applyBindingState(bool isCompute) +void D3D11Renderer::_applyBindingState(bool isCompute) { auto context = m_immediateContext.get(); diff --git a/tools/render-test/render-d3d12.cpp b/tools/render-test/render-d3d12.cpp index 66f148c23..635c6739f 100644 --- a/tools/render-test/render-d3d12.cpp +++ b/tools/render-test/render-d3d12.cpp @@ -73,11 +73,11 @@ public: virtual void setBindingState(BindingState* state); virtual void setVertexBuffers(UInt startSlot, UInt slotCount, BufferResource*const* buffers, const UInt* strides, const UInt* offsets) override; virtual void setShaderProgram(ShaderProgram* inProgram) override; - virtual void setConstantBuffers(UInt startSlot, UInt slotCount, BufferResource*const* buffers, const UInt* offsets) override; virtual void draw(UInt vertexCount, UInt startVertex) override; virtual void dispatchCompute(int x, int y, int z) override; virtual void submitGpuWork() override; virtual void waitForGpu() override; + virtual RendererType getRendererType() const override { return RendererType::DirectX12; } // ShaderCompiler implementation virtual ShaderProgram* compileProgram(const ShaderCompileRequest& request) override; @@ -88,12 +88,6 @@ protected: static const Int kMaxNumRenderFrames = 4; static const Int kMaxNumRenderTargets = 3; - enum class ProgramType - { - Compute, - Graphics, - }; - struct Submitter { virtual void setRootConstantBufferView(int index, D3D12_GPU_VIRTUAL_ADDRESS gpuBufferLocation) = 0; @@ -115,7 +109,7 @@ protected: class ShaderProgramImpl: public ShaderProgram { public: - ProgramType m_programType; + PipelineType m_pipelineType; List<uint8_t> m_vertexShader; List<uint8_t> m_pixelShader; List<uint8_t> m_computeShader; @@ -350,7 +344,7 @@ protected: Result _bindRenderState(RenderState* renderState, ID3D12GraphicsCommandList* commandList, Submitter* submitter); Result _calcBindParameters(BindParameters& params); - RenderState* findRenderState(ProgramType programType); + RenderState* findRenderState(PipelineType pipelineType); PFN_D3D12_SERIALIZE_ROOT_SIGNATURE m_D3D12SerializeRootSignature = nullptr; @@ -359,7 +353,6 @@ protected: int m_commandListOpenCount = 0; ///< If >0 the command list should be open List<BoundVertexBuffer> m_boundVertexBuffers; - List<RefPtr<BufferResourceImpl> > m_boundConstantBuffers; RefPtr<ShaderProgramImpl> m_boundShaderProgram; RefPtr<InputLayoutImpl> m_boundInputLayout; @@ -971,11 +964,11 @@ Result D3D12Renderer::calcGraphicsPipelineState(ComPtr<ID3D12RootSignature>& sig return SLANG_OK; } -D3D12Renderer::RenderState* D3D12Renderer::findRenderState(ProgramType programType) +D3D12Renderer::RenderState* D3D12Renderer::findRenderState(PipelineType pipelineType) { - switch (programType) + switch (pipelineType) { - case ProgramType::Compute: + case PipelineType::Compute: { // Check if current state is a match if (m_currentRenderState) @@ -999,7 +992,7 @@ D3D12Renderer::RenderState* D3D12Renderer::findRenderState(ProgramType programTy } break; } - case ProgramType::Graphics: + case PipelineType::Graphics: { if (m_currentRenderState) { @@ -1040,7 +1033,7 @@ D3D12Renderer::RenderState* D3D12Renderer::calcRenderState() { return nullptr; } - m_currentRenderState = findRenderState(m_boundShaderProgram->m_programType); + m_currentRenderState = findRenderState(m_boundShaderProgram->m_pipelineType); if (m_currentRenderState) { return m_currentRenderState; @@ -1049,9 +1042,9 @@ D3D12Renderer::RenderState* D3D12Renderer::calcRenderState() ComPtr<ID3D12RootSignature> rootSignature; ComPtr<ID3D12PipelineState> pipelineState; - switch (m_boundShaderProgram->m_programType) + switch (m_boundShaderProgram->m_pipelineType) { - case ProgramType::Compute: + case PipelineType::Compute: { if (SLANG_FAILED(calcComputePipelineState(rootSignature, pipelineState))) { @@ -1059,7 +1052,7 @@ D3D12Renderer::RenderState* D3D12Renderer::calcRenderState() } break; } - case ProgramType::Graphics: + case PipelineType::Graphics: { if (SLANG_FAILED(calcGraphicsPipelineState(rootSignature, pipelineState))) { @@ -1170,28 +1163,6 @@ Result D3D12Renderer::_calcBindParameters(BindParameters& params) } } -#if 1 - // Okay we need to try and create a render state - for (int i = 0; i < int(m_boundConstantBuffers.Count()); i++) - { - const BufferResourceImpl* buffer = m_boundConstantBuffers[i]; - if (buffer) - { - assert(buffer->m_backingStyle == BufferResourceImpl::BackingStyle::MemoryBacked); - - D3D12_ROOT_PARAMETER& param = params.nextParameter(); - param.ParameterType = D3D12_ROOT_PARAMETER_TYPE_CBV; - param.ShaderVisibility = D3D12_SHADER_VISIBILITY_ALL; - - D3D12_ROOT_DESCRIPTOR& descriptor = param.Descriptor; - descriptor.ShaderRegister = numConstantBuffers; - descriptor.RegisterSpace = 0; - - numConstantBuffers++; - } - } -#endif - // All the samplers are in one continuous section of the sampler heap if (m_boundBindingState && m_boundBindingState->m_samplerHeap.getUsedSize() > 0) { @@ -1275,17 +1246,6 @@ Result D3D12Renderer::_bindRenderState(RenderState* renderState, ID3D12GraphicsC } } } - - // Okay we need to try and create a render state - for (int i = 0; i < int(m_boundConstantBuffers.Count()); i++) - { - const BufferResourceImpl* buffer = m_boundConstantBuffers[i]; - if (buffer) - { - buffer->bindConstantBufferView(m_circularResourceHeap, index++, submitter); - numConstantBuffers++; - } - } } if (bindingState && bindingState->m_samplerHeap.getUsedSize() > 0) @@ -2275,27 +2235,6 @@ void D3D12Renderer::setShaderProgram(ShaderProgram* inProgram) m_boundShaderProgram = static_cast<ShaderProgramImpl*>(inProgram); } -void D3D12Renderer::setConstantBuffers(UInt startSlot, UInt slotCount, BufferResource*const* buffers, const UInt* offsets) -{ - { - const UInt num = startSlot + slotCount; - if (num > m_boundConstantBuffers.Count()) - { - m_boundConstantBuffers.SetSize(num); - } - } - - for (UInt i = 0; i < slotCount; i++) - { - BufferResourceImpl* buffer = static_cast<BufferResourceImpl*>(buffers[i]); - if (buffer) - { - assert(buffer->m_initialUsage == Resource::Usage::ConstantBuffer); - } - m_boundConstantBuffers[startSlot + i] = buffer; - } -} - void D3D12Renderer::draw(UInt vertexCount, UInt startVertex) { ID3D12GraphicsCommandList* commandList = m_commandList; @@ -2527,7 +2466,7 @@ ShaderProgram* D3D12Renderer::compileProgram(const ShaderCompileRequest& request if (request.computeShader.name) { - program->m_programType = ProgramType::Compute; + program->m_pipelineType = PipelineType::Compute; ComPtr<ID3DBlob> computeShaderBlob; SLANG_RETURN_NULL_ON_FAIL(D3DUtil::compileHLSLShader(request.computeShader.source.path, request.computeShader.source.dataBegin, request.computeShader.name, request.computeShader.profile, computeShaderBlob)); @@ -2535,7 +2474,7 @@ ShaderProgram* D3D12Renderer::compileProgram(const ShaderCompileRequest& request } else { - program->m_programType = ProgramType::Graphics; + program->m_pipelineType = PipelineType::Graphics; ComPtr<ID3DBlob> vertexShaderBlob, fragmentShaderBlob; SLANG_RETURN_NULL_ON_FAIL(D3DUtil::compileHLSLShader(request.vertexShader.source.path, request.vertexShader.source.dataBegin, request.vertexShader.name, request.vertexShader.profile, vertexShaderBlob)); SLANG_RETURN_NULL_ON_FAIL(D3DUtil::compileHLSLShader(request.fragmentShader.source.path, request.fragmentShader.source.dataBegin, request.fragmentShader.name, request.fragmentShader.profile, fragmentShaderBlob)); diff --git a/tools/render-test/render-gl.cpp b/tools/render-test/render-gl.cpp index 10e25fdcd..f34f9d910 100644 --- a/tools/render-test/render-gl.cpp +++ b/tools/render-test/render-gl.cpp @@ -95,11 +95,11 @@ public: virtual void setBindingState(BindingState* state); virtual void setVertexBuffers(UInt startSlot, UInt slotCount, BufferResource*const* buffers, const UInt* strides, const UInt* offsets) override; virtual void setShaderProgram(ShaderProgram* inProgram) override; - virtual void setConstantBuffers(UInt startSlot, UInt slotCount, BufferResource*const* buffers, const UInt* offsets) override; virtual void draw(UInt vertexCount, UInt startVertex) override; virtual void dispatchCompute(int x, int y, int z) override; virtual void submitGpuWork() override {} virtual void waitForGpu() override {} + virtual RendererType getRendererType() const override { return RendererType::OpenGl; } // ShaderCompiler implementation virtual ShaderProgram* compileProgram(const ShaderCompileRequest& request) override; @@ -280,7 +280,7 @@ public: MAP_GL_EXTENSION_FUNCS(DECLARE_GL_EXTENSION_FUNC) #undef DECLARE_GL_EXTENSION_FUNC - static const GlPixelFormatInfo s_pixelFormatInfos[int(GlPixelFormat::CountOf)]; + static const GlPixelFormatInfo s_pixelFormatInfos[int(GlPixelFormat::CountOf)]; }; /* static */GLRenderer::GlPixelFormat GLRenderer::_getGlPixelFormat(Format format) @@ -333,6 +333,7 @@ void GLRenderer::debugCallback(GLenum source, GLenum type, GLuint id, GLenum sev #define CASE(NAME, COUNT, TYPE, NORMALIZED) \ case Format::NAME: do { VertexAttributeFormat result = {COUNT, TYPE, NORMALIZED}; return result; } while (0) + CASE(RGBA_Float32, 4, GL_FLOAT, GL_FALSE); CASE(RGB_Float32, 3, GL_FLOAT, GL_FALSE); CASE(RG_Float32, 2, GL_FLOAT, GL_FALSE); CASE(R_Float32, 1, GL_FLOAT, GL_FALSE); @@ -895,11 +896,6 @@ void GLRenderer::setShaderProgram(ShaderProgram* programIn) glUseProgram(programID); } -void GLRenderer::setConstantBuffers(UInt startSlot, UInt slotCount, BufferResource*const* buffers, const UInt* offsets) -{ - bindBufferImpl(GL_UNIFORM_BUFFER, startSlot, slotCount, buffers, offsets); -} - void GLRenderer::draw(UInt vertexCount, UInt startVertex = 0) { flushStateForDraw(); diff --git a/tools/render-test/render-test.vcxproj b/tools/render-test/render-test.vcxproj index 1d4d5ed2e..3fecb6879 100644 --- a/tools/render-test/render-test.vcxproj +++ b/tools/render-test/render-test.vcxproj @@ -180,6 +180,11 @@ <ClCompile Include="shader-input-layout.cpp" /> <ClCompile Include="shader-renderer-util.cpp" /> <ClCompile Include="slang-support.cpp" /> + <ClCompile Include="vk-api.cpp" /> + <ClCompile Include="vk-device-queue.cpp" /> + <ClCompile Include="vk-module.cpp" /> + <ClCompile Include="vk-swap-chain.cpp" /> + <ClCompile Include="vk-util.cpp" /> </ItemGroup> <ItemGroup> <ClInclude Include="circular-resource-heap-d3d12.h" /> @@ -195,6 +200,11 @@ <ClInclude Include="shader-input-layout.h" /> <ClInclude Include="shader-renderer-util.h" /> <ClInclude Include="slang-support.h" /> + <ClInclude Include="vk-api.h" /> + <ClInclude Include="vk-device-queue.h" /> + <ClInclude Include="vk-module.h" /> + <ClInclude Include="vk-swap-chain.h" /> + <ClInclude Include="vk-util.h" /> <ClInclude Include="window.h" /> </ItemGroup> <ItemGroup> diff --git a/tools/render-test/render-test.vcxproj.filters b/tools/render-test/render-test.vcxproj.filters index 82e1d990f..2bbf03af2 100644 --- a/tools/render-test/render-test.vcxproj.filters +++ b/tools/render-test/render-test.vcxproj.filters @@ -57,6 +57,21 @@ <ClCompile Include="shader-renderer-util.cpp"> <Filter>Source Files</Filter> </ClCompile> + <ClCompile Include="vk-api.cpp"> + <Filter>Source Files</Filter> + </ClCompile> + <ClCompile Include="vk-module.cpp"> + <Filter>Source Files</Filter> + </ClCompile> + <ClCompile Include="vk-swap-chain.cpp"> + <Filter>Source Files</Filter> + </ClCompile> + <ClCompile Include="vk-device-queue.cpp"> + <Filter>Source Files</Filter> + </ClCompile> + <ClCompile Include="vk-util.cpp"> + <Filter>Source Files</Filter> + </ClCompile> </ItemGroup> <ItemGroup> <ClInclude Include="options.h"> @@ -101,5 +116,20 @@ <ClInclude Include="shader-renderer-util.h"> <Filter>Source Files</Filter> </ClInclude> + <ClInclude Include="vk-api.h"> + <Filter>Source Files</Filter> + </ClInclude> + <ClInclude Include="vk-module.h"> + <Filter>Source Files</Filter> + </ClInclude> + <ClInclude Include="vk-swap-chain.h"> + <Filter>Source Files</Filter> + </ClInclude> + <ClInclude Include="vk-device-queue.h"> + <Filter>Source Files</Filter> + </ClInclude> + <ClInclude Include="vk-util.h"> + <Filter>Source 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 index ef05d23b6..9999d1e82 100644 --- a/tools/render-test/render-vk.cpp +++ b/tools/render-test/render-vk.cpp @@ -6,84 +6,32 @@ #include "../../source/core/smart-pointer.h" -#ifdef _WIN32 -#define VK_USE_PLATFORM_WIN32_KHR 1 -#endif +#include "vk-api.h" +#include "vk-util.h" +#include "vk-device-queue.h" +#include "vk-swap-chain.h" -#define VK_NO_PROTOTYPES -#include <vulkan/vulkan.h> +// Vulkan has a different coordinate system to ogl +// http://anki3d.org/vulkan-coordinate-system/ #define ENABLE_VALIDATION_LAYER 1 - #ifdef _MSC_VER - -#include <stddef.h> - -#pragma warning(disable: 4996) - -#if (_MSC_VER < 1900) -#define snprintf sprintf_s +# include <stddef.h> +# pragma warning(disable: 4996) +# if (_MSC_VER < 1900) +# define snprintf sprintf_s +# endif #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 { using namespace Slang; -#define RETURN_ON_VK_FAIL(x) { VkResult _vkRes = x; if (_vkRes != VK_SUCCESS) { SLANG_RETURN_ON_FAIL(toSlangResult(_vkRes)); }} - class VKRenderer : public Renderer, public ShaderCompiler { public: + enum { kMaxRenderTargets = 8, kMaxAttachments = kMaxRenderTargets + 1 }; + // Renderer implementation virtual SlangResult initialize(void* inWindowHandle) override; virtual void setClearColor(const float color[4]) override; @@ -102,44 +50,56 @@ public: virtual void setBindingState(BindingState* state); virtual void setVertexBuffers(UInt startSlot, UInt slotCount, BufferResource*const* buffers, const UInt* strides, const UInt* offsets) override; virtual void setShaderProgram(ShaderProgram* inProgram) override; - virtual void setConstantBuffers(UInt startSlot, UInt slotCount, BufferResource*const* buffers, const UInt* offsets) override; virtual void draw(UInt vertexCount, UInt startVertex) override; virtual void dispatchCompute(int x, int y, int z) override; - virtual void submitGpuWork() override {} - virtual void waitForGpu() override {} + virtual void submitGpuWork() override; + virtual void waitForGpu() override; + virtual RendererType getRendererType() const override { return RendererType::Vulkan; } // ShaderCompiler implementation virtual ShaderProgram* compileProgram(const ShaderCompileRequest& request) override; + /// Dtor + ~VKRenderer(); + protected: class Buffer { public: - + /// Initialize a buffer with specified size, and memory props + Result init(const VulkanApi& api, size_t bufferSize, VkBufferUsageFlags usage, VkMemoryPropertyFlags reqMemoryProperties); + /// Returns true if has been initialized - bool isInitialized() const { return m_renderer != nullptr; } + bool isInitialized() const { return m_api != nullptr; } // Default Ctor Buffer(): - m_renderer(nullptr) + m_api(nullptr) {} /// Dtor ~Buffer() { - if (m_renderer) + if (m_api) { - m_renderer->vkDestroyBuffer(m_renderer->m_device, m_buffer, nullptr); - m_renderer->vkFreeMemory(m_renderer->m_device, m_memory, nullptr); + m_api->vkDestroyBuffer(m_api->m_device, m_buffer, nullptr); + m_api->vkFreeMemory(m_api->m_device, m_memory, nullptr); } } VkBuffer m_buffer; VkDeviceMemory m_memory; - VKRenderer* m_renderer; + const VulkanApi* m_api; }; + class InputLayoutImpl : public InputLayout + { + public: + List<VkVertexInputAttributeDescription> m_vertexDescs; + int m_vertexSize; + }; + class BufferResourceImpl: public BufferResource { public: @@ -166,6 +126,12 @@ public: { public: + ShaderProgramImpl(PipelineType pipelineType): + m_pipelineType(pipelineType) + {} + + PipelineType m_pipelineType; + VkPipelineShaderStageCreateInfo m_compute; VkPipelineShaderStageCreateInfo m_vertex; VkPipelineShaderStageCreateInfo m_fragment; @@ -195,185 +161,586 @@ public: VKRenderer* m_renderer; List<BindingDetail> m_bindingDetails; }; - - class InputLayoutImpl: public InputLayout + + struct BoundVertexBuffer { - public: + RefPtr<BufferResourceImpl> m_buffer; + int m_stride; + int m_offset; + }; + + class Pipeline : public RefObject + { + public: + Pipeline(const VulkanApi& api): + m_api(&api) + { + } + ~Pipeline() + { + if (m_pipeline != VK_NULL_HANDLE) + { + m_api->vkDestroyPipeline(m_api->m_device, m_pipeline, nullptr); + } + if (m_descriptorPool != VK_NULL_HANDLE) + { + m_api->vkDestroyDescriptorPool(m_api->m_device, m_descriptorPool, nullptr); + } + if (m_pipelineLayout != VK_NULL_HANDLE) + { + m_api->vkDestroyPipelineLayout(m_api->m_device, m_pipelineLayout, nullptr); + } + if(m_descriptorSetLayout != VK_NULL_HANDLE) + { + m_api->vkDestroyDescriptorSetLayout(m_api->m_device, m_descriptorSetLayout, nullptr); + } + } + + const VulkanApi* m_api; + + VkPrimitiveTopology m_primitiveTopology; + RefPtr<BindingStateImpl> m_bindingState; + RefPtr<InputLayoutImpl> m_inputLayout; + RefPtr<ShaderProgramImpl> m_shaderProgram; + + VkDescriptorSetLayout m_descriptorSetLayout = VK_NULL_HANDLE; + VkPipelineLayout m_pipelineLayout = VK_NULL_HANDLE; + VkDescriptorPool m_descriptorPool = VK_NULL_HANDLE; + VkDescriptorSet m_descriptorSet = VK_NULL_HANDLE; + VkPipeline m_pipeline = VK_NULL_HANDLE; }; VkBool32 handleDebugMessage(VkDebugReportFlagsEXT flags, VkDebugReportObjectTypeEXT objType, uint64_t srcObject, size_t location, int32_t msgCode, const char* pLayerPrefix, const char* pMsg); - VkCommandBuffer getCommandBuffer(); - VkCommandBuffer beginCommandBuffer(); - void flushCommandBuffer(VkCommandBuffer commandBuffer); - - uint32_t getMemoryTypeIndex(uint32_t inTypeBits, VkMemoryPropertyFlags properties); - VkPipelineShaderStageCreateInfo compileEntryPoint(const ShaderCompileRequest::EntryPoint& entryPointRequest, VkShaderStageFlagBits stage, List<char>& bufferOut); - SlangResult _initBuffer(size_t bufferSize, VkBufferUsageFlags usage, VkMemoryPropertyFlags reqMemoryProperties, Buffer& bufferOut); - - static SlangResult toSlangResult(VkResult res); - static void checkResult(VkResult result); 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); - VkInstance m_instance; - VkPhysicalDevice m_physicalDevice; - VkPhysicalDeviceProperties m_deviceProperties; - VkPhysicalDeviceFeatures m_deviceFeatures; - VkPhysicalDeviceMemoryProperties m_deviceMemoryProperties; - VkDevice m_device; - VkQueue m_queue; - VkCommandPool m_commandPool; - VkSubmitInfo m_submitInfo; - VkDebugReportCallbackEXT m_debugReportCallback; + /// Returns true if m_currentPipeline matches the current configuration + Pipeline* _getPipeline(); + bool _isEqual(const Pipeline& pipeline) const; + Slang::Result _createPipeline(RefPtr<Pipeline>& pipelineOut); + void _beginRender(); + void _endRender(); + Slang::Result _beginPass(); + void _endPass(); + + VkDebugReportCallbackEXT m_debugReportCallback; + + RefPtr<InputLayoutImpl> m_currentInputLayout; RefPtr<BindingStateImpl> m_currentBindingState; RefPtr<ShaderProgramImpl> m_currentProgram; - float m_clearColor[4] = { 0, 0, 0, 0 };; + List<RefPtr<Pipeline> > m_pipelineCache; + Pipeline* m_currentPipeline = nullptr; -#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 + List<BoundVertexBuffer> m_boundVertexBuffers; + VkPrimitiveTopology m_primitiveTopology = VK_PRIMITIVE_TOPOLOGY_TRIANGLE_LIST; + + VkDevice m_device = VK_NULL_HANDLE; + + VulkanModule m_module; + VulkanApi m_api; + + VulkanDeviceQueue m_deviceQueue; + VulkanSwapChain m_swapChain; + + VkRenderPass m_renderPass = VK_NULL_HANDLE; + + int m_swapChainImageIndex = -1; + + float m_clearColor[4] = { 0, 0, 0, 0 }; }; -Renderer* createVKRenderer() +/* !!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!! VkRenderer::Buffer !!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!! */ + +Result VKRenderer::Buffer::init(const VulkanApi& api, size_t bufferSize, VkBufferUsageFlags usage, VkMemoryPropertyFlags reqMemoryProperties) { - return new VKRenderer; + assert(!isInitialized()); + + m_api = &api; + m_memory = VK_NULL_HANDLE; + m_buffer = VK_NULL_HANDLE; + + VkBufferCreateInfo bufferCreateInfo = { VK_STRUCTURE_TYPE_BUFFER_CREATE_INFO }; + bufferCreateInfo.size = bufferSize; + bufferCreateInfo.usage = usage; + + SLANG_VK_CHECK(api.vkCreateBuffer(api.m_device, &bufferCreateInfo, nullptr, &m_buffer)); + + VkMemoryRequirements memoryReqs = {}; + api.vkGetBufferMemoryRequirements(api.m_device, m_buffer, &memoryReqs); + + int memoryTypeIndex = api.findMemoryTypeIndex(memoryReqs.memoryTypeBits, reqMemoryProperties); + assert(memoryTypeIndex >= 0); + + VkMemoryPropertyFlags actualMemoryProperites = api.m_deviceMemoryProperties.memoryTypes[memoryTypeIndex].propertyFlags; + + VkMemoryAllocateInfo allocateInfo = { VK_STRUCTURE_TYPE_MEMORY_ALLOCATE_INFO }; + allocateInfo.allocationSize = memoryReqs.size; + allocateInfo.memoryTypeIndex = memoryTypeIndex; + + SLANG_VK_CHECK(api.vkAllocateMemory(api.m_device, &allocateInfo, nullptr, &m_memory)); + SLANG_VK_CHECK(api.vkBindBufferMemory(api.m_device, m_buffer, m_memory, 0)); + + return SLANG_OK; } -/* static */SlangResult VKRenderer::toSlangResult(VkResult res) +/* !!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!! VkRenderer !!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!! */ + +bool VKRenderer::_isEqual(const Pipeline& pipeline) const { - return (res == VK_SUCCESS) ? SLANG_OK : SLANG_FAIL; + return + pipeline.m_bindingState == m_currentBindingState && + pipeline.m_primitiveTopology == m_primitiveTopology && + pipeline.m_inputLayout == m_currentInputLayout && + pipeline.m_shaderProgram == m_currentProgram; } -void VKRenderer::checkResult(VkResult result) +VKRenderer::Pipeline* VKRenderer::_getPipeline() { - assert(result == VK_SUCCESS); + if (m_currentPipeline && _isEqual(*m_currentPipeline)) + { + return m_currentPipeline; + } + + // Look for a match in the cache + for (int i = 0; i < int(m_pipelineCache.Count()); ++i) + { + Pipeline* pipeline = m_pipelineCache[i]; + if (_isEqual(*pipeline)) + { + m_currentPipeline = pipeline; + return pipeline; + } + } + + RefPtr<Pipeline> pipeline; + SLANG_RETURN_NULL_ON_FAIL(_createPipeline(pipeline)); + m_pipelineCache.Add(pipeline); + m_currentPipeline = pipeline; + return pipeline; } -VkBool32 VKRenderer::handleDebugMessage(VkDebugReportFlagsEXT flags, VkDebugReportObjectTypeEXT objType, uint64_t srcObject, - size_t location, int32_t msgCode, const char* pLayerPrefix, const char* pMsg) +Slang::Result VKRenderer::_createPipeline(RefPtr<Pipeline>& pipelineOut) { - char const* severity = "message"; - if (flags & VK_DEBUG_REPORT_WARNING_BIT_EXT) - severity = "warning"; - if (flags & VK_DEBUG_REPORT_ERROR_BIT_EXT) - severity = "error"; + RefPtr<Pipeline> pipeline(new Pipeline(m_api)); - char buffer[1024]; - sprintf_s(buffer, - "%s: %s %d: %s\n", - pLayerPrefix, - severity, - msgCode, - pMsg); + // Initialize the state + pipeline->m_primitiveTopology = m_primitiveTopology; + pipeline->m_bindingState = m_currentBindingState; + pipeline->m_shaderProgram = m_currentProgram; + pipeline->m_inputLayout = m_currentInputLayout; - fprintf(stderr, "%s", buffer); - fflush(stderr); + // Must be equal at this point if all the items are correctly set in pipeline + assert(_isEqual(*pipeline)); - OutputDebugStringA(buffer); + // First create a pipeline layout based on what is bound - return VK_FALSE; -} + const auto& srcDetails = m_currentBindingState->m_bindingDetails; + const auto& srcBindings = m_currentBindingState->getDesc().m_bindings; -/* static */VKAPI_ATTR VkBool32 VKAPI_CALL VKRenderer::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); -} + const int numBindings = int(srcBindings.Count()); -VkCommandBuffer VKRenderer::getCommandBuffer() -{ - VkCommandBufferAllocateInfo info = { VK_STRUCTURE_TYPE_COMMAND_BUFFER_ALLOCATE_INFO }; - info.commandPool = m_commandPool; - info.level = VK_COMMAND_BUFFER_LEVEL_PRIMARY; - info.commandBufferCount = 1; + Slang::List<VkDescriptorSetLayoutBinding> dstBindings; + for (int i = 0; i < numBindings; ++i) + { + const auto& srcDetail = srcDetails[i]; + const auto& srcBinding = srcBindings[i]; - VkCommandBuffer commandBuffer; - checkResult(vkAllocateCommandBuffers(m_device, &info, &commandBuffer)); + switch (srcBinding.bindingType) + { + case BindingType::Buffer: + { + BufferResourceImpl* bufferResource = static_cast<BufferResourceImpl*>(srcBinding.resource.Ptr()); + const BufferResource::Desc& bufferResourceDesc = bufferResource->getDesc(); - return commandBuffer; -} + if (bufferResourceDesc.bindFlags & Resource::BindFlag::UnorderedAccess) + { + VkDescriptorSetLayoutBinding dstBinding = {}; + dstBinding.binding = srcDetail.m_binding; + dstBinding.descriptorCount = 1; + dstBinding.descriptorType = VK_DESCRIPTOR_TYPE_STORAGE_BUFFER; + dstBinding.stageFlags = VK_SHADER_STAGE_ALL; -VkCommandBuffer VKRenderer::beginCommandBuffer() -{ - VkCommandBuffer commandBuffer = getCommandBuffer(); + dstBindings.Add(dstBinding); + } + else if (bufferResourceDesc.bindFlags & Resource::BindFlag::ConstantBuffer) + { + VkDescriptorSetLayoutBinding dstBinding = {}; + dstBinding.binding = srcDetail.m_binding; + dstBinding.descriptorCount = 1; + dstBinding.descriptorType = VK_DESCRIPTOR_TYPE_UNIFORM_BUFFER; + dstBinding.stageFlags = VK_SHADER_STAGE_ALL; - VkCommandBufferBeginInfo beginInfo = { VK_STRUCTURE_TYPE_COMMAND_BUFFER_BEGIN_INFO }; - checkResult(vkBeginCommandBuffer(commandBuffer, &beginInfo)); + dstBindings.Add(dstBinding); + } - return commandBuffer; -} + break; + } + default: + // TODO: handle the other cases + break; + } + } -void VKRenderer::flushCommandBuffer(VkCommandBuffer commandBuffer) -{ - checkResult(vkEndCommandBuffer(commandBuffer)); + VkDescriptorSetLayoutCreateInfo descriptorSetLayoutInfo = { VK_STRUCTURE_TYPE_DESCRIPTOR_SET_LAYOUT_CREATE_INFO }; + descriptorSetLayoutInfo.bindingCount = uint32_t(dstBindings.Count()); + descriptorSetLayoutInfo.pBindings = dstBindings.Buffer(); - VkSubmitInfo submitInfo = { VK_STRUCTURE_TYPE_SUBMIT_INFO }; - submitInfo.commandBufferCount = 1; - submitInfo.pCommandBuffers = &commandBuffer; + SLANG_VK_CHECK(m_api.vkCreateDescriptorSetLayout(m_device, &descriptorSetLayoutInfo, nullptr, &pipeline->m_descriptorSetLayout)); - checkResult(vkQueueSubmit(m_queue, 1, &submitInfo, VK_NULL_HANDLE)); - checkResult(vkQueueWaitIdle(m_queue)); + // 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 }, + }; - vkFreeCommandBuffers(m_device, m_commandPool, 1, &commandBuffer); -} + VkDescriptorPoolCreateInfo descriptorPoolInfo = { VK_STRUCTURE_TYPE_DESCRIPTOR_POOL_CREATE_INFO }; + descriptorPoolInfo.maxSets = 128; // TODO: actually pick a size + descriptorPoolInfo.poolSizeCount = SLANG_COUNT_OF(poolSizes); + descriptorPoolInfo.pPoolSizes = poolSizes; -SlangResult VKRenderer::_initBuffer(size_t bufferSize, VkBufferUsageFlags usage, VkMemoryPropertyFlags reqMemoryProperties, Buffer& bufferOut) -{ - assert(!bufferOut.isInitialized()); + SLANG_VK_CHECK(m_api.vkCreateDescriptorPool(m_device, &descriptorPoolInfo, nullptr, &pipeline->m_descriptorPool)); - bufferOut.m_renderer = this; - bufferOut.m_memory = 0; - bufferOut.m_buffer = 0; - - VkBufferCreateInfo bufferCreateInfo = { VK_STRUCTURE_TYPE_BUFFER_CREATE_INFO }; - bufferCreateInfo.size = bufferSize; - bufferCreateInfo.usage = usage; + // Create a descriptor set based on our layout - checkResult(vkCreateBuffer(m_device, &bufferCreateInfo, nullptr, &bufferOut.m_buffer)); + VkDescriptorSetAllocateInfo descriptorSetAllocInfo = { VK_STRUCTURE_TYPE_DESCRIPTOR_SET_ALLOCATE_INFO }; + descriptorSetAllocInfo.descriptorPool = pipeline->m_descriptorPool; + descriptorSetAllocInfo.descriptorSetCount = 1; + descriptorSetAllocInfo.pSetLayouts = &pipeline->m_descriptorSetLayout; - VkMemoryRequirements memoryReqs = {}; - vkGetBufferMemoryRequirements(m_device, bufferOut.m_buffer, &memoryReqs); + SLANG_VK_CHECK(m_api.vkAllocateDescriptorSets(m_device, &descriptorSetAllocInfo, &pipeline->m_descriptorSet)); - uint32_t memoryTypeIndex = getMemoryTypeIndex(memoryReqs.memoryTypeBits, reqMemoryProperties); + // Fill in the descriptor set, using our binding information - VkMemoryPropertyFlags actualMemoryProperites = m_deviceMemoryProperties.memoryTypes[memoryTypeIndex].propertyFlags; + int elementIndex = 0; - VkMemoryAllocateInfo allocateInfo = { VK_STRUCTURE_TYPE_MEMORY_ALLOCATE_INFO }; - allocateInfo.allocationSize = memoryReqs.size; - allocateInfo.memoryTypeIndex = memoryTypeIndex; + for (int i = 0; i < numBindings; ++i) + { + const auto& srcDetail = srcDetails[i]; + const auto& srcBinding = srcBindings[i]; + + switch (srcBinding.bindingType) + { + case BindingType::Buffer: + { + BufferResourceImpl* bufferResource = static_cast<BufferResourceImpl*>(srcBinding.resource.Ptr()); + const BufferResource::Desc& bufferResourceDesc = bufferResource->getDesc(); + + VkDescriptorBufferInfo bufferInfo; + bufferInfo.buffer = bufferResource->m_buffer.m_buffer; + bufferInfo.offset = 0; + bufferInfo.range = bufferResourceDesc.sizeInBytes; + + VkWriteDescriptorSet writeInfo = { VK_STRUCTURE_TYPE_WRITE_DESCRIPTOR_SET }; + writeInfo.descriptorCount = 1; + + writeInfo.descriptorType = VK_DESCRIPTOR_TYPE_STORAGE_BUFFER; + if (bufferResource->m_initialUsage == Resource::Usage::UnorderedAccess) + { + writeInfo.descriptorType = VK_DESCRIPTOR_TYPE_STORAGE_BUFFER; + } + else if (bufferResource->m_initialUsage == Resource::Usage::ConstantBuffer) + { + writeInfo.descriptorType = VK_DESCRIPTOR_TYPE_UNIFORM_BUFFER; + } - checkResult(vkAllocateMemory(m_device, &allocateInfo, nullptr, &bufferOut.m_memory)); - checkResult(vkBindBufferMemory(m_device, bufferOut.m_buffer, bufferOut.m_memory, 0)); + writeInfo.dstSet = pipeline->m_descriptorSet; + writeInfo.dstBinding = srcDetail.m_binding; + writeInfo.dstArrayElement = 0; + writeInfo.pBufferInfo = &bufferInfo; + + m_api.vkUpdateDescriptorSets(m_device, 1, &writeInfo, 0, nullptr); + + break; + } + default: + { + // handle 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 = &pipeline->m_descriptorSetLayout; + + SLANG_VK_CHECK(m_api.vkCreatePipelineLayout(m_device, &pipelineLayoutInfo, nullptr, &pipeline->m_pipelineLayout)); + + VkPipelineCache pipelineCache = VK_NULL_HANDLE; + + if (m_currentProgram->m_pipelineType == PipelineType::Compute) + { + // Then create a pipeline to use that layout + + VkComputePipelineCreateInfo computePipelineInfo = { VK_STRUCTURE_TYPE_COMPUTE_PIPELINE_CREATE_INFO }; + computePipelineInfo.stage = m_currentProgram->m_compute; + computePipelineInfo.layout = pipeline->m_pipelineLayout; + + SLANG_VK_CHECK(m_api.vkCreateComputePipelines(m_device, pipelineCache, 1, &computePipelineInfo, nullptr, &pipeline->m_pipeline)); + } + else if (m_currentProgram->m_pipelineType == PipelineType::Graphics) + { + // Create the graphics pipeline + + const int width = m_swapChain.getWidth(); + const int height = m_swapChain.getHeight(); + + VkPipelineShaderStageCreateInfo shaderStages[] = { m_currentProgram->m_vertex, m_currentProgram->m_fragment }; + + // VertexBuffer/s + // Currently only handles one + + VkPipelineVertexInputStateCreateInfo vertexInputInfo = { VK_STRUCTURE_TYPE_PIPELINE_VERTEX_INPUT_STATE_CREATE_INFO }; + vertexInputInfo.sType = VK_STRUCTURE_TYPE_PIPELINE_VERTEX_INPUT_STATE_CREATE_INFO; + vertexInputInfo.vertexBindingDescriptionCount = 0; + vertexInputInfo.vertexAttributeDescriptionCount = 0; + + VkVertexInputBindingDescription vertexInputBindingDescription; + + if (m_currentInputLayout) + { + vertexInputBindingDescription.binding = 0; + vertexInputBindingDescription.stride = m_currentInputLayout->m_vertexSize; + vertexInputBindingDescription.inputRate = VK_VERTEX_INPUT_RATE_VERTEX; + + const auto& srcAttributeDescs = m_currentInputLayout->m_vertexDescs; + + vertexInputInfo.vertexBindingDescriptionCount = 1; + vertexInputInfo.pVertexBindingDescriptions = &vertexInputBindingDescription; + + vertexInputInfo.vertexAttributeDescriptionCount = static_cast<uint32_t>(srcAttributeDescs.Count()); + vertexInputInfo.pVertexAttributeDescriptions = srcAttributeDescs.Buffer(); + } + + // + + VkPipelineInputAssemblyStateCreateInfo inputAssembly = {}; + inputAssembly.sType = VK_STRUCTURE_TYPE_PIPELINE_INPUT_ASSEMBLY_STATE_CREATE_INFO; + inputAssembly.topology = VK_PRIMITIVE_TOPOLOGY_TRIANGLE_LIST; + inputAssembly.primitiveRestartEnable = VK_FALSE; + + VkViewport viewport = {}; + viewport.x = 0.0f; + viewport.y = 0.0f; + viewport.width = (float)width; + viewport.height = (float)height; + viewport.minDepth = 0.0f; + viewport.maxDepth = 1.0f; + + VkRect2D scissor = {}; + scissor.offset = { 0, 0 }; + scissor.extent = { uint32_t(width), uint32_t(height) }; + + VkPipelineViewportStateCreateInfo viewportState = {}; + viewportState.sType = VK_STRUCTURE_TYPE_PIPELINE_VIEWPORT_STATE_CREATE_INFO; + viewportState.viewportCount = 1; + viewportState.pViewports = &viewport; + viewportState.scissorCount = 1; + viewportState.pScissors = &scissor; + + VkPipelineRasterizationStateCreateInfo rasterizer = {}; + rasterizer.sType = VK_STRUCTURE_TYPE_PIPELINE_RASTERIZATION_STATE_CREATE_INFO; + rasterizer.depthClampEnable = VK_FALSE; + rasterizer.rasterizerDiscardEnable = VK_FALSE; + rasterizer.polygonMode = VK_POLYGON_MODE_FILL; + rasterizer.lineWidth = 1.0f; + rasterizer.cullMode = VK_CULL_MODE_NONE; + rasterizer.frontFace = VK_FRONT_FACE_CLOCKWISE; + rasterizer.depthBiasEnable = VK_FALSE; + + VkPipelineMultisampleStateCreateInfo multisampling = {}; + multisampling.sType = VK_STRUCTURE_TYPE_PIPELINE_MULTISAMPLE_STATE_CREATE_INFO; + multisampling.sampleShadingEnable = VK_FALSE; + multisampling.rasterizationSamples = VK_SAMPLE_COUNT_1_BIT; + + VkPipelineColorBlendAttachmentState colorBlendAttachment = {}; + colorBlendAttachment.colorWriteMask = VK_COLOR_COMPONENT_R_BIT | VK_COLOR_COMPONENT_G_BIT | VK_COLOR_COMPONENT_B_BIT | VK_COLOR_COMPONENT_A_BIT; + colorBlendAttachment.blendEnable = VK_FALSE; + + VkPipelineColorBlendStateCreateInfo colorBlending = {}; + colorBlending.sType = VK_STRUCTURE_TYPE_PIPELINE_COLOR_BLEND_STATE_CREATE_INFO; + colorBlending.logicOpEnable = VK_FALSE; + colorBlending.logicOp = VK_LOGIC_OP_COPY; + colorBlending.attachmentCount = 1; + colorBlending.pAttachments = &colorBlendAttachment; + colorBlending.blendConstants[0] = 0.0f; + colorBlending.blendConstants[1] = 0.0f; + colorBlending.blendConstants[2] = 0.0f; + colorBlending.blendConstants[3] = 0.0f; + + VkGraphicsPipelineCreateInfo pipelineInfo = { VK_STRUCTURE_TYPE_GRAPHICS_PIPELINE_CREATE_INFO }; + + pipelineInfo.sType = VK_STRUCTURE_TYPE_GRAPHICS_PIPELINE_CREATE_INFO; + pipelineInfo.stageCount = 2; + pipelineInfo.pStages = shaderStages; + pipelineInfo.pVertexInputState = &vertexInputInfo; + pipelineInfo.pInputAssemblyState = &inputAssembly; + pipelineInfo.pViewportState = &viewportState; + pipelineInfo.pRasterizationState = &rasterizer; + pipelineInfo.pMultisampleState = &multisampling; + pipelineInfo.pColorBlendState = &colorBlending; + pipelineInfo.layout = pipeline->m_pipelineLayout; + pipelineInfo.renderPass = m_renderPass; + pipelineInfo.subpass = 0; + pipelineInfo.basePipelineHandle = VK_NULL_HANDLE; + + SLANG_VK_CHECK(m_api.vkCreateGraphicsPipelines(m_device, pipelineCache, 1, &pipelineInfo, nullptr, &pipeline->m_pipeline)); + } + else + { + assert(!"Unhandled program type"); + return SLANG_FAIL; + } + + pipelineOut = pipeline; return SLANG_OK; } -uint32_t VKRenderer::getMemoryTypeIndex(uint32_t inTypeBits, VkMemoryPropertyFlags properties) +Result VKRenderer::_beginPass() { - uint32_t typeBits = inTypeBits; - uint32_t typeIndex = 0; - while (typeBits) + if (m_swapChainImageIndex < 0) { - if ((m_deviceMemoryProperties.memoryTypes[typeIndex].propertyFlags & properties) == properties) + return SLANG_FAIL; + } + + const int numRenderTargets = 1; + + const VulkanSwapChain::Image& image = m_swapChain.getImages()[m_swapChainImageIndex]; + + int numAttachments = 0; + + // Start render pass + VkClearValue clearValues[kMaxAttachments]; + clearValues[numAttachments++] = VkClearValue{ m_clearColor[0], m_clearColor[1], m_clearColor[2], m_clearColor[3] }; + + bool hasDepthBuffer = false; + if (hasDepthBuffer) + { + VkClearValue& clearValue = clearValues[numAttachments++]; + + clearValue.depthStencil.depth = 1.0f; + clearValue.depthStencil.stencil = 0; + } + + const int width = m_swapChain.getWidth(); + const int height = m_swapChain.getHeight(); + + VkCommandBuffer cmdBuffer = m_deviceQueue.getCommandBuffer(); + + VkRenderPassBeginInfo renderPassBegin = {}; + renderPassBegin.sType = VK_STRUCTURE_TYPE_RENDER_PASS_BEGIN_INFO; + renderPassBegin.renderPass = m_renderPass; + renderPassBegin.framebuffer = image.m_frameBuffer; + renderPassBegin.renderArea.offset.x = 0; + renderPassBegin.renderArea.offset.y = 0; + renderPassBegin.renderArea.extent.width = width; + renderPassBegin.renderArea.extent.height = height; + renderPassBegin.clearValueCount = numAttachments; + renderPassBegin.pClearValues = clearValues; + + m_api.vkCmdBeginRenderPass(cmdBuffer, &renderPassBegin, VK_SUBPASS_CONTENTS_INLINE); + + // Set up scissor and viewport + { + VkRect2D rects[kMaxRenderTargets] = {}; + VkViewport viewports[kMaxRenderTargets] = {}; + for (int i = 0; i < numRenderTargets; ++i) { - return typeIndex; + rects[i] = VkRect2D{ 0, 0, uint32_t(width), uint32_t(height) }; + + VkViewport& dstViewport = viewports[i]; + + dstViewport.x = 0.0f; + dstViewport.y = 0.0f; + dstViewport.width = float(width); + dstViewport.height = float(height); + dstViewport.minDepth = 0.0f; + dstViewport.maxDepth = 1.0f; } - typeIndex++; - typeBits >>= 1; + + m_api.vkCmdSetScissor(cmdBuffer, 0, numRenderTargets, rects); + m_api.vkCmdSetViewport(cmdBuffer, 0, numRenderTargets, viewports); + } + + return SLANG_OK; +} + +void VKRenderer::_endPass() +{ + VkCommandBuffer cmdBuffer = m_deviceQueue.getCommandBuffer(); + m_api.vkCmdEndRenderPass(cmdBuffer); +} + +void VKRenderer::_beginRender() +{ + m_swapChainImageIndex = m_swapChain.nextFrontImageIndex(); + + if (m_swapChainImageIndex < 0) + { + return; } +} - assert(!"failed to find a usable memory type"); - return uint32_t(-1); +void VKRenderer::_endRender() +{ + m_deviceQueue.flush(); +} + +Renderer* createVKRenderer() +{ + return new VKRenderer; +} + +VKRenderer::~VKRenderer() +{ + if (m_renderPass != VK_NULL_HANDLE) + { + m_api.vkDestroyRenderPass(m_device, m_renderPass, nullptr); + m_renderPass = VK_NULL_HANDLE; + } +} + + +VkBool32 VKRenderer::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 VKRenderer::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); } VkPipelineShaderStageCreateInfo VKRenderer::compileEntryPoint(const ShaderCompileRequest::EntryPoint& entryPointRequest, VkShaderStageFlagBits stage, List<char>& bufferOut) @@ -394,15 +761,14 @@ VkPipelineShaderStageCreateInfo VKRenderer::compileEntryPoint(const ShaderCompil moduleCreateInfo.codeSize = codeSize; VkShaderModule module; - checkResult(vkCreateShaderModule(m_device, &moduleCreateInfo, nullptr, &module)); - - //::free(codeBegin); + SLANG_VK_CHECK(m_api.vkCreateShaderModule(m_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; } @@ -410,31 +776,22 @@ VkPipelineShaderStageCreateInfo VKRenderer::compileEntryPoint(const ShaderCompil SlangResult VKRenderer::initialize(void* inWindowHandle) { - char const* dynamicLibraryName = "vulkan-1.dll"; - HMODULE vulkan = LoadLibraryA(dynamicLibraryName); - if (!vulkan) - { - fprintf(stderr, "error: failed load '%s'\n", dynamicLibraryName); - return SLANG_FAIL; - } - - vkGetInstanceProcAddr = (PFN_vkGetInstanceProcAddr)GetProcAddress(vulkan, "vkGetInstanceProcAddr"); - if (!vkGetInstanceProcAddr) - { - fprintf(stderr, "error: failed load symbol 'vkGetInstanceProcAddr'\n"); - return SLANG_FAIL; - } + SLANG_RETURN_ON_FAIL(m_module.init()); + SLANG_RETURN_ON_FAIL(m_api.initGlobalProcs(m_module)); 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[] = { + char const* instanceExtensions[] = + { VK_KHR_SURFACE_EXTENSION_NAME, -#ifdef _WIN32 + +#if SLANG_WINDOWS_FAMILY VK_KHR_WIN32_SURFACE_EXTENSION_NAME, #else + VK_KHR_XLIB_SURFACE_EXTENSION_NAME #endif #if ENABLE_VALIDATION_LAYER @@ -442,35 +799,22 @@ SlangResult VKRenderer::initialize(void* inWindowHandle) #endif }; + VkInstance instance = VK_NULL_HANDLE; + VkInstanceCreateInfo instanceCreateInfo = { VK_STRUCTURE_TYPE_INSTANCE_CREATE_INFO }; instanceCreateInfo.pApplicationInfo = &applicationInfo; - instanceCreateInfo.enabledExtensionCount = sizeof(instanceExtensions) / sizeof(instanceExtensions[0]); + instanceCreateInfo.enabledExtensionCount = SLANG_COUNT_OF(instanceExtensions); instanceCreateInfo.ppEnabledExtensionNames = &instanceExtensions[0]; #if ENABLE_VALIDATION_LAYER - - uint32_t layerCount = 1; - const char *layerNames[] = { - "VK_LAYER_LUNARG_standard_validation" - }; - - instanceCreateInfo.enabledLayerCount = layerCount; + const char* layerNames[] = { "VK_LAYER_LUNARG_standard_validation" }; + instanceCreateInfo.enabledLayerCount = SLANG_COUNT_OF(layerNames); instanceCreateInfo.ppEnabledLayerNames = layerNames; #endif - m_instance = 0; - -#define LOAD_INSTANCE_PROC(NAME) NAME = (PFN_##NAME) vkGetInstanceProcAddr(m_instance, #NAME); - - FOREACH_GLOBAL_PROC(LOAD_INSTANCE_PROC); - - RETURN_ON_VK_FAIL(vkCreateInstance(&instanceCreateInfo, nullptr, &m_instance)); - - FOREACH_INSTANCE_PROC(LOAD_INSTANCE_PROC); - -#undef LOAD_INSTANCE_PROC - + SLANG_VK_RETURN_ON_FAIL(m_api.vkCreateInstance(&instanceCreateInfo, nullptr, &instance)); + SLANG_RETURN_ON_FAIL(m_api.initInstanceProcs(instance)); #if ENABLE_VALIDATION_LAYER VkDebugReportFlagsEXT debugFlags = VK_DEBUG_REPORT_ERROR_BIT_EXT | VK_DEBUG_REPORT_WARNING_BIT_EXT; @@ -480,43 +824,23 @@ SlangResult VKRenderer::initialize(void* inWindowHandle) debugCreateInfo.pUserData = this; debugCreateInfo.flags = debugFlags; - RETURN_ON_VK_FAIL(vkCreateDebugReportCallbackEXT(m_instance, &debugCreateInfo, nullptr, &m_debugReportCallback)); - + SLANG_VK_RETURN_ON_FAIL(m_api.vkCreateDebugReportCallbackEXT(instance, &debugCreateInfo, nullptr, &m_debugReportCallback)); #endif - uint32_t physicalDeviceCount = 0; - RETURN_ON_VK_FAIL(vkEnumeratePhysicalDevices(m_instance, &physicalDeviceCount, nullptr)); + uint32_t numPhysicalDevices = 0; + SLANG_VK_RETURN_ON_FAIL(m_api.vkEnumeratePhysicalDevices(instance, &numPhysicalDevices, nullptr)); - VkPhysicalDevice* physicalDevices = (VkPhysicalDevice*)alloca(physicalDeviceCount * sizeof(VkPhysicalDevice)); - RETURN_ON_VK_FAIL(vkEnumeratePhysicalDevices(m_instance, &physicalDeviceCount, physicalDevices)); + List<VkPhysicalDevice> physicalDevices; + physicalDevices.SetSize(numPhysicalDevices); + SLANG_VK_RETURN_ON_FAIL(m_api.vkEnumeratePhysicalDevices(instance, &numPhysicalDevices, physicalDevices.Buffer())); - uint32_t selectedDeviceIndex = 0; // TODO: allow override of selected device - m_physicalDevice = physicalDevices[selectedDeviceIndex]; - - vkGetPhysicalDeviceProperties(m_physicalDevice, &m_deviceProperties); - vkGetPhysicalDeviceFeatures(m_physicalDevice, &m_deviceFeatures); - vkGetPhysicalDeviceMemoryProperties(m_physicalDevice, &m_deviceMemoryProperties); - - uint32_t queueFamilyCount = 0; - vkGetPhysicalDeviceQueueFamilyProperties(m_physicalDevice, &queueFamilyCount, nullptr); - - VkQueueFamilyProperties* queueFamilies = (VkQueueFamilyProperties*)alloca(queueFamilyCount * sizeof(VkQueueFamilyProperties)); - vkGetPhysicalDeviceQueueFamilyProperties(m_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); + uint32_t selectedDeviceIndex = 0; + + SLANG_RETURN_ON_FAIL(m_api.initPhysicalDevice(physicalDevices[selectedDeviceIndex])); + + int queueFamilyIndex = m_api.findQueue(VK_QUEUE_GRAPHICS_BIT | VK_QUEUE_COMPUTE_BIT); + assert(queueFamilyIndex >= 0); float queuePriority = 0.0f; VkDeviceQueueCreateInfo queueCreateInfo = { VK_STRUCTURE_TYPE_DEVICE_QUEUE_CREATE_INFO }; @@ -532,48 +856,136 @@ SlangResult VKRenderer::initialize(void* inWindowHandle) VkDeviceCreateInfo deviceCreateInfo = { VK_STRUCTURE_TYPE_DEVICE_CREATE_INFO }; deviceCreateInfo.queueCreateInfoCount = 1; deviceCreateInfo.pQueueCreateInfos = &queueCreateInfo; - deviceCreateInfo.pEnabledFeatures = &m_deviceFeatures; - - deviceCreateInfo.enabledExtensionCount = sizeof(deviceExtensions) / sizeof(deviceExtensions[0]); - deviceCreateInfo.ppEnabledExtensionNames = &deviceExtensions[0]; + deviceCreateInfo.pEnabledFeatures = &m_api.m_deviceFeatures; - RETURN_ON_VK_FAIL(vkCreateDevice(m_physicalDevice, &deviceCreateInfo, nullptr, &m_device)); + deviceCreateInfo.enabledExtensionCount = SLANG_COUNT_OF(deviceExtensions); + deviceCreateInfo.ppEnabledExtensionNames = deviceExtensions; -#define LOAD_DEVICE_PROC(NAME) NAME = (PFN_##NAME) vkGetDeviceProcAddr(m_device, #NAME); - FOREACH_DEVICE_PROC(LOAD_DEVICE_PROC) -#undef LOAD_DEVICE_PROC + SLANG_VK_RETURN_ON_FAIL(m_api.vkCreateDevice(m_api.m_physicalDevice, &deviceCreateInfo, nullptr, &m_device)); + SLANG_RETURN_ON_FAIL(m_api.initDeviceProcs(m_device)); - // 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; - - RETURN_ON_VK_FAIL(vkCreateCommandPool(m_device, &commandPoolCreateInfo, nullptr, &m_commandPool)); - - vkGetDeviceQueue(m_device, queueFamilyIndex, 0, &m_queue); + { + VkQueue queue; + m_api.vkGetDeviceQueue(m_device, queueFamilyIndex, 0, &queue); + SLANG_RETURN_ON_FAIL(m_deviceQueue.init(m_api, queue, queueFamilyIndex)); + } // set up swap chain + { + VulkanSwapChain::Desc desc; + VulkanSwapChain::PlatformDesc* platformDesc = nullptr; + + desc.init(); + desc.m_format = Format::RGBA_Unorm_UInt8; +#if SLANG_WINDOWS_FAMILY + VulkanSwapChain::WinPlatformDesc winPlatformDesc; + winPlatformDesc.m_hinstance = ::GetModuleHandle(nullptr); + winPlatformDesc.m_hwnd = (HWND)inWindowHandle; + platformDesc = &winPlatformDesc; +#endif - // create command buffers + SLANG_RETURN_ON_FAIL(m_swapChain.init(&m_deviceQueue, desc, platformDesc)); + } // depth/stencil? // render pass? - // pipeline cache + { + const int numRenderTargets = 1; + bool shouldClear = true; + bool shouldClearDepth = false; + bool shouldClearStencil = false; + bool hasDepthBuffer = false; + + Format depthFormat = Format::Unknown; + VkFormat colorFormat = m_swapChain.getVkFormat(); + + int numAttachments = 0; + // We need extra space if we have depth buffer + VkAttachmentDescription attachmentDesc[kMaxRenderTargets + 1] = {}; + for (int i = 0; i < numRenderTargets; ++i) + { + VkAttachmentDescription& dst = attachmentDesc[numAttachments ++]; + + dst.flags = 0; + dst.format = colorFormat; + dst.samples = VK_SAMPLE_COUNT_1_BIT; + dst.loadOp = shouldClear ? VK_ATTACHMENT_LOAD_OP_CLEAR : VK_ATTACHMENT_LOAD_OP_LOAD; + dst.storeOp = VK_ATTACHMENT_STORE_OP_STORE; + dst.stencilLoadOp = VK_ATTACHMENT_LOAD_OP_DONT_CARE; + dst.stencilStoreOp = VK_ATTACHMENT_STORE_OP_DONT_CARE; + dst.initialLayout = VK_IMAGE_LAYOUT_UNDEFINED; // VK_IMAGE_LAYOUT_PRESENT_SRC_KHR; + dst.finalLayout = VK_IMAGE_LAYOUT_PRESENT_SRC_KHR; + } + if (hasDepthBuffer) + { + VkAttachmentDescription& dst = attachmentDesc[numAttachments++]; + + dst.flags = 0; + dst.format = VulkanUtil::getVkFormat(depthFormat); + dst.samples = VK_SAMPLE_COUNT_1_BIT; + dst.loadOp = shouldClearDepth ? VK_ATTACHMENT_LOAD_OP_CLEAR : VK_ATTACHMENT_LOAD_OP_LOAD; + dst.storeOp = VK_ATTACHMENT_STORE_OP_STORE; + dst.stencilLoadOp = shouldClearStencil ? VK_ATTACHMENT_LOAD_OP_CLEAR : VK_ATTACHMENT_LOAD_OP_LOAD; + dst.stencilStoreOp = VK_ATTACHMENT_STORE_OP_STORE; + dst.initialLayout = VK_IMAGE_LAYOUT_DEPTH_STENCIL_ATTACHMENT_OPTIMAL; + dst.finalLayout = VK_IMAGE_LAYOUT_DEPTH_STENCIL_ATTACHMENT_OPTIMAL; + } - // frame buffer + VkAttachmentReference colorAttachments[kMaxRenderTargets] = {}; + for (int i = 0; i < numRenderTargets; ++i) + { + VkAttachmentReference& dst = colorAttachments[i]; + dst.attachment = i; + dst.layout = VK_IMAGE_LAYOUT_COLOR_ATTACHMENT_OPTIMAL; + } + VkAttachmentReference depthAttachment = {}; + depthAttachment.attachment = numRenderTargets; + depthAttachment.layout = VK_IMAGE_LAYOUT_DEPTH_STENCIL_ATTACHMENT_OPTIMAL; + + VkSubpassDescription subpassDesc = {}; + subpassDesc.flags = 0; + subpassDesc.pipelineBindPoint = VK_PIPELINE_BIND_POINT_GRAPHICS; + subpassDesc.inputAttachmentCount = 0u; + subpassDesc.pInputAttachments = nullptr; + subpassDesc.colorAttachmentCount = numRenderTargets; + subpassDesc.pColorAttachments = colorAttachments; + subpassDesc.pResolveAttachments = nullptr; + subpassDesc.pDepthStencilAttachment = hasDepthBuffer ? &depthAttachment : nullptr; + subpassDesc.preserveAttachmentCount = 0u; + subpassDesc.pPreserveAttachments = nullptr; + + VkRenderPassCreateInfo renderPassCreateInfo = {}; + renderPassCreateInfo.sType = VK_STRUCTURE_TYPE_RENDER_PASS_CREATE_INFO; + renderPassCreateInfo.attachmentCount = numAttachments; + renderPassCreateInfo.pAttachments = attachmentDesc; + renderPassCreateInfo.subpassCount = 1; + renderPassCreateInfo.pSubpasses = &subpassDesc; + SLANG_VK_RETURN_ON_FAIL(m_api.vkCreateRenderPass(m_device, &renderPassCreateInfo, nullptr, &m_renderPass)); + } + // frame buffer + SLANG_RETURN_ON_FAIL(m_swapChain.createFrameBuffers(m_renderPass)); - // create semaphores for sync + _beginRender(); return SLANG_OK; } +void VKRenderer::submitGpuWork() +{ + m_deviceQueue.flush(); +} + +void VKRenderer::waitForGpu() +{ + m_deviceQueue.flushAndWait(); +} + void VKRenderer::setClearColor(const float color[4]) { for (int ii = 0; ii < 4; ++ii) @@ -585,7 +997,13 @@ void VKRenderer::clearFrame() } void VKRenderer::presentFrame() -{ +{ + _endRender(); + + const bool vsync = true; + m_swapChain.present(vsync); + + _beginRender(); } SlangResult VKRenderer::captureScreenShot(char const* outputPath) @@ -672,11 +1090,11 @@ BufferResource* VKRenderer::createBufferResource(Resource::Usage initialUsage, c } RefPtr<BufferResourceImpl> buffer(new BufferResourceImpl(initialUsage, desc, this)); - SLANG_RETURN_NULL_ON_FAIL(_initBuffer(desc.sizeInBytes, usage, reqMemoryProperties, buffer->m_buffer)); + SLANG_RETURN_NULL_ON_FAIL(buffer->m_buffer.init(m_api, desc.sizeInBytes, usage, reqMemoryProperties)); if ((desc.cpuAccessFlags & Resource::AccessFlag::Write) || initData) { - SLANG_RETURN_NULL_ON_FAIL(_initBuffer(bufferSize, VK_BUFFER_USAGE_TRANSFER_SRC_BIT, VK_MEMORY_PROPERTY_HOST_VISIBLE_BIT | VK_MEMORY_PROPERTY_HOST_COHERENT_BIT, buffer->m_uploadBuffer)); + SLANG_RETURN_NULL_ON_FAIL(buffer->m_uploadBuffer.init(m_api, bufferSize, VK_BUFFER_USAGE_TRANSFER_SRC_BIT, VK_MEMORY_PROPERTY_HOST_VISIBLE_BIT | VK_MEMORY_PROPERTY_HOST_COHERENT_BIT)); } if (initData) @@ -686,30 +1104,57 @@ BufferResource* VKRenderer::createBufferResource(Resource::Usage initialUsage, c // directly. // Copy into staging buffer void* mappedData = nullptr; - checkResult(vkMapMemory(m_device, buffer->m_uploadBuffer.m_memory, 0, bufferSize, 0, &mappedData)); - memcpy(mappedData, initData, bufferSize); - vkUnmapMemory(m_device, buffer->m_uploadBuffer.m_memory); + SLANG_VK_CHECK(m_api.vkMapMemory(m_device, buffer->m_uploadBuffer.m_memory, 0, bufferSize, 0, &mappedData)); + ::memcpy(mappedData, initData, bufferSize); + m_api.vkUnmapMemory(m_device, buffer->m_uploadBuffer.m_memory); // Copy from staging buffer to real buffer - VkCommandBuffer commandBuffer = beginCommandBuffer(); - + VkCommandBuffer commandBuffer = m_deviceQueue.getCommandBuffer(); + VkBufferCopy copyInfo = {}; copyInfo.size = bufferSize; - vkCmdCopyBuffer(commandBuffer, buffer->m_uploadBuffer.m_buffer, buffer->m_buffer.m_buffer, 1, ©Info); + m_api.vkCmdCopyBuffer(commandBuffer, buffer->m_uploadBuffer.m_buffer, buffer->m_buffer.m_buffer, 1, ©Info); - flushCommandBuffer(commandBuffer); + //flushCommandBuffer(commandBuffer); } return buffer.detach(); } -InputLayout* VKRenderer::createInputLayout(const InputElementDesc* inputElements, UInt inputElementCount) +InputLayout* VKRenderer::createInputLayout(const InputElementDesc* elements, UInt numElements) { - InputLayoutImpl* impl = new InputLayoutImpl; + RefPtr<InputLayoutImpl> layout(new InputLayoutImpl); + + List<VkVertexInputAttributeDescription>& dstVertexDescs = layout->m_vertexDescs; - // TODO: actually initialize things + size_t vertexSize = 0; + dstVertexDescs.SetSize(numElements); + + for (UInt i = 0; i < numElements; ++i) + { + const InputElementDesc& srcDesc = elements[i]; + VkVertexInputAttributeDescription& dstDesc = dstVertexDescs[i]; + + dstDesc.location = uint32_t(i); + dstDesc.binding = 0; + dstDesc.format = VulkanUtil::getVkFormat(srcDesc.format); + if (dstDesc.format == VK_FORMAT_UNDEFINED) + { + return nullptr; + } + + dstDesc.offset = uint32_t(srcDesc.offset); + + const size_t elementSize = RendererUtil::getFormatSize(srcDesc.format); + assert(elementSize > 0); + const size_t endElement = srcDesc.offset + elementSize; + + vertexSize = (vertexSize < endElement) ? endElement : vertexSize; + } - return (InputLayout*)impl; + // Work out the overall size + layout->m_vertexSize = int(vertexSize); + return layout.detach(); } void* VKRenderer::map(BufferResource* bufferIn, MapFlavor flavor) @@ -717,6 +1162,9 @@ void* VKRenderer::map(BufferResource* bufferIn, MapFlavor flavor) BufferResourceImpl* buffer = static_cast<BufferResourceImpl*>(bufferIn); assert(buffer->m_mapFlavor == MapFlavor::Unknown); + // Make sure everything has completed before reading... + m_deviceQueue.flushAndWait(); + const size_t bufferSize = buffer->getDesc().sizeInBytes; switch (flavor) @@ -730,7 +1178,7 @@ void* VKRenderer::map(BufferResource* bufferIn, MapFlavor flavor) } void* mappedData = nullptr; - checkResult(vkMapMemory(m_device, buffer->m_uploadBuffer.m_memory, 0, bufferSize, 0, &mappedData)); + SLANG_VK_CHECK(m_api.vkMapMemory(m_device, buffer->m_uploadBuffer.m_memory, 0, bufferSize, 0, &mappedData)); buffer->m_mapFlavor = flavor; return mappedData; } @@ -742,23 +1190,25 @@ void* VKRenderer::map(BufferResource* bufferIn, MapFlavor flavor) // create staging buffer Buffer staging; - SLANG_RETURN_NULL_ON_FAIL(_initBuffer(bufferSize, VK_BUFFER_USAGE_TRANSFER_DST_BIT, VK_MEMORY_PROPERTY_HOST_VISIBLE_BIT | VK_MEMORY_PROPERTY_HOST_COHERENT_BIT, staging)); + SLANG_RETURN_NULL_ON_FAIL(staging.init(m_api, 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(); + VkCommandBuffer commandBuffer = m_deviceQueue.getCommandBuffer(); VkBufferCopy copyInfo = {}; copyInfo.size = bufferSize; - vkCmdCopyBuffer(commandBuffer, buffer->m_buffer.m_buffer, staging.m_buffer, 1, ©Info); + m_api.vkCmdCopyBuffer(commandBuffer, buffer->m_buffer.m_buffer, staging.m_buffer, 1, ©Info); - flushCommandBuffer(commandBuffer); + m_deviceQueue.flushAndWait(); // Write out the data from the buffer void* mappedData = nullptr; - checkResult(vkMapMemory(m_device, staging.m_memory, 0, bufferSize, 0, &mappedData)); + SLANG_VK_CHECK(m_api.vkMapMemory(m_device, staging.m_memory, 0, bufferSize, 0, &mappedData)); ::memcpy(buffer->m_readBuffer.Buffer(), mappedData, bufferSize); - vkUnmapMemory(m_device, staging.m_memory); + m_api.vkUnmapMemory(m_device, staging.m_memory); + + buffer->m_mapFlavor = flavor; return buffer->m_readBuffer.Buffer(); } @@ -779,16 +1229,17 @@ void VKRenderer::unmap(BufferResource* bufferIn) case MapFlavor::WriteDiscard: case MapFlavor::HostWrite: { - vkUnmapMemory(m_device, buffer->m_uploadBuffer.m_memory); + m_api.vkUnmapMemory(m_device, buffer->m_uploadBuffer.m_memory); // Copy from staging buffer to real buffer - VkCommandBuffer commandBuffer = beginCommandBuffer(); + VkCommandBuffer commandBuffer = m_deviceQueue.getCommandBuffer(); VkBufferCopy copyInfo = {}; copyInfo.size = bufferSize; - vkCmdCopyBuffer(commandBuffer, buffer->m_uploadBuffer.m_buffer, buffer->m_buffer.m_buffer, 1, ©Info); + m_api.vkCmdCopyBuffer(commandBuffer, buffer->m_uploadBuffer.m_buffer, buffer->m_buffer.m_buffer, 1, ©Info); - flushCommandBuffer(commandBuffer); + // TODO: is this necessary? + //m_deviceQueue.flushAndWait(); break; } default: break; @@ -800,14 +1251,37 @@ void VKRenderer::unmap(BufferResource* bufferIn) void VKRenderer::setInputLayout(InputLayout* inputLayout) { + m_currentInputLayout = static_cast<InputLayoutImpl*>(inputLayout); } void VKRenderer::setPrimitiveTopology(PrimitiveTopology topology) { + m_primitiveTopology = VulkanUtil::getVkPrimitiveTopology(topology); } void VKRenderer::setVertexBuffers(UInt startSlot, UInt slotCount, BufferResource*const* buffers, const UInt* strides, const UInt* offsets) { + { + const UInt num = startSlot + slotCount; + if (num > m_boundVertexBuffers.Count()) + { + m_boundVertexBuffers.SetSize(num); + } + } + + for (UInt i = 0; i < slotCount; i++) + { + BufferResourceImpl* buffer = static_cast<BufferResourceImpl*>(buffers[i]); + if (buffer) + { + assert(buffer->m_initialUsage == Resource::Usage::VertexBuffer); + } + + BoundVertexBuffer& boundBuffer = m_boundVertexBuffers[startSlot + i]; + boundBuffer.m_buffer = buffer; + boundBuffer.m_stride = int(strides[i]); + boundBuffer.m_offset = int(offsets[i]); + } } void VKRenderer::setShaderProgram(ShaderProgram* program) @@ -815,12 +1289,58 @@ void VKRenderer::setShaderProgram(ShaderProgram* program) m_currentProgram = (ShaderProgramImpl*)program; } -void VKRenderer::setConstantBuffers(UInt startSlot, UInt slotCount, BufferResource*const* buffers, const UInt* offsets) +void VKRenderer::draw(UInt vertexCount, UInt startVertex = 0) { + Pipeline* pipeline = _getPipeline(); + if (!pipeline || pipeline->m_shaderProgram->m_pipelineType != PipelineType::Graphics) + { + assert(!"Invalid render pipeline"); + return; + } + + SLANG_RETURN_VOID_ON_FAIL(_beginPass()); + + // Also create descriptor sets based on the given pipeline layout + VkCommandBuffer commandBuffer = m_deviceQueue.getCommandBuffer(); + + m_api.vkCmdBindPipeline(commandBuffer, VK_PIPELINE_BIND_POINT_GRAPHICS, pipeline->m_pipeline); + m_api.vkCmdBindDescriptorSets(commandBuffer, VK_PIPELINE_BIND_POINT_GRAPHICS, pipeline->m_pipelineLayout, + 0, 1, &pipeline->m_descriptorSet, 0, nullptr); + + // Bind the vertex buffer + if (m_boundVertexBuffers.Count() > 0 && m_boundVertexBuffers[0].m_buffer) + { + const BoundVertexBuffer& boundVertexBuffer = m_boundVertexBuffers[0]; + + VkBuffer vertexBuffers[] = { boundVertexBuffer.m_buffer->m_buffer.m_buffer }; + VkDeviceSize offsets[] = { VkDeviceSize(boundVertexBuffer.m_offset) }; + + m_api.vkCmdBindVertexBuffers(commandBuffer, 0, 1, vertexBuffers, offsets); + } + + m_api.vkCmdDraw(commandBuffer, static_cast<uint32_t>(vertexCount), 1, 0, 0); + + _endPass(); } -void VKRenderer::draw(UInt vertexCount, UInt startVertex = 0) +void VKRenderer::dispatchCompute(int x, int y, int z) { + Pipeline* pipeline = _getPipeline(); + if (!pipeline || pipeline->m_shaderProgram->m_pipelineType != PipelineType::Compute) + { + assert(!"Invalid render pipeline"); + return; + } + + // Also create descriptor sets based on the given pipeline layout + VkCommandBuffer commandBuffer = m_deviceQueue.getCommandBuffer(); + + m_api.vkCmdBindPipeline(commandBuffer, VK_PIPELINE_BIND_POINT_COMPUTE, pipeline->m_pipeline); + + m_api.vkCmdBindDescriptorSets(commandBuffer, VK_PIPELINE_BIND_POINT_COMPUTE, pipeline->m_pipelineLayout, + 0, 1, &pipeline->m_descriptorSet, 0, nullptr); + + m_api.vkCmdDispatch(commandBuffer, x, y, z); } BindingState* VKRenderer::createBindingState(const BindingState::Desc& bindingStateDesc) @@ -871,172 +1391,12 @@ void VKRenderer::setBindingState(BindingState* state) m_currentBindingState = static_cast<BindingStateImpl*>(state); } -void VKRenderer::dispatchCompute(int x, int y, int z) -{ - // HACK: create a new pipeline for every call - - // First create a pipeline layout based on what is bound - - const auto& srcDetails = m_currentBindingState->m_bindingDetails; - const auto& srcBindings = m_currentBindingState->getDesc().m_bindings; - - const int numBindings = int(srcBindings.Count()); - - Slang::List<VkDescriptorSetLayoutBinding> dstBindings; - for (int i = 0; i < numBindings; ++i) - { - const auto& srcDetail = srcDetails[i]; - const auto& srcBinding = srcBindings[i]; - - switch (srcBinding.bindingType) - { - case BindingType::Buffer: - { - BufferResourceImpl* bufferResource = static_cast<BufferResourceImpl*>(srcBinding.resource.Ptr()); - const BufferResource::Desc& bufferResourceDesc = bufferResource->getDesc(); - - if (bufferResourceDesc.bindFlags & Resource::BindFlag::UnorderedAccess) - { - VkDescriptorSetLayoutBinding dstBinding = {}; - dstBinding.binding = srcDetail.m_binding; - dstBinding.descriptorCount = 1; - dstBinding.descriptorType = VK_DESCRIPTOR_TYPE_STORAGE_BUFFER; - dstBinding.stageFlags = VK_SHADER_STAGE_ALL; - - dstBindings.Add(dstBinding); - } - - break; - } - default: - // TODO: handle the other cases - break; - } - } - - VkDescriptorSetLayoutCreateInfo descriptorSetLayoutInfo = { VK_STRUCTURE_TYPE_DESCRIPTOR_SET_LAYOUT_CREATE_INFO }; - descriptorSetLayoutInfo.bindingCount = uint32_t(dstBindings.Count()); - descriptorSetLayoutInfo.pBindings = dstBindings.Buffer(); - - VkDescriptorSetLayout descriptorSetLayout = 0; - checkResult(vkCreateDescriptorSetLayout(m_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(m_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(m_device, &descriptorSetAllocInfo, &descriptorSet)); - - // Fill in the descriptor set, using our binding information - - for (int i = 0; i < numBindings; ++i) - { - const auto& srcDetail = srcDetails[i]; - const auto& srcBinding = srcBindings[i]; - - switch (srcBinding.bindingType) - { - case BindingType::Buffer: - { - BufferResourceImpl* bufferResource = static_cast<BufferResourceImpl*>(srcBinding.resource.Ptr()); - const BufferResource::Desc& bufferResourceDesc = bufferResource->getDesc(); - - if (bufferResourceDesc.bindFlags & Resource::BindFlag::UnorderedAccess) - { - VkDescriptorBufferInfo bufferInfo; - bufferInfo.buffer = bufferResource->m_buffer.m_buffer; - bufferInfo.offset = 0; - bufferInfo.range = bufferResourceDesc.sizeInBytes; - - VkWriteDescriptorSet writeInfo = { VK_STRUCTURE_TYPE_WRITE_DESCRIPTOR_SET }; - writeInfo.descriptorCount = 1; - writeInfo.descriptorType = VK_DESCRIPTOR_TYPE_STORAGE_BUFFER; - writeInfo.dstSet = descriptorSet; - writeInfo.dstBinding = srcDetail.m_binding; - writeInfo.dstArrayElement = 0; - writeInfo.pBufferInfo = &bufferInfo; - - vkUpdateDescriptorSets(m_device, 1, &writeInfo, 0, nullptr); - } - break; - } - default: - { - // handle 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(m_device, &pipelineLayoutInfo, nullptr, &pipelineLayout)); - - // Then create a pipeline to use that layout - - VkComputePipelineCreateInfo computePipelineInfo = { VK_STRUCTURE_TYPE_COMPUTE_PIPELINE_CREATE_INFO }; - computePipelineInfo.stage = m_currentProgram->m_compute; - computePipelineInfo.layout = pipelineLayout; - - VkPipelineCache pipelineCache = 0; - - VkPipeline pipeline; - checkResult(vkCreateComputePipelines(m_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(m_device, pipeline, nullptr); - - // TODO: need to free up the other resources too... -} - // ShaderCompiler interface -ShaderProgram* VKRenderer::compileProgram(const ShaderCompileRequest & request) +ShaderProgram* VKRenderer::compileProgram(const ShaderCompileRequest& request) { - ShaderProgramImpl* impl = new ShaderProgramImpl; + const PipelineType pipelineType = request.computeShader.name ? PipelineType::Compute : PipelineType::Graphics; + + ShaderProgramImpl* impl = new ShaderProgramImpl(pipelineType); if (request.computeShader.name) { impl->m_compute = compileEntryPoint(request.computeShader, VK_SHADER_STAGE_COMPUTE_BIT, impl->m_buffers[0]); diff --git a/tools/render-test/render.cpp b/tools/render-test/render.cpp index 37948f88b..6c47a5afe 100644 --- a/tools/render-test/render.cpp +++ b/tools/render-test/render.cpp @@ -36,6 +36,23 @@ const Resource::DescBase& Resource::getDescBase() const return s_emptyDescBase; } +/* !!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!! RendererUtil !!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!! */ + +/* static */const uint8_t RendererUtil::s_formatSize[int(Format::CountOf)] = +{ + 0, // Unknown, + + uint8_t(sizeof(float) * 4), // RGBA_Float32, + uint8_t(sizeof(float) * 3), // RGB_Float32, + uint8_t(sizeof(float) * 2), // RG_Float32, + uint8_t(sizeof(float) * 1), // R_Float32, + + uint8_t(sizeof(uint32_t)), // RGBA_Unorm_UInt8, + + uint8_t(sizeof(float)), // D_Float32, + uint8_t(sizeof(uint32_t)), // D_Unorm24_S8, +}; + /* !!!!!!!!!!!!!!!!!!!!!!!!!!! BindingState::Desc !!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!! */ void BindingState::Desc::addSampler(const SamplerDesc& desc, const ShaderBindSet& shaderBindSet) @@ -149,6 +166,26 @@ BindingState::BindIndexSlice BindingState::Desc::asSlice(ShaderStyle style, cons } +int BindingState::Desc::findBindingIndex(Resource::BindFlag::Enum bindFlag, ShaderStyleFlags shaderStyleFlags, BindIndex index) const +{ + const int numBindings = int(m_bindings.Count()); + for (int i = 0; i < numBindings; ++i) + { + const Binding& binding = m_bindings[i]; + if (binding.resource && (binding.resource->getDescBase().bindFlags & bindFlag) != 0) + { + for (int j = 0; j < int(ShaderStyle::CountOf); ++j) + { + if (indexOf(binding.shaderBindSet.shaderSlices[j], index) >= 0) + { + return i; + } + } + } + } + + return -1; +} /* !!!!!!!!!!!!!!!!!!!!!!!!!!! TextureResource::Size !!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!! */ int TextureResource::Size::calcMaxDimension(Type type) const @@ -328,4 +365,62 @@ void TextureResource::Desc::init3D(Format formatIn, int widthIn, int heightIn, i this->cpuAccessFlags = 0; } +/* !!!!!!!!!!!!!!!!!!!!!!!!! RennderUtil !!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!! */ + +ProjectionStyle RendererUtil::getProjectionStyle(RendererType type) +{ + switch (type) + { + case RendererType::DirectX11: + case RendererType::DirectX12: + { + return ProjectionStyle::DirectX; + } + case RendererType::OpenGl: return ProjectionStyle::OpenGl; + case RendererType::Vulkan: return ProjectionStyle::Vulkan; + case RendererType::Unknown: return ProjectionStyle::Unknown; + default: + { + assert(!"Unhandled type"); + return ProjectionStyle::Unknown; + } + } +} + +/* static */void RendererUtil::getIdentityProjection(ProjectionStyle style, float projMatrix[16]) +{ + switch (style) + { + case ProjectionStyle::DirectX: + case ProjectionStyle::OpenGl: + { + static const float kIdentity[] = + { + 1, 0, 0, 0, + 0, 1, 0, 0, + 0, 0, 1, 0, + 0, 0, 0, 1 + }; + ::memcpy(projMatrix, kIdentity, sizeof(kIdentity)); + break; + } + case ProjectionStyle::Vulkan: + { + static const float kIdentity[] = + { + 1, 0, 0, 0, + 0, -1, 0, 0, + 0, 0, 1, 0, + 0, 0, 0, 1 + }; + ::memcpy(projMatrix, kIdentity, sizeof(kIdentity)); + break; + } + default: + { + assert(!"Not handled"); + } + } +} + } // renderer_test diff --git a/tools/render-test/render.h b/tools/render-test/render.h index 861b56b32..64857dc4f 100644 --- a/tools/render-test/render.h +++ b/tools/render-test/render.h @@ -1,7 +1,6 @@ // render.h #pragma once -#include "options.h" #include "window.h" //#include "shader-input-layout.h" @@ -12,6 +11,9 @@ namespace renderer_test { +// Had to move here, because Options needs types defined here +typedef intptr_t Int; +typedef uintptr_t UInt; // Declare opaque type class InputLayout: public Slang::RefObject @@ -19,12 +21,37 @@ class InputLayout: public Slang::RefObject public: }; -class ShaderProgram: public Slang::RefObject +enum class PipelineType { - public: + Unknown, + Graphics, + Compute, + CountOf, }; +enum class RendererType +{ + Unknown, + DirectX11, + DirectX12, + OpenGl, + Vulkan, + CountOf, +}; + +enum class ProjectionStyle +{ + Unknown, + OpenGl, + DirectX, + Vulkan, + CountOf, +}; +class ShaderProgram: public Slang::RefObject +{ + public: +}; struct ShaderCompileRequest { @@ -62,14 +89,22 @@ public: virtual ShaderProgram* compileProgram(ShaderCompileRequest const& request) = 0; }; +/// Different formats of things like pixels or elements of vertices +/// NOTE! Any change to this type (adding, removing, changing order) - must also be reflected in changes to RendererUtil enum class Format { Unknown, + + RGBA_Float32, RGB_Float32, RG_Float32, R_Float32, RGBA_Unorm_UInt8, + + D_Float32, + D_Unorm24_S8, + CountOf, }; @@ -159,6 +194,9 @@ class Resource: public Slang::RefObject /// Base class for Descs struct DescBase { + bool canBind(BindFlag::Enum bindFlag) const { return (bindFlags & bindFlag) != 0; } + bool hasCpuAccessFlag(AccessFlag::Enum accessFlag) { return (cpuAccessFlags & accessFlag) != 0; } + int bindFlags = 0; ///< Combination of Resource::BindFlag or 0 (and will use initialUsage to set) int cpuAccessFlags = 0; ///< Combination of Resource::AccessFlag }; @@ -173,7 +211,7 @@ class Resource: public Slang::RefObject /// Get the descBase const DescBase& getDescBase() const; /// Returns true if can bind with flag - bool canBind(BindFlag::Enum bindFlag) const { return (getDescBase().bindFlags & bindFlag) != 0; } + bool canBind(BindFlag::Enum bindFlag) const { return getDescBase().canBind(bindFlag); } /// For a usage gives the required binding flags static const BindFlag::Enum s_requiredBinding[int(Usage::CountOf)]; @@ -348,6 +386,16 @@ public: CountOf, }; + struct ShaderStyleFlag + { + enum Enum + { + Hlsl = 1 << int(ShaderStyle::Hlsl), + Glsl = 1 << int(ShaderStyle::Glsl), + }; + }; + typedef int ShaderStyleFlags; ///< Combination of ShaderStyleFlag + /// A 'compact' representation of a 0 or more BindIndices. /// A Slice in this context is effectively an unowned array. /// If only a single index is he held (which is common) it's held directly in the m_indexOrBase member, otherwise m_indexOrBase is an index into the @@ -374,6 +422,13 @@ public: struct ShaderBindSet { void set(ShaderStyle style, const CompactBindIndexSlice& slice) { shaderSlices[int(style)] = slice; } + void setAll(const CompactBindIndexSlice& slice) + { + for (int i = 0; i < int(ShaderStyle::CountOf); ++i) + { + shaderSlices[i] = slice; + } + } CompactBindIndexSlice shaderSlices[int(ShaderStyle::CountOf)]; }; @@ -385,6 +440,18 @@ public: const BindIndex* begin() const { return data; } const BindIndex* end() const { return data + size; } + int indexOf(BindIndex index) const + { + for (int i = 0; i < size; ++i) + { + if (data[i] == index) + { + return i; + } + } + return -1; + } + int getSize() const { return int(size); } BindIndex operator[](int i) const { assert(i >= 0 && i < size); return data[i]; } @@ -437,6 +504,12 @@ public: /// Only >= 0 indices are valid CompactBindIndexSlice makeCompactSlice(const int* indices, int numIndices); + /// Returns the index of the element in the slice + int indexOf(const CompactBindIndexSlice& slice, BindIndex index) const { return asSlice(slice).indexOf(index); } + + /// Find the + int findBindingIndex(Resource::BindFlag::Enum bindFlag, ShaderStyleFlags shaderStyleFlags, BindIndex index) const; + Slang::List<Binding> m_bindings; ///< All of the bindings in order Slang::List<SamplerDesc> m_samplerDescs; ///< Holds the SamplerDesc for the binding - indexed by the descIndex member of Binding Slang::List<BindIndex> m_sharedBindIndices; ///< Used to store BindIndex slices that don't fit into CompactBindIndexSlice @@ -458,6 +531,12 @@ public: class Renderer: public Slang::RefObject { public: + + struct Info + { + ProjectionStyle m_projectionStyle; + }; + virtual SlangResult initialize(void* inWindowHandle) = 0; virtual void setClearColor(const float color[4]) = 0; @@ -488,9 +567,6 @@ public: virtual void setShaderProgram(ShaderProgram* program) = 0; - virtual void setConstantBuffers(UInt startSlot, UInt slotCount, BufferResource*const* buffers, const UInt* offsets) = 0; - inline void setConstantBuffer(UInt slot, BufferResource* buffer, UInt offset = 0); - virtual void draw(UInt vertexCount, UInt startVertex = 0) = 0; virtual void dispatchCompute(int x, int y, int z) = 0; @@ -499,6 +575,9 @@ public: virtual void submitGpuWork() = 0; /// Blocks until Gpu work is complete virtual void waitForGpu() = 0; + + /// Get the type of this renderer + virtual RendererType getRendererType() const = 0; }; // ---------------------------------------------------------------------------------------- @@ -506,11 +585,20 @@ inline void Renderer::setVertexBuffer(UInt slot, BufferResource* buffer, UInt st { setVertexBuffers(slot, 1, &buffer, &stride, &offset); } -// ---------------------------------------------------------------------------------------- -inline void Renderer::setConstantBuffer(UInt slot, BufferResource* buffer, UInt offset) + +/// Functions that are around Renderer and it's types +struct RendererUtil { - setConstantBuffers(slot, 1, &buffer, &offset); -} + /// Gets the size in bytes of a Format type. Returns 0 if a size is not defined/invalid + SLANG_FORCE_INLINE static size_t getFormatSize(Format format) { return s_formatSize[int(format)]; } + /// Given a renderer type, gets a projection style + static ProjectionStyle getProjectionStyle(RendererType type); + /// Given the projection style returns an 'identity' matrix, which ensures x,y mapping to pixels is the same on all targets + static void getIdentityProjection(ProjectionStyle style, float projMatrix[16]); + + private: + static const uint8_t s_formatSize[int(Format::CountOf)]; +}; } // renderer_test diff --git a/tools/render-test/slang-support.cpp b/tools/render-test/slang-support.cpp index 094c306d1..342af571c 100644 --- a/tools/render-test/slang-support.cpp +++ b/tools/render-test/slang-support.cpp @@ -4,6 +4,8 @@ #include "slang-support.h" +#include "options.h" + #include <assert.h> #include <stdio.h> diff --git a/tools/render-test/vk-api.cpp b/tools/render-test/vk-api.cpp new file mode 100644 index 000000000..9d721f18f --- /dev/null +++ b/tools/render-test/vk-api.cpp @@ -0,0 +1,136 @@ +// vk-api.cpp +#include "vk-api.h" + +#include "../../source/core/list.h" + +namespace renderer_test { +using namespace Slang; + +// !!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!! VulkanApi !!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!! + +#define VK_API_CHECK_FUNCTION(x) && (x != nullptr) +#define VK_API_CHECK_FUNCTIONS(FUNCTION_LIST) true FUNCTION_LIST(VK_API_CHECK_FUNCTION) + +bool VulkanApi::areDefined(ProcType type) const +{ + switch (type) + { + case ProcType::Global: return VK_API_CHECK_FUNCTIONS(VK_API_ALL_GLOBAL_PROCS); + case ProcType::Instance: return VK_API_CHECK_FUNCTIONS(VK_API_ALL_INSTANCE_PROCS); + case ProcType::Device: return VK_API_CHECK_FUNCTIONS(VK_API_ALL_DEVICE_PROCS); + default: + { + assert(!"Unhandled type"); + return false; + } + } +} + +Slang::Result VulkanApi::initGlobalProcs(const VulkanModule& module) +{ +#define VK_API_GET_GLOBAL_PROC(x) x = (PFN_##x)module.getFunction(#x); + + // Initialize all the global functions + VK_API_ALL_GLOBAL_PROCS(VK_API_GET_GLOBAL_PROC) + + if (!areDefined(ProcType::Global)) + { + return SLANG_FAIL; + } + m_module = &module; + return SLANG_OK; +} + +Slang::Result VulkanApi::initInstanceProcs(VkInstance instance) +{ + assert(instance && vkGetInstanceProcAddr != nullptr); + +#define VK_API_GET_INSTANCE_PROC(x) x = (PFN_##x)vkGetInstanceProcAddr(instance, #x); + + VK_API_ALL_INSTANCE_PROCS(VK_API_GET_INSTANCE_PROC) + + if (!areDefined(ProcType::Instance)) + { + return SLANG_FAIL; + } + + m_instance = instance; + return SLANG_OK; +} + +Slang::Result VulkanApi::initPhysicalDevice(VkPhysicalDevice physicalDevice) +{ + assert(m_physicalDevice == VK_NULL_HANDLE); + m_physicalDevice = physicalDevice; + + vkGetPhysicalDeviceProperties(m_physicalDevice, &m_deviceProperties); + vkGetPhysicalDeviceFeatures(m_physicalDevice, &m_deviceFeatures); + vkGetPhysicalDeviceMemoryProperties(m_physicalDevice, &m_deviceMemoryProperties); + + return SLANG_OK; +} + +Slang::Result VulkanApi::initDeviceProcs(VkDevice device) +{ + assert(m_instance && device && vkGetDeviceProcAddr != nullptr); + +#define VK_API_GET_DEVICE_PROC(x) x = (PFN_##x)vkGetDeviceProcAddr(device, #x); + + VK_API_ALL_DEVICE_PROCS(VK_API_GET_DEVICE_PROC) + + if (!areDefined(ProcType::Device)) + { + return SLANG_FAIL; + } + + m_device = device; + return SLANG_OK; +} + +int VulkanApi::findMemoryTypeIndex(uint32_t typeBits, VkMemoryPropertyFlags properties) const +{ + const int numMemoryTypes = int(m_deviceMemoryProperties.memoryTypeCount); + + // bit holds current test bit against typeBits. Ie bit == 1 << typeBits + + uint32_t bit = 1; + for (int i = 0; (typeBits != 0) && i < numMemoryTypes; ++i, bit += bit) + { + auto const& memoryType = m_deviceMemoryProperties.memoryTypes[i]; + if ((typeBits & bit) && (memoryType.propertyFlags & properties) == properties) + { + return i; + } + } + + //assert(!"failed to find a usable memory type"); + return -1; +} + +int VulkanApi::findQueue(VkQueueFlags reqFlags) const +{ + assert(m_physicalDevice != VK_NULL_HANDLE); + + uint32_t numQueueFamilies = 0; + vkGetPhysicalDeviceQueueFamilyProperties(m_physicalDevice, &numQueueFamilies, nullptr); + + Slang::List<VkQueueFamilyProperties> queueFamilies; + queueFamilies.SetSize(numQueueFamilies); + vkGetPhysicalDeviceQueueFamilyProperties(m_physicalDevice, &numQueueFamilies, queueFamilies.Buffer()); + + // Find a queue that can service our needs + //VkQueueFlags reqQueueFlags = VK_QUEUE_GRAPHICS_BIT | VK_QUEUE_COMPUTE_BIT; + + int queueFamilyIndex = -1; + for (int i = 0; i < int(numQueueFamilies); ++i) + { + if ((queueFamilies[i].queueFlags & reqFlags) == reqFlags) + { + return i; + } + } + + return -1; +} + +} // renderer_test diff --git a/tools/render-test/vk-api.h b/tools/render-test/vk-api.h new file mode 100644 index 000000000..1616a4065 --- /dev/null +++ b/tools/render-test/vk-api.h @@ -0,0 +1,187 @@ +// vk-api.h +#pragma once + +#include "vk-module.h" + +namespace renderer_test { + +#define VK_API_GLOBAL_PROCS(x) \ + x(vkGetInstanceProcAddr) \ + x(vkCreateInstance) \ + /* */ + +#define VK_API_INSTANCE_PROCS(x) \ + x(vkCreateDevice) \ + x(vkCreateDebugReportCallbackEXT) \ + x(vkDestroyDebugReportCallbackEXT) \ + x(vkDebugReportMessageEXT) \ + x(vkEnumeratePhysicalDevices) \ + x(vkGetPhysicalDeviceProperties) \ + x(vkGetPhysicalDeviceFeatures) \ + x(vkGetPhysicalDeviceMemoryProperties) \ + x(vkGetPhysicalDeviceQueueFamilyProperties) \ + x(vkGetPhysicalDeviceFormatProperties) \ + x(vkGetDeviceProcAddr) \ + /* */ + +#define VK_API_DEVICE_PROCS(x) \ + x(vkCreateDescriptorPool) \ + x(vkDestroyDescriptorPool) \ + x(vkGetDeviceQueue) \ + x(vkQueueSubmit) \ + x(vkQueueWaitIdle) \ + x(vkCreateBuffer) \ + x(vkGetBufferMemoryRequirements) \ + x(vkAllocateMemory) \ + x(vkBindBufferMemory) \ + x(vkMapMemory) \ + x(vkUnmapMemory) \ + x(vkCmdCopyBuffer) \ + x(vkDestroyBuffer) \ + x(vkFreeMemory) \ + x(vkCreateDescriptorSetLayout) \ + x(vkDestroyDescriptorSetLayout) \ + x(vkAllocateDescriptorSets) \ + x(vkUpdateDescriptorSets) \ + x(vkCreatePipelineLayout) \ + x(vkDestroyPipelineLayout) \ + x(vkCreateComputePipelines) \ + x(vkCreateGraphicsPipelines) \ + x(vkDestroyPipeline) \ + x(vkCreateShaderModule) \ + x(vkDestroyShaderModule) \ + x(vkCreateFramebuffer) \ + x(vkDestroyFramebuffer) \ + \ + x(vkCmdBindPipeline) \ + x(vkCmdBindDescriptorSets) \ + x(vkCmdDispatch) \ + x(vkCmdDraw) \ + x(vkCmdSetScissor) \ + x(vkCmdSetViewport) \ + x(vkCmdBindVertexBuffers) \ + x(vkCmdBindIndexBuffer) \ + x(vkCmdBeginRenderPass) \ + x(vkCmdEndRenderPass) \ + \ + x(vkCreateFence) \ + x(vkDestroyFence) \ + x(vkResetFences) \ + x(vkGetFenceStatus) \ + x(vkWaitForFences) \ + \ + x(vkCreateSemaphore) \ + x(vkDestroySemaphore) \ + \ + x(vkCreateEvent) \ + x(vkDestroyEvent) \ + x(vkGetEventStatus) \ + x(vkSetEvent) \ + x(vkResetEvent) \ + \ + x(vkCreateCommandPool) \ + x(vkDestroyCommandPool) \ + \ + x(vkFreeCommandBuffers) \ + x(vkAllocateCommandBuffers) \ + x(vkBeginCommandBuffer) \ + x(vkEndCommandBuffer) \ + x(vkResetCommandBuffer) \ + \ + x(vkCreateImageView) \ + x(vkDestroyImageView) \ + \ + x(vkCreateRenderPass) \ + x(vkDestroyRenderPass) \ + /* */ + +#if SLANG_WINDOWS_FAMILY +# define VK_API_INSTANCE_PLATFORM_KHR_PROCS(x) \ + x(vkCreateWin32SurfaceKHR) \ + /* */ +#else +# define VK_API_INSTANCE_PLATFORM_KHR_PROCS(x) \ + x(vkCreateXlibSurfaceKHR) \ + /* */ +#endif + +#define VK_API_INSTANCE_KHR_PROCS(x) \ + VK_API_INSTANCE_PLATFORM_KHR_PROCS(x) \ + x(vkGetPhysicalDeviceSurfaceSupportKHR) \ + x(vkGetPhysicalDeviceSurfaceFormatsKHR) \ + x(vkGetPhysicalDeviceSurfacePresentModesKHR) \ + x(vkGetPhysicalDeviceSurfaceCapabilitiesKHR) \ + x(vkDestroySurfaceKHR) \ + /* */ + +#define VK_API_DEVICE_KHR_PROCS(x) \ + x(vkQueuePresentKHR) \ + x(vkCreateSwapchainKHR) \ + x(vkGetSwapchainImagesKHR) \ + x(vkDestroySwapchainKHR) \ + x(vkAcquireNextImageKHR) \ + /* */ + +#define VK_API_ALL_GLOBAL_PROCS(x) \ + VK_API_GLOBAL_PROCS(x) + +#define VK_API_ALL_INSTANCE_PROCS(x) \ + VK_API_INSTANCE_PROCS(x) \ + VK_API_INSTANCE_KHR_PROCS(x) + +#define VK_API_ALL_DEVICE_PROCS(x) \ + VK_API_DEVICE_PROCS(x) \ + VK_API_DEVICE_KHR_PROCS(x) + +#define VK_API_ALL_PROCS(x) \ + VK_API_ALL_GLOBAL_PROCS(x) \ + VK_API_ALL_INSTANCE_PROCS(x) \ + VK_API_ALL_DEVICE_PROCS(x) \ + /* */ + +#define VK_API_DECLARE_PROC(NAME) PFN_##NAME NAME = nullptr; + +struct VulkanApi +{ + VK_API_ALL_PROCS(VK_API_DECLARE_PROC) + + enum class ProcType + { + Global, + Instance, + Device, + }; + + /// Returns true if all the functions in the class are defined + bool areDefined(ProcType type) const; + + /// Sets up global parameters + Slang::Result initGlobalProcs(const VulkanModule& module); + /// Initialize the instance functions + Slang::Result initInstanceProcs(VkInstance instance); + + /// Called before initDevice + Slang::Result initPhysicalDevice(VkPhysicalDevice physicalDevice); + + /// Initialize the device functions + Slang::Result initDeviceProcs(VkDevice device); + + /// Type bits control which indices are tested against bit 0 for testing at index 0 + /// properties - a memory type must have all the bits set as passed in + /// Returns -1 if couldn't find an appropriate memory type index + int findMemoryTypeIndex(uint32_t typeBits, VkMemoryPropertyFlags properties) const; + + /// Given queue required flags, finds a queue + int findQueue(VkQueueFlags reqFlags) const; + + const VulkanModule* m_module = nullptr; ///< Module this was all loaded from + VkInstance m_instance = VK_NULL_HANDLE; + VkDevice m_device = VK_NULL_HANDLE; + VkPhysicalDevice m_physicalDevice = VK_NULL_HANDLE; + + VkPhysicalDeviceProperties m_deviceProperties; + VkPhysicalDeviceFeatures m_deviceFeatures; + VkPhysicalDeviceMemoryProperties m_deviceMemoryProperties; +}; + +} // renderer_test diff --git a/tools/render-test/vk-device-queue.cpp b/tools/render-test/vk-device-queue.cpp new file mode 100644 index 000000000..733ee6125 --- /dev/null +++ b/tools/render-test/vk-device-queue.cpp @@ -0,0 +1,198 @@ +// vk-device-queue.cpp +#include "vk-device-queue.h" + +#include <stdlib.h> +#include <stdio.h> + +namespace renderer_test { +using namespace Slang; + +VulkanDeviceQueue::~VulkanDeviceQueue() +{ + for (int i = 0; i < int(EventType::CountOf); ++i) + { + m_api->vkDestroySemaphore(m_api->m_device, m_semaphores[i], nullptr); + } + + for (int i = 0; i < m_numCommandBuffers; i++) + { + m_api->vkFreeCommandBuffers(m_api->m_device, m_commandPool, 1, &m_commandBuffers[i]); + m_api->vkDestroyFence(m_api->m_device, m_fences[i].fence, nullptr); + } + m_api->vkDestroyCommandPool(m_api->m_device, m_commandPool, nullptr); +} + +SlangResult VulkanDeviceQueue::init(const VulkanApi& api, VkQueue queue, int queueIndex) +{ + assert(m_api == nullptr); + m_api = &api; + + for (int i = 0; i < int(EventType::CountOf); ++i) + { + m_semaphores[i] = VK_NULL_HANDLE; + m_currentSemaphores[i] = VK_NULL_HANDLE; + } + + m_numCommandBuffers = kMaxCommandBuffers; + m_queueIndex = queueIndex; + + m_queue = queue; + + VkCommandPoolCreateInfo poolCreateInfo = {}; + poolCreateInfo.sType = VK_STRUCTURE_TYPE_COMMAND_POOL_CREATE_INFO; + poolCreateInfo.flags = VK_COMMAND_POOL_CREATE_RESET_COMMAND_BUFFER_BIT; + + poolCreateInfo.queueFamilyIndex = queueIndex; + + api.vkCreateCommandPool(api.m_device, &poolCreateInfo, nullptr, &m_commandPool); + + VkCommandBufferAllocateInfo commandInfo = {}; + commandInfo.sType = VK_STRUCTURE_TYPE_COMMAND_BUFFER_ALLOCATE_INFO; + commandInfo.commandPool = m_commandPool; + commandInfo.level = VK_COMMAND_BUFFER_LEVEL_PRIMARY; + commandInfo.commandBufferCount = 1; + + VkFenceCreateInfo fenceCreateInfo = {}; + fenceCreateInfo.sType = VK_STRUCTURE_TYPE_FENCE_CREATE_INFO; + fenceCreateInfo.flags = 0; // VK_FENCE_CREATE_SIGNALED_BIT; + + for (int i = 0; i < m_numCommandBuffers; i++) + { + Fence& fence = m_fences[i]; + + api.vkAllocateCommandBuffers(api.m_device, &commandInfo, &m_commandBuffers[i]); + + api.vkCreateFence(api.m_device, &fenceCreateInfo, nullptr, &fence.fence); + fence.active = false; + fence.value = 0; + } + + VkSemaphoreCreateInfo semaphoreCreateInfo = {}; + semaphoreCreateInfo.sType = VK_STRUCTURE_TYPE_SEMAPHORE_CREATE_INFO; + + for (int i = 0; i < int(EventType::CountOf); ++i) + { + api.vkCreateSemaphore(api.m_device, &semaphoreCreateInfo, nullptr, &m_semaphores[i]); + } + + // Second step of flush to prime command buffer + flushStepB(); + + return SLANG_OK; +} + +void VulkanDeviceQueue::flushStepA() +{ + m_api->vkEndCommandBuffer(m_commandBuffer); + + VkPipelineStageFlags stageFlags = VK_PIPELINE_STAGE_BOTTOM_OF_PIPE_BIT; + + VkSubmitInfo submitInfo = {}; + submitInfo.sType = VK_STRUCTURE_TYPE_SUBMIT_INFO; + + // Wait semaphores + if (isCurrent(EventType::BeginFrame)) + { + submitInfo.waitSemaphoreCount = 1; + submitInfo.pWaitSemaphores = &m_currentSemaphores[int(EventType::BeginFrame)]; + } + + submitInfo.pWaitDstStageMask = &stageFlags; + submitInfo.commandBufferCount = 1; + submitInfo.pCommandBuffers = &m_commandBuffer; + + // Signal semaphores + if (isCurrent(EventType::EndFrame)) + { + submitInfo.signalSemaphoreCount = 1; + submitInfo.pSignalSemaphores = &m_currentSemaphores[int(EventType::EndFrame)]; + } + + Fence& fence = m_fences[m_commandBufferIndex]; + + m_api->vkQueueSubmit(m_queue, 1, &submitInfo, fence.fence); + + // mark signaled fence value + fence.value = m_nextFenceValue; + fence.active = true; + + // increment fence value + m_nextFenceValue++; + + // No longer waiting on this semaphore + makeCompleted(EventType::BeginFrame); +} + +void VulkanDeviceQueue::_updateFenceAtIndex( int fenceIndex, bool blocking) +{ + Fence& fence = m_fences[fenceIndex]; + + if (fence.active) + { + uint64_t timeout = blocking ? ~uint64_t(0) : 0; + + if (VK_SUCCESS == m_api->vkWaitForFences(m_api->m_device, 1, &fence.fence, VK_TRUE, timeout)) + { + m_api->vkResetFences(m_api->m_device, 1, &fence.fence); + + fence.active = false; + + if (fence.value > m_lastFenceCompleted) + { + m_lastFenceCompleted = fence.value; + } + } + } +} + +void VulkanDeviceQueue::flushStepB() +{ + m_commandBufferIndex = (m_commandBufferIndex + 1) % m_numCommandBuffers; + m_commandBuffer = m_commandBuffers[m_commandBufferIndex]; + + // non-blocking update of fence values + for (int i = 0; i < m_numCommandBuffers; ++i) + { + _updateFenceAtIndex(i, false); + } + + // blocking update of fence values + _updateFenceAtIndex(m_commandBufferIndex, true); + + m_api->vkResetCommandBuffer(m_commandBuffer, 0); + + //m_api.vkResetCommandPool(m_api->m_device, m_commandPool, 0); + + VkCommandBufferBeginInfo beginInfo = {}; + beginInfo.sType = VK_STRUCTURE_TYPE_COMMAND_BUFFER_BEGIN_INFO; + beginInfo.flags = VK_COMMAND_BUFFER_USAGE_ONE_TIME_SUBMIT_BIT; + + m_api->vkBeginCommandBuffer(m_commandBuffer, &beginInfo); +} + +void VulkanDeviceQueue::flush() +{ + flushStepA(); + flushStepB(); +} + +void VulkanDeviceQueue::flushAndWait() +{ + flush(); + waitForIdle(); +} + +VkSemaphore VulkanDeviceQueue::makeCurrent(EventType eventType) +{ + assert(!isCurrent(eventType)); + VkSemaphore semaphore = m_semaphores[int(eventType)]; + m_currentSemaphores[int(eventType)] = semaphore; + return semaphore; +} + +void VulkanDeviceQueue::makeCompleted(EventType eventType) +{ + m_currentSemaphores[int(eventType)] = VK_NULL_HANDLE; +} + +} // renderer_test diff --git a/tools/render-test/vk-device-queue.h b/tools/render-test/vk-device-queue.h new file mode 100644 index 000000000..ea50cbd81 --- /dev/null +++ b/tools/render-test/vk-device-queue.h @@ -0,0 +1,95 @@ +// vk-swap-chain.h +#pragma once + +#include "vk-api.h" + +namespace renderer_test { + +struct VulkanDeviceQueue +{ + enum + { + kMaxCommandBuffers = 8, + }; + + enum class EventType + { + BeginFrame, + EndFrame, + CountOf, + }; + + /// Initialize - must be called before anything else can be done + SlangResult init(const VulkanApi& api, VkQueue queue, int queueIndex); + + /// Flushes the current command list, and steps to next (internally this is equivalent to a stepA followed by stepB) + void flush(); + /// Performs a full flush, and then waits for idle. + void flushAndWait(); + + /// Blocks until all work submitted to GPU has completed + void waitForIdle() { m_api->vkQueueWaitIdle(m_queue); } + + /// Set the graphics queue index (as set on init) + int getQueueIndex() const { return m_queueIndex; } + + + /// Make the specified event 'current' - meaning it's semaphone must be waited on + VkSemaphore makeCurrent(EventType eventType); + /// Makes the event no longer required to be waited on + void makeCompleted(EventType eventType); + /// Returns true if the event is already current + SLANG_FORCE_INLINE bool isCurrent(EventType eventType) const { return m_currentSemaphores[int(eventType)] != VK_NULL_HANDLE; } + + /// Get the command buffer + VkCommandBuffer getCommandBuffer() const { return m_commandBuffer; } + + /// Get the queue + VkQueue getQueue() const { return m_queue; } + + /// Get the API + const VulkanApi* getApi() const { return m_api; } + + /// Flushes the current command list + void flushStepA(); + /// Steps to next command buffer and opens. May block if command buffer is still in use + void flushStepB(); + + /// Dtor + ~VulkanDeviceQueue(); + + protected: + + struct Fence + { + VkFence fence; + bool active; + uint64_t value; + }; + + void _updateFenceAtIndex(int fenceIndex, bool blocking); + + VkQueue m_queue = VK_NULL_HANDLE; + + VkCommandPool m_commandPool = VK_NULL_HANDLE; + int m_numCommandBuffers = 0; + int m_commandBufferIndex = 0; + // There are the same amount of command buffers as fences + VkCommandBuffer m_commandBuffers[kMaxCommandBuffers] = { VK_NULL_HANDLE }; + + Fence m_fences[kMaxCommandBuffers] = { {VK_NULL_HANDLE, 0, 0u} }; + + VkCommandBuffer m_commandBuffer = VK_NULL_HANDLE; + + VkSemaphore m_semaphores[int(EventType::CountOf)]; + VkSemaphore m_currentSemaphores[int(EventType::CountOf)]; + + uint64_t m_lastFenceCompleted = 1; + uint64_t m_nextFenceValue = 2; + + int m_queueIndex = 0; + + const VulkanApi* m_api = nullptr; +}; + +} // renderer_test diff --git a/tools/render-test/vk-module.cpp b/tools/render-test/vk-module.cpp new file mode 100644 index 000000000..a10333584 --- /dev/null +++ b/tools/render-test/vk-module.cpp @@ -0,0 +1,75 @@ +// module.cpp +#include "vk-module.h" + +#include <stdlib.h> +#include <stdio.h> + +#if SLANG_WINDOWS_FAMILY +# include <windows.h> +#else +# include <dlfcn.h> +#endif + +namespace renderer_test { +using namespace Slang; + +// !!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!! VulkanModule !!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!! + +Slang::Result VulkanModule::init() +{ + if (isInitialized()) + { + destroy(); + return SLANG_OK; + } + + const char* dynamicLibraryName = "Unknown"; + +#if SLANG_WINDOWS_FAMILY + dynamicLibraryName = "vulkan-1.dll"; + HMODULE module = ::LoadLibraryA(dynamicLibraryName); + m_module = (void*)module; +#else + dynamicLibraryName = "libvulkan.so.1"; + m_module = dlopen(dynamicLibraryName, RTLD_NOW); +#endif + + if (!m_module) + { + fprintf(stderr, "error: failed load '%s'\n", dynamicLibraryName); + return SLANG_FAIL; + } + + return SLANG_OK; +} + +PFN_vkVoidFunction VulkanModule::getFunction(const char* name) const +{ + assert(m_module); + if (!m_module) + { + return nullptr; + } +#if SLANG_WINDOWS_FAMILY + return (PFN_vkVoidFunction)::GetProcAddress((HMODULE)m_module, name); +#else + return (PFN_vkVoidFunction)dlsym(m_module, name); +#endif +} + +void VulkanModule::destroy() +{ + if (!isInitialized()) + { + return; + } + +#if SLANG_WINDOWS_FAMILY + ::FreeLibrary((HMODULE)m_module); +#else + dlclose(m_module); +#endif + m_module = nullptr; +} + +} // renderer_test diff --git a/tools/render-test/vk-module.h b/tools/render-test/vk-module.h new file mode 100644 index 000000000..dd3c1eaa1 --- /dev/null +++ b/tools/render-test/vk-module.h @@ -0,0 +1,38 @@ +// vk-module.h +#pragma once + +#include "../../source/core/slang-defines.h" +#include "../../source/core/slang-result.h" + +#if SLANG_WINDOWS_FAMILY +# define VK_USE_PLATFORM_WIN32_KHR 1 +#else +# define VK_USE_PLATFORM_XLIB_KHR 1 +#endif + +#define VK_NO_PROTOTYPES +#include <vulkan/vulkan.h> + +namespace renderer_test { + +struct VulkanModule +{ + /// true if has been initialized + SLANG_FORCE_INLINE bool isInitialized() const { return m_module != nullptr; } + + /// Get a function by name + PFN_vkVoidFunction getFunction(const char* name) const; + + /// Initialize + Slang::Result init(); + /// Destroy + void destroy(); + + /// Dtor + ~VulkanModule() { destroy(); } + + protected: + void* m_module = nullptr; +}; + +} // renderer_test diff --git a/tools/render-test/vk-swap-chain.cpp b/tools/render-test/vk-swap-chain.cpp new file mode 100644 index 000000000..7b8b6221c --- /dev/null +++ b/tools/render-test/vk-swap-chain.cpp @@ -0,0 +1,421 @@ +// vk-swap-chain.cpp +#include "vk-swap-chain.h" + +#include "vk-util.h" + +#include "../../source/core/list.h" + +#include <stdlib.h> +#include <stdio.h> + +namespace renderer_test { +using namespace Slang; + +static int _indexOf(List<VkSurfaceFormatKHR>& formatsIn, VkFormat format) +{ + const int numFormats = int(formatsIn.Count()); + const VkSurfaceFormatKHR* formats = formatsIn.Buffer(); + + for (int i = 0; i < numFormats; ++i) + { + if (formats[i].format == format) + { + return i; + } + } + return -1; +} + +SlangResult VulkanSwapChain::init(VulkanDeviceQueue* deviceQueue, const Desc& descIn, const PlatformDesc* platformDescIn) +{ + assert(platformDescIn); + + m_deviceQueue = deviceQueue; + m_api = deviceQueue->getApi(); + + // Make sure it's not set initially + m_format = VK_FORMAT_UNDEFINED; + + Desc desc(descIn); + +#if SLANG_WINDOWS_FAMILY + const WinPlatformDesc* platformDesc = static_cast<const WinPlatformDesc*>(platformDescIn); + _setPlatformDesc(*platformDesc); + + VkWin32SurfaceCreateInfoKHR surfaceCreateInfo = {}; + surfaceCreateInfo.sType = VK_STRUCTURE_TYPE_WIN32_SURFACE_CREATE_INFO_KHR; + surfaceCreateInfo.hinstance = platformDesc->m_hinstance; + surfaceCreateInfo.hwnd = platformDesc->m_hwnd; + + SLANG_VK_RETURN_ON_FAIL(m_api->vkCreateWin32SurfaceKHR(m_api->m_instance, &surfaceCreateInfo, nullptr, &m_surface)); +#else + const XPlatformDesc* platformDesc = static_cast<const XPlatformDesc*>(platformDescIn); + _setPlatformDesc(*platformDesc); + + VkXlibSurfaceCreateInfoKHR surfaceCreateInfo = {}; + surfaceCreateInfo.sType = VK_STRUCTURE_TYPE_XLIB_SURFACE_CREATE_INFO_KHR; + surfaceCreateInfo.dpy = platformDesc->m_display; + surfaceCreateInfo.window = platformDesc->m_window; + + SLANG_VK_RETURN_ON_FAIL(m_api->vkCreateXlibSurfaceKHR(m_api->m_instance, &surfaceCreateInfo, nullptr, &m_surface)); +#endif + + VkBool32 supported = false; + m_api->vkGetPhysicalDeviceSurfaceSupportKHR(m_api->m_physicalDevice, deviceQueue->getQueueIndex(), m_surface, &supported); + + uint32_t numSurfaceFormats = 0; + List<VkSurfaceFormatKHR> surfaceFormats; + m_api->vkGetPhysicalDeviceSurfaceFormatsKHR(m_api->m_physicalDevice, m_surface, &numSurfaceFormats, nullptr); + surfaceFormats.SetSize(int(numSurfaceFormats)); + m_api->vkGetPhysicalDeviceSurfaceFormatsKHR(m_api->m_physicalDevice, m_surface, &numSurfaceFormats, surfaceFormats.Buffer()); + + // Look for a suitable format + List<VkFormat> formats; + formats.Add(VulkanUtil::getVkFormat(desc.m_format)); + // HACK! To check for a different format if couldn't be found + if (descIn.m_format == Format::RGBA_Unorm_UInt8) + { + formats.Add(VK_FORMAT_B8G8R8A8_UNORM); + } + + for(int i = 0; i < int(formats.Count()); ++i) + { + VkFormat format = formats[i]; + if (_indexOf(surfaceFormats, format) >= 0) + { + m_format = format; + } + } + + if (m_format == VK_FORMAT_UNDEFINED) + { + return SLANG_FAIL; + } + + // Save the desc + m_desc = desc; + + SLANG_RETURN_ON_FAIL(_createSwapChain()); + + m_desc = desc; + return SLANG_OK; +} + +void VulkanSwapChain::getWindowSize(int* widthOut, int* heightOut) const +{ +#if SLANG_WINDOWS_FAMILY + auto platformDesc = _getPlatformDesc<WinPlatformDesc>(); + + RECT rc; + ::GetClientRect(platformDesc->m_hwnd, &rc); + *widthOut = rc.right - rc.left; + *heightOut = rc.bottom - rc.top; +#else + auto platformDesc = _getPlatformDesc<XPlatformDesc>(); + + XWindowAttributes winAttr = {}; + XGetWindowAttributes(platformDesc->m_display, platformDesc->m_window, &winAttr); + + *widthOut = winAttr.width; + *heightOut = winAttr.height; +#endif +} + +SlangResult VulkanSwapChain::_createFrameBuffers(VkRenderPass renderPass) +{ + assert(renderPass != VK_NULL_HANDLE); + + for (int i = 0; i < int(m_images.Count()); ++i) + { + Image& image = m_images[i]; + VkImageView attachments[] = + { + image.m_imageView + }; + + VkFramebufferCreateInfo framebufferInfo = {}; + framebufferInfo.sType = VK_STRUCTURE_TYPE_FRAMEBUFFER_CREATE_INFO; + framebufferInfo.renderPass = renderPass; + framebufferInfo.attachmentCount = 1; + framebufferInfo.pAttachments = attachments; + framebufferInfo.width = m_width; + framebufferInfo.height = m_height; + framebufferInfo.layers = 1; + + SLANG_VK_RETURN_ON_FAIL(m_api->vkCreateFramebuffer(m_api->m_device, &framebufferInfo, nullptr, &image.m_frameBuffer)); + } + + return SLANG_OK; +} + +void VulkanSwapChain::_destroyFrameBuffers() +{ + for (int i = 0; i < int(m_images.Count()); ++i) + { + Image& image = m_images[i]; + if (image.m_frameBuffer != VK_NULL_HANDLE) + { + m_api->vkDestroyFramebuffer(m_api->m_device, image.m_frameBuffer, nullptr); + image.m_frameBuffer = VK_NULL_HANDLE; + } + } +} + +SlangResult VulkanSwapChain::createFrameBuffers(VkRenderPass renderPass) +{ + if (m_renderPass != VK_NULL_HANDLE) + { + _destroyFrameBuffers(); + m_renderPass = VK_NULL_HANDLE; + } + if (renderPass != VK_NULL_HANDLE) + { + SLANG_RETURN_ON_FAIL(_createFrameBuffers(renderPass)); + } + m_renderPass = renderPass; + return SLANG_OK; +} + +SlangResult VulkanSwapChain::_createSwapChain() +{ + if (hasValidSwapChain()) + { + return SLANG_OK; + } + + int width, height; + getWindowSize(&width, &height); + + VkExtent2D imageExtent = {}; + imageExtent.width = width; + imageExtent.height = height; + + m_width = width; + m_height = height; + + // catch this before throwing error + if (m_width == 0 || m_height == 0) + { + return SLANG_FAIL; + } + + // It is necessary to query the caps -> otherwise the LunarG verification layer will issue an error + { + VkSurfaceCapabilitiesKHR surfaceCaps; + + SLANG_VK_RETURN_ON_FAIL(m_api->vkGetPhysicalDeviceSurfaceCapabilitiesKHR(m_api->m_physicalDevice, m_surface, &surfaceCaps)); + } + + List<VkPresentModeKHR> presentModes; + uint32_t numPresentModes = 0; + m_api->vkGetPhysicalDeviceSurfacePresentModesKHR(m_api->m_physicalDevice, m_surface, &numPresentModes, nullptr); + presentModes.SetSize(numPresentModes); + m_api->vkGetPhysicalDeviceSurfacePresentModesKHR(m_api->m_physicalDevice, m_surface, &numPresentModes, presentModes.Buffer()); + + { + int numCheckPresentOptions = 3; + VkPresentModeKHR presentOptions[] = { VK_PRESENT_MODE_IMMEDIATE_KHR, VK_PRESENT_MODE_MAILBOX_KHR, VK_PRESENT_MODE_FIFO_KHR }; + if (m_vsync) + { + presentOptions[0] = VK_PRESENT_MODE_FIFO_KHR; + presentOptions[1] = VK_PRESENT_MODE_IMMEDIATE_KHR; + presentOptions[2] = VK_PRESENT_MODE_MAILBOX_KHR; + } + + m_presentMode = VK_PRESENT_MODE_MAX_ENUM_KHR; // Invalid + + // Find the first option that's available on the device + for (int j = 0; j < numCheckPresentOptions; j++) + { + if (presentModes.IndexOf(presentOptions[j]) != UInt(-1)) + { + m_presentMode = presentOptions[j]; + break; + } + } + + if (m_presentMode == VK_PRESENT_MODE_MAX_ENUM_KHR) + { + return SLANG_FAIL; + } + } + + VkSwapchainKHR oldSwapchain = VK_NULL_HANDLE; + + VkSwapchainCreateInfoKHR swapchainDesc = {}; + swapchainDesc.sType = VK_STRUCTURE_TYPE_SWAPCHAIN_CREATE_INFO_KHR; + swapchainDesc.surface = m_surface; + swapchainDesc.minImageCount = 3; + swapchainDesc.imageFormat = m_format; + swapchainDesc.imageColorSpace = VK_COLOR_SPACE_SRGB_NONLINEAR_KHR; + swapchainDesc.imageExtent = imageExtent; + swapchainDesc.imageArrayLayers = 1; + swapchainDesc.imageUsage = VK_IMAGE_USAGE_COLOR_ATTACHMENT_BIT | VK_IMAGE_USAGE_TRANSFER_DST_BIT; + swapchainDesc.imageSharingMode = VK_SHARING_MODE_EXCLUSIVE; + swapchainDesc.preTransform = VK_SURFACE_TRANSFORM_IDENTITY_BIT_KHR; + swapchainDesc.compositeAlpha = VK_COMPOSITE_ALPHA_OPAQUE_BIT_KHR; + swapchainDesc.presentMode = m_presentMode; + swapchainDesc.clipped = VK_TRUE; + swapchainDesc.oldSwapchain = oldSwapchain; + + SLANG_VK_RETURN_ON_FAIL(m_api->vkCreateSwapchainKHR(m_api->m_device, &swapchainDesc, nullptr, &m_swapChain)); + + uint32_t numSwapChainImages = 0; + m_api->vkGetSwapchainImagesKHR(m_api->m_device, m_swapChain, &numSwapChainImages, nullptr); + + { + List<VkImage> images; + images.SetSize(numSwapChainImages); + + m_api->vkGetSwapchainImagesKHR(m_api->m_device, m_swapChain, &numSwapChainImages, images.Buffer()); + + m_images.SetSize(numSwapChainImages); + for (int i = 0; i < int(numSwapChainImages); ++i) + { + Image& dstImage = m_images[i]; + dstImage.m_image = images[i]; + + } + } + + { + VkImageViewCreateInfo createInfo = {}; + createInfo.sType = VK_STRUCTURE_TYPE_IMAGE_VIEW_CREATE_INFO; + + createInfo.viewType = VK_IMAGE_VIEW_TYPE_2D; + createInfo.format = m_format; + + createInfo.components.r = VK_COMPONENT_SWIZZLE_IDENTITY; + createInfo.components.g = VK_COMPONENT_SWIZZLE_IDENTITY; + createInfo.components.b = VK_COMPONENT_SWIZZLE_IDENTITY; + createInfo.components.a = VK_COMPONENT_SWIZZLE_IDENTITY; + + createInfo.subresourceRange.aspectMask = VK_IMAGE_ASPECT_COLOR_BIT; + createInfo.subresourceRange.baseMipLevel = 0; + createInfo.subresourceRange.levelCount = 1; + createInfo.subresourceRange.baseArrayLayer = 0; + createInfo.subresourceRange.layerCount = 1; + + for (int i = 0; i < int(numSwapChainImages); ++i) + { + Image& image = m_images[i]; + + createInfo.image = image.m_image; + + SLANG_VK_RETURN_ON_FAIL(m_api->vkCreateImageView(m_api->m_device, &createInfo, nullptr, &image.m_imageView)); + } + } + + if (m_renderPass != VK_NULL_HANDLE) + { + _createFrameBuffers(m_renderPass); + } + + return SLANG_OK; +} + +void VulkanSwapChain::_destroySwapChain() +{ + if (!hasValidSwapChain()) + { + return; + } + + m_deviceQueue->waitForIdle(); + + if (m_renderPass != VK_NULL_HANDLE) + { + _destroyFrameBuffers(); + } + + for (int i = 0; i < int(m_images.Count()); ++i) + { + Image& image = m_images[i]; + + if (image.m_imageView != VK_NULL_HANDLE) + { + m_api->vkDestroyImageView(m_api->m_device, image.m_imageView, nullptr); + } + } + + if (m_swapChain != VK_NULL_HANDLE) + { + m_api->vkDestroySwapchainKHR(m_api->m_device, m_swapChain, nullptr); + m_swapChain = VK_NULL_HANDLE; + } + + // Mark that it is no longer used + m_images.Clear(); +} + +VulkanSwapChain::~VulkanSwapChain() +{ + _destroySwapChain(); + + if (m_surface) + { + m_api->vkDestroySurfaceKHR(m_api->m_instance, m_surface, nullptr); + m_surface = VK_NULL_HANDLE; + } +} + +int VulkanSwapChain::nextFrontImageIndex() +{ + if (!hasValidSwapChain()) + { + if (SLANG_FAILED(_createSwapChain())) + { + return -1; + } + } + + VkSemaphore beginFrameSemaphore = m_deviceQueue->makeCurrent(VulkanDeviceQueue::EventType::BeginFrame); + + uint32_t swapChainIndex = 0; + VkResult result = m_api->vkAcquireNextImageKHR(m_api->m_device, m_swapChain, UINT64_MAX, beginFrameSemaphore, VK_NULL_HANDLE, &swapChainIndex); + + if (result != VK_SUCCESS) + { + _destroySwapChain(); + return -1; + } + m_currentSwapChainIndex = int(swapChainIndex); + return swapChainIndex; +} + +void VulkanSwapChain::present(bool vsync) +{ + if (!hasValidSwapChain()) + { + m_deviceQueue->flush(); + return; + } + + VkSemaphore endFrameSemaphore = m_deviceQueue->makeCurrent(VulkanDeviceQueue::EventType::EndFrame); + + m_deviceQueue->flushStepA(); + + uint32_t swapChainIndices[] = { uint32_t(m_currentSwapChainIndex) }; + + VkPresentInfoKHR presentInfo = {}; + presentInfo.sType = VK_STRUCTURE_TYPE_PRESENT_INFO_KHR; + presentInfo.swapchainCount = 1; + presentInfo.pSwapchains = &m_swapChain; + presentInfo.pImageIndices = swapChainIndices; + presentInfo.waitSemaphoreCount = 1; + presentInfo.pWaitSemaphores = &endFrameSemaphore; + + VkResult result = m_api->vkQueuePresentKHR(m_deviceQueue->getQueue(), &presentInfo); + + m_deviceQueue->makeCompleted(VulkanDeviceQueue::EventType::EndFrame); + + m_deviceQueue->flushStepB(); + + if (result != VK_SUCCESS || m_vsync != vsync) + { + m_vsync = vsync; + _destroySwapChain(); + } +} + +} // renderer_test diff --git a/tools/render-test/vk-swap-chain.h b/tools/render-test/vk-swap-chain.h new file mode 100644 index 000000000..2c9fb588d --- /dev/null +++ b/tools/render-test/vk-swap-chain.h @@ -0,0 +1,141 @@ +// vk-swap-chain.h +#pragma once + +#include "vk-api.h" +#include "vk-device-queue.h" + +#include "render.h" + +#include "../../source/core/list.h" + +namespace renderer_test { + +struct VulkanSwapChain +{ + /* enum + { + kMaxImages = 8, + }; */ + + /// Base class for platform specific information + struct PlatformDesc + { + }; + +#if SLANG_WINDOWS_FAMILY + struct WinPlatformDesc: public PlatformDesc + { + HINSTANCE m_hinstance; + HWND m_hwnd; + }; +#else + struct XPlatformDesc : public PlatformDesc + { + Display* m_display; + Window m_window; + }; +#endif + + struct Desc + { + void init() + { + m_format = Format::Unknown; + m_depthFormatTypeless = Format::Unknown; + m_depthFormat = Format::Unknown; + m_textureDepthFormat = Format::Unknown; + } + + Format m_format; + //bool m_enableFormat; + Format m_depthFormatTypeless; + Format m_depthFormat; + Format m_textureDepthFormat; + }; + + struct Image + { + VkImage m_image = VK_NULL_HANDLE; + VkImageView m_imageView = VK_NULL_HANDLE; + VkFramebuffer m_frameBuffer = VK_NULL_HANDLE; + }; + + + /// Must be called before the swap chain can be used + SlangResult init(VulkanDeviceQueue* deviceQueue, const Desc& desc, const PlatformDesc* platformDesc); + + /// Create the frame buffers (they must be compatible with the supplied renderPass) + SlangResult createFrameBuffers(VkRenderPass renderPass); + + /// Returned the desc used to construct the swap chain. + /// Is invalid if init hasn't returned with successful result. + const Desc& getDesc() const { return m_desc; } + + /// True if the swap chain is available + bool hasValidSwapChain() const { return m_images.Count() > 0; } + + /// Present to the display + void present(bool vsync); + + /// Get the current size of the window (in pixels written to widthOut, heightOut) + void getWindowSize(int* widthOut, int* heightOut) const; + + /// Get the VkFormat for the back buffer + VkFormat getVkFormat() const { return m_format; } + + /// Get width of the back buffers + int getWidth() const { return m_width; } + /// Get the height of the back buffer + int getHeight() const { return m_height; } + + /// Get the detail about the images + const Slang::List<Image>& getImages() const { return m_images; } + + /// Get the next front render image index. Returns -1, if image couldn't be found + int nextFrontImageIndex(); + + /// Dtor + ~VulkanSwapChain(); + + protected: + + + template <typename T> + void _setPlatformDesc(const T& desc) + { + const PlatformDesc* check = &desc; + int size = (sizeof(T) + sizeof(void*) - 1) / sizeof(void*); + m_platformDescBuffer.SetSize(size); + *(T*)m_platformDescBuffer.Buffer() = desc; + } + template <typename T> + const T* _getPlatformDesc() const { return static_cast<const T*>((const PlatformDesc*)m_platformDescBuffer.Buffer()); } + SlangResult _createSwapChain(); + void _destroySwapChain(); + SlangResult _createFrameBuffers(VkRenderPass renderPass); + void _destroyFrameBuffers(); + + bool m_vsync = true; + int m_width = 0; + int m_height = 0; + + VkPresentModeKHR m_presentMode = VK_PRESENT_MODE_IMMEDIATE_KHR; + VkFormat m_format = VK_FORMAT_UNDEFINED; ///< The format used for backbuffer. Valid after successful init. + + VkSurfaceKHR m_surface = VK_NULL_HANDLE; + VkSwapchainKHR m_swapChain = VK_NULL_HANDLE; + + VkRenderPass m_renderPass = VK_NULL_HANDLE; //< Not owned + + int m_currentSwapChainIndex = 0; + + Slang::List<Image> m_images; + + VulkanDeviceQueue* m_deviceQueue = nullptr; + const VulkanApi* m_api = nullptr; + + Desc m_desc; ///< The desc used to init this swap chain + Slang::List<void*> m_platformDescBuffer; ///< Buffer to hold the platform specific description parameters (as passed in platformDesc) +}; + +} // renderer_test diff --git a/tools/render-test/vk-util.cpp b/tools/render-test/vk-util.cpp new file mode 100644 index 000000000..022d15a32 --- /dev/null +++ b/tools/render-test/vk-util.cpp @@ -0,0 +1,58 @@ +// vk-util.cpp +#include "vk-util.h" + +#include <stdlib.h> +#include <stdio.h> + +namespace renderer_test { + +/* static */VkFormat VulkanUtil::getVkFormat(Format format) +{ + switch (format) + { + case Format::RGBA_Float32: return VK_FORMAT_R32G32B32A32_SFLOAT; + case Format::RGB_Float32: return VK_FORMAT_R32G32B32_SFLOAT; + case Format::RG_Float32: return VK_FORMAT_R32G32_SFLOAT; + case Format::R_Float32: return VK_FORMAT_R32_SFLOAT; + case Format::RGBA_Unorm_UInt8: return VK_FORMAT_R8G8B8A8_UNORM; + + case Format::D_Float32: return VK_FORMAT_D32_SFLOAT; + case Format::D_Unorm24_S8: return VK_FORMAT_D24_UNORM_S8_UINT; + + default: return VK_FORMAT_UNDEFINED; + } +} + +/* static */SlangResult VulkanUtil::toSlangResult(VkResult res) +{ + return (res == VK_SUCCESS) ? SLANG_OK : SLANG_FAIL; +} + +/* static */Slang::Result VulkanUtil::handleFail(VkResult res) +{ + if (res != VK_SUCCESS) + { + assert(!"Vulkan returned a failure"); + } + return toSlangResult(res); +} + +/* static */void VulkanUtil::checkFail(VkResult res) +{ + assert(res != VK_SUCCESS); + assert(!"Vulkan check failed"); + +} + +/* static */VkPrimitiveTopology VulkanUtil::getVkPrimitiveTopology(PrimitiveTopology topology) +{ + switch (topology) + { + case PrimitiveTopology::TriangleList: return VK_PRIMITIVE_TOPOLOGY_TRIANGLE_LIST; + default: break; + } + assert(!"Unknown topology"); + return VK_PRIMITIVE_TOPOLOGY_MAX_ENUM; +} + +} // renderer_test diff --git a/tools/render-test/vk-util.h b/tools/render-test/vk-util.h new file mode 100644 index 000000000..6e2c2388c --- /dev/null +++ b/tools/render-test/vk-util.h @@ -0,0 +1,39 @@ +// vk-util.h +#pragma once + +#include "vk-api.h" +#include "render.h" + +// Macros to make testing vulkan return codes simpler + +/// SLANG_VK_RETURN_ON_FAIL can be used in a similar way to SLANG_RETURN_ON_FAIL macro, except it will turn a vulkan failure into Slang::Result in the process +/// Calls handleFail which on debug builds asserts +#define SLANG_VK_RETURN_ON_FAIL(x) { VkResult _res = x; if (_res != VK_SUCCESS) { return VulkanUtil::handleFail(_res); } } + +/// Is similar to SLANG_VK_RETURN_ON_FAIL, but does not return. Will call checkFail on failure - which asserts on debug builds. +#define SLANG_VK_CHECK(x) { VkResult _res = x; if (_res != VK_SUCCESS) { VulkanUtil::checkFail(_res); } } + +namespace renderer_test { + +// Utility functions for Vulkan +struct VulkanUtil +{ + /// Get the equivalent VkFormat from the format + /// Returns VK_FORMAT_UNDEFINED if a match is not found + static VkFormat getVkFormat(Format format); + + /// Called by SLANG_VK_RETURN_FAIL if a res is a failure. + /// On debug builds this will cause an assertion on failure. + static Slang::Result handleFail(VkResult res); + /// Called when a failure has occurred with SLANG_VK_CHECK - will typically assert. + static void checkFail(VkResult res); + + /// Get the VkPrimitiveTopology for the given topology. + /// Returns VK_PRIMITIVE_TOPOLOGY_MAX_ENUM on failure + static VkPrimitiveTopology getVkPrimitiveTopology(PrimitiveTopology topology); + + /// Returns Slang::Result equivalent of a VkResult + static Slang::Result toSlangResult(VkResult res); +}; + +} // renderer_test |
