Ajout de Jolt Physics + 1ere version des factory entitecomposants - camera, transform, rigidbody, collider, renderer

This commit is contained in:
Tom Ray
2026-03-22 00:28:03 +01:00
parent 6695d46bcd
commit 48348936a8
1147 changed files with 214331 additions and 353 deletions

View File

@@ -0,0 +1,21 @@
// Jolt Physics Library (https://github.com/jrouwe/JoltPhysics)
// SPDX-FileCopyrightText: 2024 Jorrit Rouwe
// SPDX-License-Identifier: MIT
#pragma once
#include <vulkan/vulkan.h>
/// Simple wrapper class to manage a Vulkan buffer
class BufferVK
{
public:
VkBuffer mBuffer = VK_NULL_HANDLE;
VkDeviceMemory mMemory = VK_NULL_HANDLE;
VkDeviceSize mOffset = 0;
VkDeviceSize mSize = 0;
VkBufferUsageFlags mUsage;
VkMemoryPropertyFlags mProperties;
VkDeviceSize mAllocatedSize;
};

View File

@@ -0,0 +1,32 @@
// Jolt Physics Library (https://github.com/jrouwe/JoltPhysics)
// SPDX-FileCopyrightText: 2024 Jorrit Rouwe
// SPDX-License-Identifier: MIT
#include <TestFramework.h>
#include <Renderer/VK/ConstantBufferVK.h>
#include <Renderer/VK/RendererVK.h>
#include <Renderer/VK/FatalErrorIfFailedVK.h>
ConstantBufferVK::ConstantBufferVK(RendererVK *inRenderer, VkDeviceSize inBufferSize) :
mRenderer(inRenderer)
{
mRenderer->CreateBuffer(inBufferSize, VK_BUFFER_USAGE_UNIFORM_BUFFER_BIT, VK_MEMORY_PROPERTY_HOST_VISIBLE_BIT | VK_MEMORY_PROPERTY_HOST_COHERENT_BIT, mBuffer);
}
ConstantBufferVK::~ConstantBufferVK()
{
mRenderer->FreeBuffer(mBuffer);
}
void *ConstantBufferVK::MapInternal()
{
void *data = nullptr;
FatalErrorIfFailed(vkMapMemory(mRenderer->GetDevice(), mBuffer.mMemory, mBuffer.mOffset, mBuffer.mSize, 0, &data));
return data;
}
void ConstantBufferVK::Unmap()
{
vkUnmapMemory(mRenderer->GetDevice(), mBuffer.mMemory);
}

View File

@@ -0,0 +1,30 @@
// Jolt Physics Library (https://github.com/jrouwe/JoltPhysics)
// SPDX-FileCopyrightText: 2024 Jorrit Rouwe
// SPDX-License-Identifier: MIT
#pragma once
#include <Renderer/VK/BufferVK.h>
class RendererVK;
/// A binary blob that can be used to pass constants to a shader
class ConstantBufferVK
{
public:
/// Constructor
ConstantBufferVK(RendererVK *inRenderer, VkDeviceSize inBufferSize);
~ConstantBufferVK();
/// Map / unmap buffer (get pointer to data). This will discard all data in the buffer.
template <typename T> T * Map() { return reinterpret_cast<T *>(MapInternal()); }
void Unmap();
VkBuffer GetBuffer() const { return mBuffer.mBuffer; }
private:
void * MapInternal();
RendererVK * mRenderer;
BufferVK mBuffer;
};

View File

@@ -0,0 +1,14 @@
// Jolt Physics Library (https://github.com/jrouwe/JoltPhysics)
// SPDX-FileCopyrightText: 2024 Jorrit Rouwe
// SPDX-License-Identifier: MIT
#include <TestFramework.h>
#include <Renderer/VK/FatalErrorIfFailedVK.h>
#include <Utils/Log.h>
void FatalErrorIfFailed(VkResult inVkResult)
{
if (inVkResult != VK_SUCCESS)
FatalError("Vulkan error returned: %d", inVkResult);
}

View File

@@ -0,0 +1,11 @@
// Jolt Physics Library (https://github.com/jrouwe/JoltPhysics)
// SPDX-FileCopyrightText: 2024 Jorrit Rouwe
// SPDX-License-Identifier: MIT
#pragma once
#include <vulkan/vulkan.h>
/// Convert Vulkan error to readable text and alert
void FatalErrorIfFailed(VkResult inVkResult);

View File

