summaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
authorjsmall-nvidia <jsmall@nvidia.com>2018-05-03 14:25:13 -0400
committerGitHub <noreply@github.com>2018-05-03 14:25:13 -0400
commit367f3a78a40731da45ee12b9a18c94707f1d1429 (patch)
tree5993ef627e1a094ea1d401c31e6b00e3c63c308a
parent78935493587ec65a199d844327613021667acc1b (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.
-rw-r--r--tools/render-test/d3d-util.cpp5
-rw-r--r--tools/render-test/main.cpp68
-rw-r--r--tools/render-test/options.cpp32
-rw-r--r--tools/render-test/options.h67
-rw-r--r--tools/render-test/render-d3d11.cpp33
-rw-r--r--tools/render-test/render-d3d12.cpp87
-rw-r--r--tools/render-test/render-gl.cpp10
-rw-r--r--tools/render-test/render-test.vcxproj10
-rw-r--r--tools/render-test/render-test.vcxproj.filters30
-rw-r--r--tools/render-test/render-vk.cpp1310
-rw-r--r--tools/render-test/render.cpp95
-rw-r--r--tools/render-test/render.h110
-rw-r--r--tools/render-test/slang-support.cpp2
-rw-r--r--tools/render-test/vk-api.cpp136
-rw-r--r--tools/render-test/vk-api.h187
-rw-r--r--tools/render-test/vk-device-queue.cpp198
-rw-r--r--tools/render-test/vk-device-queue.h95
-rw-r--r--tools/render-test/vk-module.cpp75
-rw-r--r--tools/render-test/vk-module.h38
-rw-r--r--tools/render-test/vk-swap-chain.cpp421
-rw-r--r--tools/render-test/vk-swap-chain.h141
-rw-r--r--tools/render-test/vk-util.cpp58
-rw-r--r--tools/render-test/vk-util.h39
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, &copyInfo);
+ m_api.vkCmdCopyBuffer(commandBuffer, buffer->m_uploadBuffer.m_buffer, buffer->m_buffer.m_buffer, 1, &copyInfo);
- 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, &copyInfo);
+ m_api.vkCmdCopyBuffer(commandBuffer, buffer->m_buffer.m_buffer, staging.m_buffer, 1, &copyInfo);
- 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, &copyInfo);
+ m_api.vkCmdCopyBuffer(commandBuffer, buffer->m_uploadBuffer.m_buffer, buffer->m_buffer.m_buffer, 1, &copyInfo);
- 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