@@ -0,0 +1,175 @@
// Jolt Physics Library (https://github.com/jrouwe/JoltPhysics)
// SPDX-FileCopyrightText: 2024 Jorrit Rouwe
// SPDX-License-Identifier: MIT
#include <TestFramework.h>
#include <Renderer/VK/PipelineStateVK.h>
#include <Renderer/VK/RendererVK.h>
#include <Renderer/VK/FatalErrorIfFailedVK.h>
PipelineStateVK::PipelineStateVK(RendererVK *inRenderer, const VertexShaderVK *inVertexShader, const EInputDescription *inInputDescription, uint inInputDescriptionCount, const PixelShaderVK *inPixelShader, EDrawPass inDrawPass, EFillMode inFillMode, ETopology inTopology, EDepthTest inDepthTest, EBlendMode inBlendMode, ECullMode inCullMode) :
mRenderer(inRenderer),
mVertexShader(inVertexShader),
mPixelShader(inPixelShader)
{
VkPipelineShaderStageCreateInfo shader_stages[] = { inVertexShader->mStageInfo, inPixelShader->mStageInfo };
// TODO: This doesn't follow the SPIR-V alignment rules
Array<VkVertexInputAttributeDescription> attribute_descriptions;
VkVertexInputAttributeDescription temp_vtx = { }, temp_instance = { };
temp_instance.binding = 1;
uint instance_alignment = 1;
for (uint i = 0; i < inInputDescriptionCount; ++i)
switch (inInputDescription[i])
{
case EInputDescription::Position:
case EInputDescription::Normal:
temp_vtx.format = VK_FORMAT_R32G32B32_SFLOAT;
attribute_descriptions.push_back(temp_vtx);
temp_vtx.offset += 3 * sizeof(float);
break;
case EInputDescription::Color:
temp_vtx.format = VK_FORMAT_R8G8B8A8_UNORM;
attribute_descriptions.push_back(temp_vtx);
temp_vtx.offset += 4 * sizeof(uint8);
break;
case EInputDescription::TexCoord:
temp_vtx.format = VK_FORMAT_R32G32_SFLOAT;
attribute_descriptions.push_back(temp_vtx);
temp_vtx.offset += 2 * sizeof(float);
break;
case EInputDescription::InstanceColor:
instance_alignment = max(instance_alignment, 4u);
temp_instance.format = VK_FORMAT_R8G8B8A8_UNORM;
attribute_descriptions.push_back(temp_instance);
temp_instance.offset += 4 * sizeof(uint8);
break;
case EInputDescription::InstanceTransform:
case EInputDescription::InstanceInvTransform:
instance_alignment = max(instance_alignment, 16u);
temp_instance.format = VK_FORMAT_R32G32B32A32_SFLOAT;
for (int j = 0; j < 4; ++j)
{
attribute_descriptions.push_back(temp_instance);
temp_instance.offset += 4 * sizeof(float);
}
break;
}
for (uint32 i = 0; i < uint32(attribute_descriptions.size()); ++i)
attribute_descriptions[i].location = i;
VkVertexInputBindingDescription binding_description[2];
binding_description[0].binding = 0;
binding_description[0].stride = temp_vtx.offset;
binding_description[0].inputRate = VK_VERTEX_INPUT_RATE_VERTEX;
binding_description[1].binding = 1;
binding_description[1].stride = AlignUp(temp_instance.offset, instance_alignment);
binding_description[1].inputRate = VK_VERTEX_INPUT_RATE_INSTANCE;
VkPipelineVertexInputStateCreateInfo vertex_input_info = {};
vertex_input_info.sType = VK_STRUCTURE_TYPE_PIPELINE_VERTEX_INPUT_STATE_CREATE_INFO;
vertex_input_info.vertexBindingDescriptionCount = temp_instance.offset > 0? 2 : 1;
vertex_input_info.pVertexBindingDescriptions = binding_description;
vertex_input_info.vertexAttributeDescriptionCount = uint32(attribute_descriptions.size());
vertex_input_info.pVertexAttributeDescriptions = attribute_descriptions.data();
VkPipelineInputAssemblyStateCreateInfo input_assembly = {};
input_assembly.sType = VK_STRUCTURE_TYPE_PIPELINE_INPUT_ASSEMBLY_STATE_CREATE_INFO;
input_assembly.topology = inTopology == ETopology::Triangle? VK_PRIMITIVE_TOPOLOGY_TRIANGLE_LIST : VK_PRIMITIVE_TOPOLOGY_LINE_LIST;
input_assembly.primitiveRestartEnable = VK_FALSE;
VkPipelineViewportStateCreateInfo viewport_state = {};
viewport_state.sType = VK_STRUCTURE_TYPE_PIPELINE_VIEWPORT_STATE_CREATE_INFO;
viewport_state.viewportCount = 1;
viewport_state.scissorCount = 1;
VkPipelineRasterizationStateCreateInfo rasterizer = {};
rasterizer.sType = VK_STRUCTURE_TYPE_PIPELINE_RASTERIZATION_STATE_CREATE_INFO;
rasterizer.depthClampEnable = VK_FALSE;
rasterizer.rasterizerDiscardEnable = VK_FALSE;
rasterizer.polygonMode = inFillMode == EFillMode::Solid? VK_POLYGON_MODE_FILL : VK_POLYGON_MODE_LINE;
rasterizer.lineWidth = 1.0f;
rasterizer.cullMode = inCullMode == ECullMode::Backface? VK_CULL_MODE_BACK_BIT : VK_CULL_MODE_FRONT_BIT;
rasterizer.frontFace = VK_FRONT_FACE_COUNTER_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;
VkPipelineDepthStencilStateCreateInfo depth_stencil = {};
depth_stencil.sType = VK_STRUCTURE_TYPE_PIPELINE_DEPTH_STENCIL_STATE_CREATE_INFO;
depth_stencil.depthTestEnable = inDepthTest == EDepthTest::On? VK_TRUE : VK_FALSE;
depth_stencil.depthWriteEnable = inDepthTest == EDepthTest::On? VK_TRUE : VK_FALSE;
depth_stencil.depthCompareOp = VK_COMPARE_OP_GREATER_OR_EQUAL; // Reverse-Z, greater is closer
VkPipelineColorBlendAttachmentState color_blend_attachment = {};
color_blend_attachment.colorWriteMask = VK_COLOR_COMPONENT_R_BIT | VK_COLOR_COMPONENT_G_BIT | VK_COLOR_COMPONENT_B_BIT | VK_COLOR_COMPONENT_A_BIT;
switch (inBlendMode)
{
case EBlendMode::Write:
color_blend_attachment.blendEnable = VK_FALSE;
break;
case EBlendMode::AlphaBlend:
color_blend_attachment.blendEnable = VK_TRUE;
color_blend_attachment.srcColorBlendFactor = VK_BLEND_FACTOR_SRC_ALPHA;
color_blend_attachment.dstColorBlendFactor = VK_BLEND_FACTOR_ONE_MINUS_SRC_ALPHA;
color_blend_attachment.colorBlendOp = VK_BLEND_OP_ADD;
color_blend_attachment.srcAlphaBlendFactor = VK_BLEND_FACTOR_ZERO;
color_blend_attachment.dstAlphaBlendFactor = VK_BLEND_FACTOR_ZERO;
color_blend_attachment.alphaBlendOp = VK_BLEND_OP_ADD;
break;
}
VkPipelineColorBlendStateCreateInfo color_blending = {};
color_blending.sType = VK_STRUCTURE_TYPE_PIPELINE_COLOR_BLEND_STATE_CREATE_INFO;
color_blending.logicOpEnable = VK_FALSE;
color_blending.logicOp = VK_LOGIC_OP_COPY;
color_blending.attachmentCount = 1;
color_blending.pAttachments = &color_blend_attachment;
VkDynamicState dynamic_states[] = {
VK_DYNAMIC_STATE_VIEWPORT,
VK_DYNAMIC_STATE_SCISSOR
};
VkPipelineDynamicStateCreateInfo dynamic_state = {};
dynamic_state.sType = VK_STRUCTURE_TYPE_PIPELINE_DYNAMIC_STATE_CREATE_INFO;
dynamic_state.dynamicStateCount = std::size(dynamic_states);
dynamic_state.pDynamicStates = dynamic_states;
VkGraphicsPipelineCreateInfo pipeline_info = {};
pipeline_info.sType = VK_STRUCTURE_TYPE_GRAPHICS_PIPELINE_CREATE_INFO;
pipeline_info.stageCount = std::size(shader_stages);
pipeline_info.pStages = shader_stages;
pipeline_info.pVertexInputState = &vertex_input_info;
pipeline_info.pInputAssemblyState = &input_assembly;
pipeline_info.pViewportState = &viewport_state;
pipeline_info.pRasterizationState = &rasterizer;
pipeline_info.pMultisampleState = &multisampling;
pipeline_info.pDepthStencilState = &depth_stencil;
pipeline_info.pColorBlendState = &color_blending;
pipeline_info.pDynamicState = &dynamic_state;
pipeline_info.layout = mRenderer->GetPipelineLayout();
pipeline_info.renderPass = inDrawPass == EDrawPass::Normal? mRenderer->GetRenderPass() : mRenderer->GetRenderPassShadow();
FatalErrorIfFailed(vkCreateGraphicsPipelines(mRenderer->GetDevice(), VK_NULL_HANDLE, 1, &pipeline_info, nullptr, &mGraphicsPipeline));
}
PipelineStateVK::~PipelineStateVK()
{
vkDeviceWaitIdle(mRenderer->GetDevice());
vkDestroyPipeline(mRenderer->GetDevice(), mGraphicsPipeline, nullptr);
}
void PipelineStateVK::Activate()
{
vkCmdBindPipeline(mRenderer->GetCommandBuffer(), VK_PIPELINE_BIND_POINT_GRAPHICS, mGraphicsPipeline);
}

View File

@@ -0,0 +1,30 @@
// Jolt Physics Library (https://github.com/jrouwe/JoltPhysics)
// SPDX-FileCopyrightText: 2024 Jorrit Rouwe
// SPDX-License-Identifier: MIT
#pragma once
#include <Renderer/PipelineState.h>
#include <Renderer/VK/VertexShaderVK.h>
#include <Renderer/VK/PixelShaderVK.h>
class RendererVK;
/// Vulkan pipeline state object
class PipelineStateVK : public PipelineState
{
public:
/// Constructor
PipelineStateVK(RendererVK *inRenderer, const VertexShaderVK *inVertexShader, const EInputDescription *inInputDescription, uint inInputDescriptionCount, const PixelShaderVK *inPixelShader, EDrawPass inDrawPass, EFillMode inFillMode, ETopology inTopology, EDepthTest inDepthTest, EBlendMode inBlendMode, ECullMode inCullMode);
virtual ~PipelineStateVK() override;
/// Make this pipeline state active (any primitives rendered after this will use this state)
virtual void Activate() override;
private:
RendererVK * mRenderer;
RefConst<VertexShaderVK> mVertexShader;
RefConst<PixelShaderVK> mPixelShader;
VkPipeline mGraphicsPipeline;
};

View File

@@ -0,0 +1,34 @@
// Jolt Physics Library (https://github.com/jrouwe/JoltPhysics)
// SPDX-FileCopyrightText: 2024 Jorrit Rouwe
// SPDX-License-Identifier: MIT
#pragma once
#include <Renderer/PixelShader.h>
#include <vulkan/vulkan.h>
/// Pixel shader handle for Vulkan
class PixelShaderVK : public PixelShader
{
public:
/// Constructor
PixelShaderVK(VkDevice inDevice, VkShaderModule inShaderModule) :
mDevice(inDevice),
mStageInfo()
{
mStageInfo.sType = VK_STRUCTURE_TYPE_PIPELINE_SHADER_STAGE_CREATE_INFO;
mStageInfo.stage = VK_SHADER_STAGE_FRAGMENT_BIT;
mStageInfo.module = inShaderModule;
mStageInfo.pName = "main";
}
/// Destructor
virtual ~PixelShaderVK() override
{
vkDestroyShaderModule(mDevice, mStageInfo.module, nullptr);
}
VkDevice mDevice;
VkPipelineShaderStageCreateInfo mStageInfo;
};

View File

@@ -0,0 +1,57 @@
// Jolt Physics Library (https://github.com/jrouwe/JoltPhysics)
// SPDX-FileCopyrightText: 2024 Jorrit Rouwe
// SPDX-License-Identifier: MIT
#include <TestFramework.h>
#include <Renderer/VK/RenderInstancesVK.h>
#include <Renderer/VK/RenderPrimitiveVK.h>
#include <Renderer/VK/FatalErrorIfFailedVK.h>
void RenderInstancesVK::Clear()
{
mRenderer->FreeBuffer(mInstancesBuffer);
}
void RenderInstancesVK::CreateBuffer(int inNumInstances, int inInstanceSize)
{
Clear();
mRenderer->CreateBuffer(VkDeviceSize(inNumInstances) * inInstanceSize, VK_BUFFER_USAGE_VERTEX_BUFFER_BIT, VK_MEMORY_PROPERTY_HOST_VISIBLE_BIT | VK_MEMORY_PROPERTY_HOST_COHERENT_BIT, mInstancesBuffer);
}
void *RenderInstancesVK::Lock()
{
void *data;
FatalErrorIfFailed(vkMapMemory(mRenderer->GetDevice(), mInstancesBuffer.mMemory, mInstancesBuffer.mOffset, mInstancesBuffer.mSize, 0, &data));
return data;
}
void RenderInstancesVK::Unlock()
{
vkUnmapMemory(mRenderer->GetDevice(), mInstancesBuffer.mMemory);
}
void RenderInstancesVK::Draw(RenderPrimitive *inPrimitive, int inStartInstance, int inNumInstances) const
{
if (inNumInstances <= 0)
return;
VkCommandBuffer command_buffer = mRenderer->GetCommandBuffer();
RenderPrimitiveVK *primitive = static_cast<RenderPrimitiveVK *>(inPrimitive);
VkBuffer buffers[] = { primitive->mVertexBuffer.mBuffer, mInstancesBuffer.mBuffer };
VkDeviceSize offsets[] = { 0, 0 };
vkCmdBindVertexBuffers(command_buffer, 0, 2, buffers, offsets);
if (primitive->mIndexBuffer.mBuffer == VK_NULL_HANDLE)
{
vkCmdDraw(command_buffer, primitive->mNumVtxToDraw, inNumInstances, 0, inStartInstance);
}
else
{
vkCmdBindIndexBuffer(command_buffer, primitive->mIndexBuffer.mBuffer, 0, VK_INDEX_TYPE_UINT32);
vkCmdDrawIndexed(command_buffer, primitive->mNumIdxToDraw, inNumInstances, 0, 0, inStartInstance);
}
}

View File

@@ -0,0 +1,35 @@
// Jolt Physics Library (https://github.com/jrouwe/JoltPhysics)
// SPDX-FileCopyrightText: 2024 Jorrit Rouwe
// SPDX-License-Identifier: MIT
#pragma once
#include <Renderer/VK/RendererVK.h>
#include <Renderer/RenderInstances.h>
class RenderPrimitive;
/// Vulkan implementation of a render instances object
class RenderInstancesVK : public RenderInstances
{
public:
/// Constructor
RenderInstancesVK(RendererVK *inRenderer) : mRenderer(inRenderer) { }
virtual ~RenderInstancesVK() override { Clear(); }
/// Erase all instance data
virtual void Clear() override;
/// Instance buffer management functions
virtual void CreateBuffer(int inNumInstances, int inInstanceSize) override;
virtual void * Lock() override;
virtual void Unlock() override;
/// Draw the instances when context has been set by Renderer::BindShader
virtual void Draw(RenderPrimitive *inPrimitive, int inStartInstance, int inNumInstances) const override;
private:
RendererVK * mRenderer;
BufferVK mInstancesBuffer;
};

View File

@@ -0,0 +1,100 @@
// Jolt Physics Library (https://github.com/jrouwe/JoltPhysics)
// SPDX-FileCopyrightText: 2024 Jorrit Rouwe
// SPDX-License-Identifier: MIT
#include <TestFramework.h>
#include <Renderer/VK/RenderPrimitiveVK.h>
#include <Renderer/VK/FatalErrorIfFailedVK.h>
void RenderPrimitiveVK::ReleaseVertexBuffer()
{
mRenderer->FreeBuffer(mVertexBuffer);
mVertexBufferDeviceLocal = false;
RenderPrimitive::ReleaseVertexBuffer();
}
void RenderPrimitiveVK::ReleaseIndexBuffer()
{
mRenderer->FreeBuffer(mIndexBuffer);
mIndexBufferDeviceLocal = false;
RenderPrimitive::ReleaseIndexBuffer();
}
void RenderPrimitiveVK::CreateVertexBuffer(int inNumVtx, int inVtxSize, const void *inData)
{
RenderPrimitive::CreateVertexBuffer(inNumVtx, inVtxSize, inData);
VkDeviceSize size = VkDeviceSize(inNumVtx) * inVtxSize;
if (inData != nullptr)
{
mRenderer->CreateDeviceLocalBuffer(inData, size, VK_BUFFER_USAGE_VERTEX_BUFFER_BIT, mVertexBuffer);
mVertexBufferDeviceLocal = true;
}
else
mRenderer->CreateBuffer(size, VK_BUFFER_USAGE_VERTEX_BUFFER_BIT, VK_MEMORY_PROPERTY_HOST_VISIBLE_BIT | VK_MEMORY_PROPERTY_HOST_COHERENT_BIT, mVertexBuffer);
}
void *RenderPrimitiveVK::LockVertexBuffer()
{
JPH_ASSERT(!mVertexBufferDeviceLocal);
void *data;
FatalErrorIfFailed(vkMapMemory(mRenderer->GetDevice(), mVertexBuffer.mMemory, mVertexBuffer.mOffset, VkDeviceSize(mNumVtx) * mVtxSize, 0, &data));
return data;
}
void RenderPrimitiveVK::UnlockVertexBuffer()
{
vkUnmapMemory(mRenderer->GetDevice(), mVertexBuffer.mMemory);
}
void RenderPrimitiveVK::CreateIndexBuffer(int inNumIdx, const uint32 *inData)
{
RenderPrimitive::CreateIndexBuffer(inNumIdx, inData);
VkDeviceSize size = VkDeviceSize(inNumIdx) * sizeof(uint32);
if (inData != nullptr)
{
mRenderer->CreateDeviceLocalBuffer(inData, size, VK_BUFFER_USAGE_INDEX_BUFFER_BIT, mIndexBuffer);
mIndexBufferDeviceLocal = true;
}
else
mRenderer->CreateBuffer(size, VK_BUFFER_USAGE_INDEX_BUFFER_BIT, VK_MEMORY_PROPERTY_HOST_VISIBLE_BIT | VK_MEMORY_PROPERTY_HOST_COHERENT_BIT, mIndexBuffer);
}
uint32 *RenderPrimitiveVK::LockIndexBuffer()
{
JPH_ASSERT(!mIndexBufferDeviceLocal);
void *data;
vkMapMemory(mRenderer->GetDevice(), mIndexBuffer.mMemory, mIndexBuffer.mOffset, VkDeviceSize(mNumIdx) * sizeof(uint32), 0, &data);
return reinterpret_cast<uint32 *>(data);
}
void RenderPrimitiveVK::UnlockIndexBuffer()
{
vkUnmapMemory(mRenderer->GetDevice(), mIndexBuffer.mMemory);
}
void RenderPrimitiveVK::Draw() const
{
VkCommandBuffer command_buffer = mRenderer->GetCommandBuffer();
VkBuffer vertex_buffers[] = { mVertexBuffer.mBuffer };
VkDeviceSize offsets[] = { 0 };
vkCmdBindVertexBuffers(command_buffer, 0, 1, vertex_buffers, offsets);
if (mIndexBuffer.mBuffer == VK_NULL_HANDLE)
{
vkCmdDraw(command_buffer, mNumVtxToDraw, 1, 0, 0);
}
else
{
vkCmdBindIndexBuffer(command_buffer, mIndexBuffer.mBuffer, 0, VK_INDEX_TYPE_UINT32);
vkCmdDrawIndexed(command_buffer, mNumIdxToDraw, 1, 0, 0, 0);
}
}

View File

@@ -0,0 +1,44 @@
// Jolt Physics Library (https://github.com/jrouwe/JoltPhysics)
// SPDX-FileCopyrightText: 2024 Jorrit Rouwe
// SPDX-License-Identifier: MIT
#pragma once
#include <Renderer/RenderPrimitive.h>
#include <Renderer/VK/RendererVK.h>
#include <Renderer/VK/BufferVK.h>
/// Vulkan implementation of a render primitive
class RenderPrimitiveVK : public RenderPrimitive
{
public:
/// Constructor
RenderPrimitiveVK(RendererVK *inRenderer) : mRenderer(inRenderer) { }
virtual ~RenderPrimitiveVK() override { Clear(); }
/// Vertex buffer management functions
virtual void CreateVertexBuffer(int inNumVtx, int inVtxSize, const void *inData = nullptr) override;
virtual void ReleaseVertexBuffer() override;
virtual void * LockVertexBuffer() override;
virtual void UnlockVertexBuffer() override;
/// Index buffer management functions
virtual void CreateIndexBuffer(int inNumIdx, const uint32 *inData = nullptr) override;
virtual void ReleaseIndexBuffer() override;
virtual uint32 * LockIndexBuffer() override;
virtual void UnlockIndexBuffer() override;
/// Draw the primitive
virtual void Draw() const override;
private:
friend class RenderInstancesVK;
RendererVK * mRenderer;
BufferVK mVertexBuffer;
bool mVertexBufferDeviceLocal = false;
BufferVK mIndexBuffer;
bool mIndexBufferDeviceLocal = false;
};

File diff suppressed because it is too large Load Diff

View File

@@ -0,0 +1,156 @@
// Jolt Physics Library (https://github.com/jrouwe/JoltPhysics)
// SPDX-FileCopyrightText: 2024 Jorrit Rouwe
// SPDX-License-Identifier: MIT
#pragma once
#include <Renderer/Renderer.h>
#include <Renderer/VK/ConstantBufferVK.h>
#include <Renderer/VK/TextureVK.h>
#include <Jolt/Core/UnorderedMap.h>
#include <vulkan/vulkan.h>
/// Vulkan renderer
class RendererVK : public Renderer
{
public:
/// Destructor
virtual ~RendererVK() override;
// See: Renderer
virtual void Initialize(ApplicationWindow *inWindow) override;
virtual bool BeginFrame(const CameraState &inCamera, float inWorldScale) override;
virtual void EndShadowPass() override;
virtual void EndFrame() override;
virtual void SetProjectionMode() override;
virtual void SetOrthoMode() override;
virtual Ref<Texture> CreateTexture(const Surface *inSurface) override;
virtual Ref<VertexShader> CreateVertexShader(const char *inName) override;
virtual Ref<PixelShader> CreatePixelShader(const char *inName) override;
virtual unique_ptr<PipelineState> CreatePipelineState(const VertexShader *inVertexShader, const PipelineState::EInputDescription *inInputDescription, uint inInputDescriptionCount, const PixelShader *inPixelShader, PipelineState::EDrawPass inDrawPass, PipelineState::EFillMode inFillMode, PipelineState::ETopology inTopology, PipelineState::EDepthTest inDepthTest, PipelineState::EBlendMode inBlendMode, PipelineState::ECullMode inCullMode) override;
virtual RenderPrimitive * CreateRenderPrimitive(PipelineState::ETopology inType) override;
virtual RenderInstances * CreateRenderInstances() override;
virtual Texture * GetShadowMap() const override { return mShadowMap.GetPtr(); }
virtual void OnWindowResize() override;
VkDevice GetDevice() const { return mDevice; }
VkDescriptorPool GetDescriptorPool() const { return mDescriptorPool; }
VkDescriptorSetLayout GetDescriptorSetLayoutTexture() const { return mDescriptorSetLayoutTexture; }
VkSampler GetTextureSamplerRepeat() const { return mTextureSamplerRepeat; }
VkSampler GetTextureSamplerShadow() const { return mTextureSamplerShadow; }
VkRenderPass GetRenderPassShadow() const { return mRenderPassShadow; }
VkRenderPass GetRenderPass() const { return mRenderPass; }
VkPipelineLayout GetPipelineLayout() const { return mPipelineLayout; }
VkCommandBuffer GetCommandBuffer() { JPH_ASSERT(mInFrame); return mCommandBuffers[mFrameIndex]; }
VkCommandBuffer StartTempCommandBuffer();
void EndTempCommandBuffer(VkCommandBuffer inCommandBuffer);
void AllocateMemory(VkDeviceSize inSize, uint32 inMemoryTypeBits, VkMemoryPropertyFlags inProperties, VkDeviceMemory &outMemory);
void FreeMemory(VkDeviceMemory inMemory, VkDeviceSize inSize);
void CreateBuffer(VkDeviceSize inSize, VkBufferUsageFlags inUsage, VkMemoryPropertyFlags inProperties, BufferVK &outBuffer);
void CopyBuffer(VkBuffer inSrc, VkBuffer inDst, VkDeviceSize inSize);
void CreateDeviceLocalBuffer(const void *inData, VkDeviceSize inSize, VkBufferUsageFlags inUsage, BufferVK &outBuffer);
void FreeBuffer(BufferVK &ioBuffer);
unique_ptr<ConstantBufferVK> CreateConstantBuffer(VkDeviceSize inBufferSize);
void CreateImage(uint32 inWidth, uint32 inHeight, VkFormat inFormat, VkImageTiling inTiling, VkImageUsageFlags inUsage, VkMemoryPropertyFlags inProperties, VkImage &outImage, VkDeviceMemory &outMemory);
void DestroyImage(VkImage inImage, VkDeviceMemory inMemory);
VkImageView CreateImageView(VkImage inImage, VkFormat inFormat, VkImageAspectFlags inAspectFlags);
VkFormat FindDepthFormat();
private:
uint32 FindMemoryType(uint32 inTypeFilter, VkMemoryPropertyFlags inProperties);
void FreeBufferInternal(BufferVK &ioBuffer);
VkSurfaceFormatKHR SelectFormat(VkPhysicalDevice inDevice);
void CreateSwapChain(VkPhysicalDevice inDevice);
void DestroySwapChain();
void UpdateViewPortAndScissorRect(uint32 inWidth, uint32 inHeight);
VkSemaphore AllocateSemaphore();
void FreeSemaphore(VkSemaphore inSemaphore);
VkInstance mInstance = VK_NULL_HANDLE;
#ifdef JPH_DEBUG
VkDebugUtilsMessengerEXT mDebugMessenger = VK_NULL_HANDLE;
#endif
VkPhysicalDevice mPhysicalDevice = VK_NULL_HANDLE;
VkPhysicalDeviceMemoryProperties mMemoryProperties;
VkDevice mDevice = VK_NULL_HANDLE;
uint32 mGraphicsQueueIndex = 0;
uint32 mPresentQueueIndex = 0;
VkQueue mGraphicsQueue = VK_NULL_HANDLE;
VkQueue mPresentQueue = VK_NULL_HANDLE;
VkSurfaceKHR mSurface = VK_NULL_HANDLE;
VkSwapchainKHR mSwapChain = VK_NULL_HANDLE;
bool mSubOptimalSwapChain = false;
Array<VkImage> mSwapChainImages;
VkFormat mSwapChainImageFormat;
VkExtent2D mSwapChainExtent;
Array<VkImageView> mSwapChainImageViews;
VkImage mDepthImage = VK_NULL_HANDLE;
VkDeviceMemory mDepthImageMemory = VK_NULL_HANDLE;
VkImageView mDepthImageView = VK_NULL_HANDLE;
VkDescriptorSetLayout mDescriptorSetLayoutUBO = VK_NULL_HANDLE;
VkDescriptorSetLayout mDescriptorSetLayoutTexture = VK_NULL_HANDLE;
VkDescriptorPool mDescriptorPool = VK_NULL_HANDLE;
VkDescriptorSet mDescriptorSets[cFrameCount];
VkDescriptorSet mDescriptorSetsOrtho[cFrameCount];
VkSampler mTextureSamplerShadow = VK_NULL_HANDLE;
VkSampler mTextureSamplerRepeat = VK_NULL_HANDLE;
VkRenderPass mRenderPassShadow = VK_NULL_HANDLE;
VkRenderPass mRenderPass = VK_NULL_HANDLE;
VkPipelineLayout mPipelineLayout = VK_NULL_HANDLE;
VkFramebuffer mShadowFrameBuffer = VK_NULL_HANDLE;
Array<VkFramebuffer> mSwapChainFramebuffers;
uint32 mImageIndex = 0;
VkCommandPool mCommandPool = VK_NULL_HANDLE;
VkCommandBuffer mCommandBuffers[cFrameCount];
Array<VkSemaphore> mAvailableSemaphores;
Array<VkSemaphore> mImageAvailableSemaphores;
Array<VkSemaphore> mRenderFinishedSemaphores;
VkFence mInFlightFences[cFrameCount];
Ref<TextureVK> mShadowMap;
unique_ptr<ConstantBufferVK> mVertexShaderConstantBufferProjection[cFrameCount];
unique_ptr<ConstantBufferVK> mVertexShaderConstantBufferOrtho[cFrameCount];
unique_ptr<ConstantBufferVK> mPixelShaderConstantBuffer[cFrameCount];
struct Key
{
bool operator == (const Key &inRHS) const
{
return mSize == inRHS.mSize && mUsage == inRHS.mUsage && mProperties == inRHS.mProperties;
}
VkDeviceSize mSize;
VkBufferUsageFlags mUsage;
VkMemoryPropertyFlags mProperties;
};
JPH_MAKE_HASH_STRUCT(Key, KeyHasher, t.mSize, t.mUsage, t.mProperties)
// We try to recycle buffers from frame to frame
using BufferCache = UnorderedMap<Key, Array<BufferVK>, KeyHasher>;
BufferCache mFreedBuffers[cFrameCount];
BufferCache mBufferCache;
// Smaller allocations (from cMinAllocSize to cMaxAllocSize) will be done in blocks of cBlockSize bytes.
// We do this because there is a limit to the number of allocations that we can make in Vulkan.
static constexpr VkDeviceSize cMinAllocSize = 512;
static constexpr VkDeviceSize cMaxAllocSize = 65536;
static constexpr VkDeviceSize cBlockSize = 524288;
JPH_MAKE_HASH_STRUCT(Key, MemKeyHasher, t.mUsage, t.mProperties, t.mSize)
struct Memory
{
VkDeviceMemory mMemory;
VkDeviceSize mOffset;
};
using MemoryCache = UnorderedMap<Key, Array<Memory>, KeyHasher>;
MemoryCache mMemoryCache;
uint32 mNumAllocations = 0;
uint32 mMaxNumAllocations = 0;
VkDeviceSize mTotalAllocated = 0;
VkDeviceSize mMaxTotalAllocated = 0;
};

View File

@@ -0,0 +1,180 @@
// Jolt Physics Library (https://github.com/jrouwe/JoltPhysics)
// SPDX-FileCopyrightText: 2024 Jorrit Rouwe
// SPDX-License-Identifier: MIT
#include <TestFramework.h>
#include <Renderer/VK/TextureVK.h>
#include <Renderer/VK/RendererVK.h>
#include <Renderer/VK/FatalErrorIfFailedVK.h>
#include <Image/BlitSurface.h>
TextureVK::TextureVK(RendererVK *inRenderer, const Surface *inSurface) :
Texture(inSurface->GetWidth(), inSurface->GetHeight()),
mRenderer(inRenderer)
{
ESurfaceFormat format = inSurface->GetFormat();
VkFormat vk_format = VK_FORMAT_B8G8R8A8_UNORM;
switch (format)
{
case ESurfaceFormat::A4L4: vk_format = VK_FORMAT_R8G8_UNORM; format = ESurfaceFormat::A8L8; break;
case ESurfaceFormat::L8: vk_format = VK_FORMAT_R8_UNORM; break;
case ESurfaceFormat::A8: vk_format = VK_FORMAT_A8_UNORM_KHR; break;
case ESurfaceFormat::A8L8: vk_format = VK_FORMAT_R8G8_UNORM; break;
case ESurfaceFormat::R5G6B5: vk_format = VK_FORMAT_B5G6R5_UNORM_PACK16; break;
case ESurfaceFormat::X1R5G5B5: vk_format = VK_FORMAT_B5G5R5A1_UNORM_PACK16; format = ESurfaceFormat::A1R5G5B5; break;
case ESurfaceFormat::X4R4G4B4: vk_format = VK_FORMAT_B4G4R4A4_UNORM_PACK16; format = ESurfaceFormat::A4R4G4B4; break;
case ESurfaceFormat::A1R5G5B5: vk_format = VK_FORMAT_B5G5R5A1_UNORM_PACK16; break;
case ESurfaceFormat::A4R4G4B4: vk_format = VK_FORMAT_B4G4R4A4_UNORM_PACK16; break;
case ESurfaceFormat::R8G8B8: vk_format = VK_FORMAT_B8G8R8_UNORM; break;
case ESurfaceFormat::B8G8R8: vk_format = VK_FORMAT_B8G8R8_UNORM; break;
case ESurfaceFormat::X8R8G8B8: vk_format = VK_FORMAT_B8G8R8A8_UNORM; format = ESurfaceFormat::A8R8G8B8; break;
case ESurfaceFormat::X8B8G8R8: vk_format = VK_FORMAT_B8G8R8A8_UNORM; format = ESurfaceFormat::X8R8G8B8; break;
case ESurfaceFormat::A8R8G8B8: vk_format = VK_FORMAT_B8G8R8A8_UNORM; break;
case ESurfaceFormat::A8B8G8R8: vk_format = VK_FORMAT_B8G8R8A8_UNORM; format = ESurfaceFormat::A8R8G8B8; break;
case ESurfaceFormat::Invalid:
default: JPH_ASSERT(false); break;
}
// Blit the surface to another temporary surface if the format changed
const Surface *surface = inSurface;
Ref<Surface> tmp;
if (format != inSurface->GetFormat())
{
tmp = new SoftwareSurface(mWidth, mHeight, format);
BlitSurface(inSurface, tmp);
surface = tmp;
}
int bpp = surface->GetBytesPerPixel();
VkDeviceSize image_size = VkDeviceSize(mWidth) * mHeight * bpp;
VkDevice device = mRenderer->GetDevice();
BufferVK staging_buffer;
mRenderer->CreateBuffer(image_size, VK_BUFFER_USAGE_TRANSFER_SRC_BIT, VK_MEMORY_PROPERTY_HOST_VISIBLE_BIT | VK_MEMORY_PROPERTY_HOST_COHERENT_BIT, staging_buffer);
// Copy data to upload texture
surface->Lock(ESurfaceLockMode::Read);
void *data;
vkMapMemory(device, staging_buffer.mMemory, staging_buffer.mOffset, image_size, 0, &data);
for (int y = 0; y < mHeight; ++y)
memcpy(reinterpret_cast<uint8 *>(data) + y * mWidth * bpp, surface->GetData() + y * surface->GetStride(), mWidth * bpp);
vkUnmapMemory(device, staging_buffer.mMemory);
surface->UnLock();
// Create destination image
mRenderer->CreateImage(mWidth, mHeight, vk_format, VK_IMAGE_TILING_OPTIMAL, VK_IMAGE_USAGE_TRANSFER_DST_BIT | VK_IMAGE_USAGE_SAMPLED_BIT, VK_MEMORY_PROPERTY_DEVICE_LOCAL_BIT, mImage, mImageMemory);
VkCommandBuffer command_buffer = mRenderer->StartTempCommandBuffer();
// Make the image suitable for transferring to
TransitionImageLayout(command_buffer, mImage, vk_format, VK_IMAGE_LAYOUT_UNDEFINED, VK_IMAGE_LAYOUT_TRANSFER_DST_OPTIMAL);
// Copy the data to the destination image
VkBufferImageCopy region = {};
region.imageSubresource.aspectMask = VK_IMAGE_ASPECT_COLOR_BIT;
region.imageSubresource.layerCount = 1;
region.imageExtent = { uint32(mWidth), uint32(mHeight), 1 };
vkCmdCopyBufferToImage(command_buffer, staging_buffer.mBuffer, mImage, VK_IMAGE_LAYOUT_TRANSFER_DST_OPTIMAL, 1, &region);
// Make the image suitable for sampling
TransitionImageLayout(command_buffer, mImage, vk_format, VK_IMAGE_LAYOUT_TRANSFER_DST_OPTIMAL, VK_IMAGE_LAYOUT_SHADER_READ_ONLY_OPTIMAL);
mRenderer->EndTempCommandBuffer(command_buffer);
// Destroy temporary buffer
mRenderer->FreeBuffer(staging_buffer);
CreateImageViewAndDescriptorSet(vk_format, VK_IMAGE_ASPECT_COLOR_BIT, mRenderer->GetTextureSamplerRepeat());
}
TextureVK::TextureVK(RendererVK *inRenderer, int inWidth, int inHeight) :
Texture(inWidth, inHeight),
mRenderer(inRenderer)
{
VkFormat vk_format = mRenderer->FindDepthFormat();
// Create render target
mRenderer->CreateImage(mWidth, mHeight, vk_format, VK_IMAGE_TILING_OPTIMAL, VK_IMAGE_USAGE_INPUT_ATTACHMENT_BIT | VK_IMAGE_USAGE_DEPTH_STENCIL_ATTACHMENT_BIT | VK_IMAGE_USAGE_SAMPLED_BIT, VK_MEMORY_PROPERTY_DEVICE_LOCAL_BIT, mImage, mImageMemory);
CreateImageViewAndDescriptorSet(vk_format, VK_IMAGE_ASPECT_DEPTH_BIT, mRenderer->GetTextureSamplerShadow());
}
TextureVK::~TextureVK()
{
if (mImage != VK_NULL_HANDLE)
{
VkDevice device = mRenderer->GetDevice();
vkDeviceWaitIdle(device);
vkDestroyImageView(device, mImageView, nullptr);
mRenderer->DestroyImage(mImage, mImageMemory);
}
}
void TextureVK::Bind() const
{
if (mDescriptorSet != VK_NULL_HANDLE)
vkCmdBindDescriptorSets(mRenderer->GetCommandBuffer(), VK_PIPELINE_BIND_POINT_GRAPHICS, mRenderer->GetPipelineLayout(), 1, 1, &mDescriptorSet, 0, nullptr);
}
void TextureVK::CreateImageViewAndDescriptorSet(VkFormat inFormat, VkImageAspectFlags inAspectFlags, VkSampler inSampler)
{
VkDevice device = mRenderer->GetDevice();
// Create image view
mImageView = mRenderer->CreateImageView(mImage, inFormat, inAspectFlags);
// Allocate descriptor set for binding the texture
VkDescriptorSetLayout layout = mRenderer->GetDescriptorSetLayoutTexture();
VkDescriptorSetAllocateInfo descriptor_set_alloc_info = {};
descriptor_set_alloc_info.sType = VK_STRUCTURE_TYPE_DESCRIPTOR_SET_ALLOCATE_INFO;
descriptor_set_alloc_info.descriptorPool = mRenderer->GetDescriptorPool();
descriptor_set_alloc_info.descriptorSetCount = 1;
descriptor_set_alloc_info.pSetLayouts = &layout;
FatalErrorIfFailed(vkAllocateDescriptorSets(device, &descriptor_set_alloc_info, &mDescriptorSet));
VkDescriptorImageInfo image_info = {};
image_info.imageLayout = VK_IMAGE_LAYOUT_SHADER_READ_ONLY_OPTIMAL;
image_info.imageView = mImageView;
image_info.sampler = inSampler;
VkWriteDescriptorSet descriptor_write = {};
descriptor_write.sType = VK_STRUCTURE_TYPE_WRITE_DESCRIPTOR_SET;
descriptor_write.dstSet = mDescriptorSet;
descriptor_write.dstBinding = 0;
descriptor_write.dstArrayElement = 0;
descriptor_write.descriptorType = VK_DESCRIPTOR_TYPE_COMBINED_IMAGE_SAMPLER;
descriptor_write.descriptorCount = 1;
descriptor_write.pImageInfo = &image_info;
vkUpdateDescriptorSets(device, 1, &descriptor_write, 0, nullptr);
}
void TextureVK::TransitionImageLayout(VkCommandBuffer inCommandBuffer, VkImage inImage, VkFormat inFormat, VkImageLayout inOldLayout, VkImageLayout inNewLayout)
{
VkImageMemoryBarrier barrier = {};
barrier.sType = VK_STRUCTURE_TYPE_IMAGE_MEMORY_BARRIER;
barrier.oldLayout = inOldLayout;
barrier.newLayout = inNewLayout;
barrier.srcQueueFamilyIndex = VK_QUEUE_FAMILY_IGNORED;
barrier.dstQueueFamilyIndex = VK_QUEUE_FAMILY_IGNORED;
barrier.image = inImage;
barrier.subresourceRange.aspectMask = VK_IMAGE_ASPECT_COLOR_BIT;
barrier.subresourceRange.levelCount = 1;
barrier.subresourceRange.layerCount = 1;
if (inOldLayout == VK_IMAGE_LAYOUT_UNDEFINED && inNewLayout == VK_IMAGE_LAYOUT_TRANSFER_DST_OPTIMAL)
{
barrier.srcAccessMask = 0;
barrier.dstAccessMask = VK_ACCESS_TRANSFER_WRITE_BIT;
vkCmdPipelineBarrier(inCommandBuffer, VK_PIPELINE_STAGE_TOP_OF_PIPE_BIT, VK_PIPELINE_STAGE_TRANSFER_BIT, 0, 0, nullptr, 0, nullptr, 1, &barrier);
}
else if (inOldLayout == VK_IMAGE_LAYOUT_TRANSFER_DST_OPTIMAL && inNewLayout == VK_IMAGE_LAYOUT_SHADER_READ_ONLY_OPTIMAL)
{
barrier.srcAccessMask = VK_ACCESS_TRANSFER_WRITE_BIT;
barrier.dstAccessMask = VK_ACCESS_SHADER_READ_BIT;
vkCmdPipelineBarrier(inCommandBuffer, VK_PIPELINE_STAGE_TRANSFER_BIT, VK_PIPELINE_STAGE_FRAGMENT_SHADER_BIT, 0, 0, nullptr, 0, nullptr, 1, &barrier);
}
}

View File

@@ -0,0 +1,35 @@
// Jolt Physics Library (https://github.com/jrouwe/JoltPhysics)
// SPDX-FileCopyrightText: 2024 Jorrit Rouwe
// SPDX-License-Identifier: MIT
#pragma once
#include <Renderer/Texture.h>
#include <vulkan/vulkan.h>
class RendererVK;
class TextureVK : public Texture
{
public:
/// Constructor, called by Renderer::CreateTextureVK
TextureVK(RendererVK *inRenderer, const Surface *inSurface); // Create a normal TextureVK
TextureVK(RendererVK *inRenderer, int inWidth, int inHeight); // Create a render target (depth only)
virtual ~TextureVK() override;
/// Bind texture to the pixel shader
virtual void Bind() const override;
VkImageView GetImageView() const { return mImageView; }
private:
void CreateImageViewAndDescriptorSet(VkFormat inFormat, VkImageAspectFlags inAspectFlags, VkSampler inSampler);
void TransitionImageLayout(VkCommandBuffer inCommandBuffer, VkImage inImage, VkFormat inFormat, VkImageLayout inOldLayout, VkImageLayout inNewLayout);
RendererVK * mRenderer;
VkImage mImage = VK_NULL_HANDLE;
VkDeviceMemory mImageMemory = VK_NULL_HANDLE;
VkImageView mImageView = VK_NULL_HANDLE;
VkDescriptorSet mDescriptorSet = VK_NULL_HANDLE;
};

View File

@@ -0,0 +1,34 @@
// Jolt Physics Library (https://github.com/jrouwe/JoltPhysics)
// SPDX-FileCopyrightText: 2024 Jorrit Rouwe
// SPDX-License-Identifier: MIT
#pragma once
#include <Renderer/VertexShader.h>
#include <vulkan/vulkan.h>
/// Vertex shader handle for Vulkan
class VertexShaderVK : public VertexShader
{
public:
/// Constructor
VertexShaderVK(VkDevice inDevice, VkShaderModule inShaderModule) :
mDevice(inDevice),
mStageInfo()
{
mStageInfo.sType = VK_STRUCTURE_TYPE_PIPELINE_SHADER_STAGE_CREATE_INFO;
mStageInfo.stage = VK_SHADER_STAGE_VERTEX_BIT;
mStageInfo.module = inShaderModule;
mStageInfo.pName = "main";
}
/// Destructor
virtual ~VertexShaderVK() override
{
vkDestroyShaderModule(mDevice, mStageInfo.module, nullptr);
}
VkDevice mDevice;
VkPipelineShaderStageCreateInfo mStageInfo;
};