Compare commits

..

1 Commits

Author SHA1 Message Date
Anish Bhobe 297548d2c9 Fully bindless textures. 2025-06-17 22:10:54 +02:00
36 changed files with 791 additions and 2114 deletions

View File

@ -4,7 +4,6 @@ AlignAfterOpenBracket: AlwaysBreak
AlignConsecutiveAssignments: AlignConsecutiveAssignments:
Enabled: true Enabled: true
AcrossEmptyLines: true AcrossEmptyLines: true
AlignCompound: true
AlignFunctionPointers: true AlignFunctionPointers: true
AlignConsecutiveDeclarations: AlignConsecutiveDeclarations:
Enabled: true Enabled: true

2
.gitattributes vendored
View File

@ -3,5 +3,3 @@
*.glb filter=lfs diff=lfs merge=lfs -text *.glb filter=lfs diff=lfs merge=lfs -text
*.hdr filter=lfs diff=lfs merge=lfs -text *.hdr filter=lfs diff=lfs merge=lfs -text
*.exr filter=lfs diff=lfs merge=lfs -text *.exr filter=lfs diff=lfs merge=lfs -text
*.bin filter=lfs diff=lfs merge=lfs -text
*.gltf filter=lfs diff=lfs merge=lfs -text

BIN
Assets/Models/Box.glb (Stored with Git LFS)

Binary file not shown.

BIN
Assets/Models/BoxTextured.glb (Stored with Git LFS)

Binary file not shown.

BIN
Assets/Models/BoxVertexColors.glb (Stored with Git LFS)

Binary file not shown.

BIN
Assets/Models/OrientationTest.glb (Stored with Git LFS)

Binary file not shown.

View File

@ -1,65 +1,24 @@
import Bindless; import Bindless;
struct VertexOut { struct VertexOut {
float4 outPosition : SV_Position; float4 outPosition : SV_Position;
float4 worldPosition : WorldPosition; float4 screenPosition : ScreenPosition;
float4 normal : WorldNormal; float4 vertexColor : CoarseColor;
float2 texCoord0 : TexCoord0; float2 texCoord0 : TexCoord0;
float2 texCoord1 : TexCoord1;
float4 vertexColor0 : VertexColor;
}; };
struct CameraData { struct CameraData {
float4x4 view; float4x4 view;
float4x4 proj; float4x4 proj;
float4 position;
}; };
struct PointLight { [vk::binding(0, 1)] uniform ConstantBuffer<CameraData> camera;
float3 position;
float range;
float3 color;
float attenuation;
};
struct DirectionalLight { #define INDEX_MASK 0x0007FFFF;
float3 direction;
float _padding0;
float3 color;
float _padding1;
};
struct LightData {
PointLight* pointLights;
DirectionalLight* dirLights;
uint pointLightCount;
uint dirLightCount;
PointLight getPointLight(uint idx) {
if (idx >= pointLightCount) return pointLights[0];
return pointLights[idx];
}
DirectionalLight getDirectionalLight(uint idx) {
if (idx >= dirLightCount) return dirLights[0];
return dirLights[idx];
}
};
struct PerFrameData {
CameraData camera;
LightData lightData;
};
uniform ParameterBlock<PerFrameData> pfd;
struct PerInstanceData { struct PerInstanceData {
float4x4 transform; float4x4 transform;
Sampler2D.RID textureID; Sampler2D.RID textureID;
uint _padding;
float metallic;
float roughness;
float4 baseColor;
} }
[[vk::push_constant]] [[vk::push_constant]]
@ -69,59 +28,27 @@ uniform ConstantBuffer<PerInstanceData> pcb;
VertexOut VertexMain( VertexOut VertexMain(
uint vertexId: SV_VertexID, uint vertexId: SV_VertexID,
float3 position, float3 position,
float3 normal, float3 color,
float2 texCoord0, float2 texCoord0,
float2 texCoord1,
float4 vertexColor0,
) { ) {
float4 worldPosition = mul(pcb.transform, float4(position, 1.0f));
VertexOut output; VertexOut output;
output.outPosition = mul(pfd.camera.proj, mul(pfd.camera.view, worldPosition)); output.outPosition = mul(camera.proj, mul(camera.view, mul(pcb.transform, float4(position, 1.0f))));
output.worldPosition = worldPosition; output.screenPosition = mul(camera.proj, mul(camera.view, mul(pcb.transform, float4(position, 1.0f))));
output.normal = mul(pcb.transform, float4(normalize(normal.rgb), 0.0f)); output.vertexColor = float4(color, 1.0f);
output.texCoord0 = texCoord0; output.texCoord0 = texCoord0 * 2.0f;
output.texCoord1 = texCoord1;
output.vertexColor0 = vertexColor0;
return output; return output;
} }
[shader("fragment")] [shader("fragment")]
float4 FragmentMain( float4 FragmentMain(
float4 worldPosition : WorldPosition, float4 interpolatePosition : ScreenPosition,
float4 normal : WorldNormal, float4 interpolatedColors : CoarseColor,
float2 uv0 : TexCoord0, float2 uv0 : TexCoord0,
float2 uv1 : TexCoord1,
float4 color : VertexColor,
) : SV_Target0 { ) : SV_Target0 {
float3 diffuse = 0.0f.xxx;
float3 specular = 0.0f.xxx;
for (uint i = 0; i < pfd.lightData.pointLightCount; ++i) {
PointLight pointlight = pfd.lightData.pointLights[i];
let lightPosition = pointlight.position;
let lightDisplace = worldPosition.xyz - lightPosition;
let lightDistance = length(lightDisplace);
let lightDirection = normalize(lightDisplace);
let viewDirection = normalize(worldPosition.xyz - pfd.camera.position.xyz);
let halfWayVector = normalize(-lightDirection + viewDirection);
let attenuation = (1.0f / lightDistance);
let diffuseFactor = pcb.roughness * dot(-lightDirection, normalize(normal.xyz));
diffuse += pointlight.color * diffuseFactor;
let specularFactor = (1.0f - pcb.roughness) * pow(max(dot(halfWayVector, viewDirection), 0.0f), 32.0f) * attenuation;
specular += pointlight.color * specularFactor;
}
if (let texture = pcb.textureID) { if (let texture = pcb.textureID) {
return float4(texture.Sample(uv0).rgb, 1.0f) * pcb.baseColor * color * float4((diffuse + specular), 0.0f); return float4(texture.Sample(uv0).rgb, 1.0f) * interpolatedColors;
} else { } else {
return pcb.baseColor * color * float4((diffuse + specular), 0.0f); return interpolatedColors;
} }
} }

View File

@ -1,7 +1,7 @@
 
Microsoft Visual Studio Solution File, Format Version 12.00 Microsoft Visual Studio Solution File, Format Version 12.00
# Visual Studio Version 17 # Visual Studio Version 17
VisualStudioVersion = 17.13.36105.23 VisualStudioVersion = 17.13.36105.23 d17.13
MinimumVisualStudioVersion = 10.0.40219.1 MinimumVisualStudioVersion = 10.0.40219.1
Project("{8BC9CEB8-8B4A-11D0-8D11-00A0C91BC942}") = "Blaze", "Blaze.vcxproj", "{92E725FE-D87B-4FDE-8371-5B2CE60945FD}" Project("{8BC9CEB8-8B4A-11D0-8D11-00A0C91BC942}") = "Blaze", "Blaze.vcxproj", "{92E725FE-D87B-4FDE-8371-5B2CE60945FD}"
EndProject EndProject

View File

@ -119,7 +119,7 @@
<Link> <Link>
<SubSystem>Console</SubSystem> <SubSystem>Console</SubSystem>
<GenerateDebugInformation>true</GenerateDebugInformation> <GenerateDebugInformation>true</GenerateDebugInformation>
<AdditionalLibraryDirectories>%(AdditionalLibraryDirectories)</AdditionalLibraryDirectories> <AdditionalLibraryDirectories>C:\Users\Eon\source\repos\Blaze\vcpkg_installed\x64-windows\x64-windows\bin;%(AdditionalLibraryDirectories)</AdditionalLibraryDirectories>
</Link> </Link>
</ItemDefinitionGroup> </ItemDefinitionGroup>
<ItemDefinitionGroup Condition="'$(Configuration)|$(Platform)'=='Release|x64'"> <ItemDefinitionGroup Condition="'$(Configuration)|$(Platform)'=='Release|x64'">
@ -144,7 +144,7 @@
<EnableCOMDATFolding>true</EnableCOMDATFolding> <EnableCOMDATFolding>true</EnableCOMDATFolding>
<OptimizeReferences>true</OptimizeReferences> <OptimizeReferences>true</OptimizeReferences>
<GenerateDebugInformation>true</GenerateDebugInformation> <GenerateDebugInformation>true</GenerateDebugInformation>
<AdditionalLibraryDirectories>%(AdditionalLibraryDirectories)</AdditionalLibraryDirectories> <AdditionalLibraryDirectories>C:\Users\Eon\source\repos\Blaze\vcpkg_installed\x64-windows\x64-windows\bin;%(AdditionalLibraryDirectories)</AdditionalLibraryDirectories>
</Link> </Link>
</ItemDefinitionGroup> </ItemDefinitionGroup>
<ItemGroup> <ItemGroup>
@ -172,31 +172,22 @@
</ItemGroup> </ItemGroup>
<ItemGroup> <ItemGroup>
<ClInclude Include="Blaze\AppState.h" /> <ClInclude Include="Blaze\AppState.h" />
<ClInclude Include="Blaze\BufferManager.h" />
<ClInclude Include="Blaze\EntityManager.h" /> <ClInclude Include="Blaze\EntityManager.h" />
<ClInclude Include="Blaze\Frame.h" /> <ClInclude Include="Blaze\Frame.h" />
<ClInclude Include="Blaze\FreeList.h" />
<ClInclude Include="Blaze\GlobalMemory.h" /> <ClInclude Include="Blaze\GlobalMemory.h" />
<ClInclude Include="Blaze\MacroUtils.h" /> <ClInclude Include="Blaze\MacroUtils.h" />
<ClInclude Include="Blaze\MathUtil.h" /> <ClInclude Include="Blaze\MathUtil.h" />
<ClInclude Include="Blaze\MiscData.h" /> <ClInclude Include="Blaze\MiscData.h" />
<ClInclude Include="Blaze\ModelLoader.h" />
<ClInclude Include="Blaze\RenderDevice.h" /> <ClInclude Include="Blaze\RenderDevice.h" />
<ClInclude Include="Blaze\RID.h" />
<ClInclude Include="Blaze\TextureManager.h" /> <ClInclude Include="Blaze\TextureManager.h" />
<ClInclude Include="Blaze\VulkanHeader.h" />
</ItemGroup> </ItemGroup>
<ItemGroup> <ItemGroup>
<ClCompile Include="Blaze\AppState.cpp" /> <ClCompile Include="Blaze\AppState.cpp" />
<ClCompile Include="Blaze\Blaze.cpp" /> <ClCompile Include="Blaze\Blaze.cpp" />
<ClCompile Include="Blaze\BufferManager.cpp" />
<ClCompile Include="Blaze\CgltfImpl.cpp" />
<ClCompile Include="Blaze\EntityManager.cpp" /> <ClCompile Include="Blaze\EntityManager.cpp" />
<ClCompile Include="Blaze\Frame.cpp" /> <ClCompile Include="Blaze\Frame.cpp" />
<ClCompile Include="Blaze\FreeList.cpp" />
<ClCompile Include="Blaze\GlobalMemory.cpp" /> <ClCompile Include="Blaze\GlobalMemory.cpp" />
<ClCompile Include="Blaze\MiscData.cpp" /> <ClCompile Include="Blaze\MiscData.cpp" />
<ClCompile Include="Blaze\ModelLoader.cpp" />
<ClCompile Include="Blaze\RenderDevice.cpp" /> <ClCompile Include="Blaze\RenderDevice.cpp" />
<ClCompile Include="Blaze\StbImpl.cpp" /> <ClCompile Include="Blaze\StbImpl.cpp" />
<ClCompile Include="Blaze\TextureManager.cpp" /> <ClCompile Include="Blaze\TextureManager.cpp" />

View File

@ -77,21 +77,6 @@
<ClInclude Include="Blaze\TextureManager.h"> <ClInclude Include="Blaze\TextureManager.h">
<Filter>Header Files</Filter> <Filter>Header Files</Filter>
</ClInclude> </ClInclude>
<ClInclude Include="Blaze\VulkanHeader.h">
<Filter>Header Files</Filter>
</ClInclude>
<ClInclude Include="Blaze\BufferManager.h">
<Filter>Header Files</Filter>
</ClInclude>
<ClInclude Include="Blaze\FreeList.h">
<Filter>Header Files</Filter>
</ClInclude>
<ClInclude Include="Blaze\RID.h">
<Filter>Header Files</Filter>
</ClInclude>
<ClInclude Include="Blaze\ModelLoader.h">
<Filter>Header Files</Filter>
</ClInclude>
</ItemGroup> </ItemGroup>
<ItemGroup> <ItemGroup>
<ClCompile Include="Blaze\AppState.cpp"> <ClCompile Include="Blaze\AppState.cpp">
@ -124,18 +109,6 @@
<ClCompile Include="Blaze\TextureManager.cpp"> <ClCompile Include="Blaze\TextureManager.cpp">
<Filter>Source Files</Filter> <Filter>Source Files</Filter>
</ClCompile> </ClCompile>
<ClCompile Include="Blaze\BufferManager.cpp">
<Filter>Source Files</Filter>
</ClCompile>
<ClCompile Include="Blaze\FreeList.cpp">
<Filter>Source Files</Filter>
</ClCompile>
<ClCompile Include="Blaze\ModelLoader.cpp">
<Filter>Source Files</Filter>
</ClCompile>
<ClCompile Include="Blaze\CgltfImpl.cpp">
<Filter>Source Files\HeaderOnlyImpl</Filter>
</ClCompile>
</ItemGroup> </ItemGroup>
<ItemGroup> <ItemGroup>
<Image Include="Assets\Textures\container2.png"> <Image Include="Assets\Textures\container2.png">

View File

@ -53,7 +53,7 @@ AppState* AppState_Create( GlobalMemory* memory, uint32_t const width, uint32_t
return nullptr; return nullptr;
} }
EntityManager* entityManager = EntityManager_Create( memory, renderDevice, 1000 ); EntityManager* entityManager = EntityManager_Create( memory, renderDevice, 10 );
if ( !entityManager ) if ( !entityManager )
{ {
SDL_LogError( SDL_LOG_CATEGORY_APPLICATION, "EntityManager failed to init" ); SDL_LogError( SDL_LOG_CATEGORY_APPLICATION, "EntityManager failed to init" );

View File

@ -3,18 +3,17 @@
#include <array> #include <array>
#include <cassert> #include <cassert>
#include <functional>
#include <limits> #include <limits>
#include <span> #include <span>
#include <volk.h>
#define SDL_MAIN_USE_CALLBACKS 1 #define SDL_MAIN_USE_CALLBACKS 1
#include <SDL3/SDL.h> #include <SDL3/SDL.h>
#include <SDL3/SDL_filesystem.h> #include <SDL3/SDL_filesystem.h>
#include <SDL3/SDL_main.h> #include <SDL3/SDL_main.h>
#include <SDL3/SDL_vulkan.h> #include <SDL3/SDL_vulkan.h>
#include "VulkanHeader.h"
#include "AppState.h" #include "AppState.h"
#include "EntityManager.h" #include "EntityManager.h"
#include "Frame.h" #include "Frame.h"
@ -24,8 +23,6 @@
#include "MiscData.h" #include "MiscData.h"
#include "RenderDevice.h" #include "RenderDevice.h"
#include "ModelLoader.h"
constexpr uint32_t WIDTH = 1280; constexpr uint32_t WIDTH = 1280;
constexpr uint32_t HEIGHT = 720; constexpr uint32_t HEIGHT = 720;
constexpr uint32_t NUM_FRAMES = 3; constexpr uint32_t NUM_FRAMES = 3;
@ -44,42 +41,65 @@ SDL_AppResult SDL_AppInit( void** appstate, int, char** )
*appstate = AppState_Create( &Blaze::Global::g_Memory, WIDTH, HEIGHT ); *appstate = AppState_Create( &Blaze::Global::g_Memory, WIDTH, HEIGHT );
if ( !*appstate ) return SDL_APP_FAILURE; if ( !*appstate ) return SDL_APP_FAILURE;
AppState& appState = *static_cast<AppState*>( *appstate ); AppState& appState = *static_cast<AppState*>( *appstate );
Entity const* entity = // TODO: Integrate this
LoadModel( appState.renderDevice, appState.entityManager, "Assets/Models/OrientationTest.glb" );
ASSERT( entity );
std::array pointLight = { // Model Setup
MiscData::PointLight{
.position = { 12.0f, 0.0f, 0.0f }, // modelTransform[1].position = { -1.0f, 0.0f, 0.0f };
.range = 12, // modelTransform[1].scale = 1.0f;
.color = { 1.0f, 0.0f, 0.0f }, // modelTransform[1].rotation =
.attenuation = 1.0f, // DirectX::XMQuaternionRotationAxis( DirectX::XMVectorSet( 1.0f, 0.0f, 0.0f, 0.0f ), 0.0f );
},
MiscData::PointLight{ // TL----TR
.position = { 0.0f, 12.0f, 0.0f }, // | \ |
.range = 12, // | \ |
.color = { 0.0f, 1.0f, 0.0f }, // | \ |
.attenuation = 1.0f, // BL----BR
}, //
MiscData::PointLight{ // BL -> BR -> TL
.position = { 0.0f, 0.0f, -12.0f }, // TL -> BR -> TR
.range = 6,
.color = { 0.0f, 0.0f, 1.0f }, std::array vertices = {
.attenuation = 1.0f, // Bottom Left
}, Vertex{
.position = { -1.0f, -1.0f, 0.0f },
.color = { 0.0f, 0.0f, 1.0f },
.texCoord0 = { 0.0f, 0.0f },
},
// Bottom Right
Vertex{
.position = { 1.0f, -1.0f, 0.0f },
.color = { 1.0f, 0.0f, 0.0f },
.texCoord0 = { 1.0f, 0.0f },
},
// Top Left
Vertex{
.position = { -1.0f, 1.0f, 0.0f },
.color = { 0.0f, 1.0f, 0.0f },
.texCoord0 = { 0.0f, 1.0f },
},
// Top Right
Vertex{
.position = { 1.0f, 1.0f, 0.0f },
.color = { 1.0f, 1.0f, 0.0f },
.texCoord0 = { 1.0f, 1.0f },
}
}; };
appState.miscData->lightData.pointLightCount = static_cast<uint32_t>( pointLight.size() ); Transform modelTransform = {
.position = { 1.0f, 0.0f, 0.0f },
.scale = 1.0f,
.rotation = DirectX::XMQuaternionRotationAxis( DirectX::XMVectorSet( 0.0f, 1.0f, 0.0f, 0.0f ), 0.0f ),
};
appState.renderDevice->bufferManager->writeToBuffer( for ( int i = -3; i <= 3; ++i )
appState.miscData->pointLights, std::span{ pointLight.begin(), pointLight.end() } ); {
modelTransform.position.x = static_cast<float>( i );
memcpy( appState.entityManager->createEntity(
appState.miscData->cameraUniformBufferPtr + sizeof( MiscData::CameraData ), modelTransform, vertices, i > 0 ? "Assets/Textures/wall.jpg" : "Assets/Textures/container2.png" );
&appState.miscData->lightData, }
sizeof appState.miscData->lightData );
return SDL_APP_CONTINUE; return SDL_APP_CONTINUE;
} }
@ -104,27 +124,20 @@ SDL_AppResult SDL_AppIterate( void* appstate )
misc.previousCounter = currentCounter; misc.previousCounter = currentCounter;
{ {
misc.frameTimeSum -= misc.frameTime[misc.frameTimeWriteHead]; double deltaTimeMs = deltaTime * 1000.0;
misc.frameTime[misc.frameTimeWriteHead] = deltaTime; double fps = 1.0 / deltaTime;
misc.frameTimeSum += deltaTime; ( void )sprintf_s<256>(
misc.frameTimeWriteHead = ( misc.frameTimeWriteHead + 1 ) % misc.frameTimeEntryCount; appState.sprintfBuffer, "%.2f fps %.5fms %llu -> %llu", fps, deltaTimeMs, previousCounter, currentCounter );
double avgDeltaTime = ( misc.frameTimeSum / misc.frameTimeEntryCount );
double fps = 1.0 / avgDeltaTime;
double avgDeltaTimeMs = 1000.0 * avgDeltaTime;
( void )sprintf_s<256>( appState.sprintfBuffer, "%.2f fps %.2f ms", fps, avgDeltaTimeMs );
SDL_SetWindowTitle( appState.window, appState.sprintfBuffer ); SDL_SetWindowTitle( appState.window, appState.sprintfBuffer );
} }
for ( Entity& entity : entityManager.iter() ) for ( Entity& entity : entityManager.iter() )
{ {
if ( not entity.isRoot() ) continue; entity.transform().rotation = DirectX::XMQuaternionMultiply(
entity.transform.rotation = DirectX::XMQuaternionMultiply(
DirectX::XMQuaternionRotationAxis( DirectX::XMQuaternionRotationAxis(
DirectX::XMVectorSet( 0.0f, 1.0f, 0.0f, 0.0f ), DirectX::XMVectorSet( 0.0f, 1.0f, 0.0f, 0.0f ),
DirectX::XMConvertToRadians( 60.0f ) * static_cast<float>( deltaTime ) ), DirectX::XMConvertToRadians( 60.0f ) * static_cast<float>( deltaTime ) ),
entity.transform.rotation ); entity.transform().rotation );
} }
uint32_t currentImageIndex; uint32_t currentImageIndex;
@ -157,27 +170,8 @@ SDL_AppResult SDL_AppIterate( void* appstate )
.float32 = { 0.0f, 0.0f, 0.0f, 1.0f }, .float32 = { 0.0f, 0.0f, 0.0f, 1.0f },
}; };
VkClearDepthStencilValue constexpr static DEPTH_STENCIL_CLEAR = {
.depth = 1.0f,
.stencil = 0,
};
VK_CHECK( vkBeginCommandBuffer( cmd, &beginInfo ) ); VK_CHECK( vkBeginCommandBuffer( cmd, &beginInfo ) );
{ {
VkRenderingAttachmentInfo const depthAttachmentInfo = {
.sType = VK_STRUCTURE_TYPE_RENDERING_ATTACHMENT_INFO,
.pNext = nullptr,
.imageView = currentFrame.depthView,
.imageLayout = VK_IMAGE_LAYOUT_DEPTH_ATTACHMENT_OPTIMAL,
.resolveMode = VK_RESOLVE_MODE_NONE,
.resolveImageView = nullptr,
.resolveImageLayout = VK_IMAGE_LAYOUT_UNDEFINED,
.loadOp = VK_ATTACHMENT_LOAD_OP_CLEAR,
.storeOp = VK_ATTACHMENT_STORE_OP_STORE,
.clearValue = { .depthStencil = DEPTH_STENCIL_CLEAR },
};
VkRenderingAttachmentInfo const attachmentInfo = { VkRenderingAttachmentInfo const attachmentInfo = {
.sType = VK_STRUCTURE_TYPE_RENDERING_ATTACHMENT_INFO, .sType = VK_STRUCTURE_TYPE_RENDERING_ATTACHMENT_INFO,
.pNext = nullptr, .pNext = nullptr,
@ -200,7 +194,7 @@ SDL_AppResult SDL_AppIterate( void* appstate )
.viewMask = 0, .viewMask = 0,
.colorAttachmentCount = 1, .colorAttachmentCount = 1,
.pColorAttachments = &attachmentInfo, .pColorAttachments = &attachmentInfo,
.pDepthAttachment = &depthAttachmentInfo, .pDepthAttachment = nullptr,
.pStencilAttachment = nullptr, .pStencilAttachment = nullptr,
}; };
@ -238,76 +232,30 @@ SDL_AppResult SDL_AppIterate( void* appstate )
vkCmdBindDescriptorSets( vkCmdBindDescriptorSets(
cmd, VK_PIPELINE_BIND_POINT_GRAPHICS, misc.pipelineLayout, 1, 1, &misc.descriptorSet, 0, nullptr ); cmd, VK_PIPELINE_BIND_POINT_GRAPHICS, misc.pipelineLayout, 1, 1, &misc.descriptorSet, 0, nullptr );
std::function<void( Entity const&, DirectX::XMMATRIX const&, Model const* )> drawEntity =
[&]( Entity const& entity, DirectX::XMMATRIX const& parent, Model const* current )
{
Transform const& localTransform = entity.transform;
DirectX::XMMATRIX worldTransform;
{
worldTransform =
DirectX::XMMatrixAffineTransformation(
localTransform.scale, DirectX::XMVectorZero(), localTransform.rotation, localTransform.translation ) *
parent;
}
if ( not entity.model.isNull() )
{
VkBuffer const vertexBuffer = renderDevice.bufferManager->fetchBuffer( entity.model.vertexBuffer ).value();
VkBuffer const indexBuffer = renderDevice.bufferManager->fetchBuffer( entity.model.indexBuffer ).value();
VkDeviceSize constexpr offset = 0;
vkCmdBindVertexBuffers( cmd, 0, 1, &vertexBuffer, &offset );
vkCmdBindIndexBuffer( cmd, indexBuffer, offset, VK_INDEX_TYPE_UINT32 );
}
vkCmdPushConstants(
cmd, misc.pipelineLayout, VK_SHADER_STAGE_ALL_GRAPHICS, 0, sizeof worldTransform, &worldTransform );
if ( not entity.modelMesh.isNull() )
{
ASSERT( current );
for ( Primitive const& primitive : std::span{ current->primitives.data() + entity.modelMesh.primitiveStart,
entity.modelMesh.primitiveCount } )
{
byte const* materialData = nullptr;
if ( primitive.material != UINT32_MAX )
{
Material const* mat = &current->materials[primitive.material];
materialData = reinterpret_cast<byte const*>( mat );
materialData += Material::GPU_DATA_OFFSET;
}
else
{
materialData = reinterpret_cast<byte const*>( &DEFAULT_MATERIAL );
materialData += Material::GPU_DATA_OFFSET;
}
vkCmdPushConstants(
cmd,
misc.pipelineLayout,
VK_SHADER_STAGE_ALL_GRAPHICS,
sizeof worldTransform,
Material::GPU_DATA_SIZE,
materialData );
vkCmdDrawIndexed( cmd, primitive.indexCount, 1, primitive.indexStart, primitive.vertexOffset, 0 );
}
}
for ( Entity& child : entity.children() )
{
drawEntity( child, worldTransform, entity.model.isNull() ? current : &entity.model );
}
};
for ( Entity const& entity : entityManager.iter() ) for ( Entity const& entity : entityManager.iter() )
{ {
if ( not entity.isRoot() ) VkDeviceSize constexpr offset = 0;
{ vkCmdBindVertexBuffers( cmd, 0, 1, &entity.mesh().vertexBuffer, &offset );
continue;
}
drawEntity( entity, DirectX::XMMatrixIdentity(), nullptr ); Transform const& localTransform = entity.transform();
DirectX::XMMATRIX worldTransform;
{
auto [x, y, z] = localTransform.position;
auto scale = localTransform.scale;
worldTransform = DirectX::XMMatrixScaling( scale, scale, scale ) *
DirectX::XMMatrixRotationQuaternion( localTransform.rotation ) *
DirectX::XMMatrixTranslation( x, y, z );
}
vkCmdPushConstants(
cmd, misc.pipelineLayout, VK_SHADER_STAGE_ALL_GRAPHICS, 0, sizeof worldTransform, &worldTransform );
vkCmdPushConstants(
cmd,
misc.pipelineLayout,
VK_SHADER_STAGE_ALL_GRAPHICS,
sizeof worldTransform,
sizeof entity.material().texture,
&entity.material().texture );
vkCmdDraw( cmd, entity.mesh().vertexCount, 1, 0, 0 );
} }
} }
vkCmdEndRendering( cmd ); vkCmdEndRendering( cmd );

View File

@ -1,337 +0,0 @@
#include "BufferManager.h"
#include "GlobalMemory.h"
template struct RID<Buffer>;
void BufferManager::destroyBuffer( Buffer& buf )
{
if ( not buf.buffer ) return;
ASSERT( m_pRenderDevice );
uint32_t const index = buf.index;
uint32_t const innerIndex = index & INDEX_MASK;
uint32_t const generation = ( index & GENERATION_MASK ) >> GENERATION_OFFSET;
RenderDevice const& renderDevice = *m_pRenderDevice;
vmaDestroyBuffer( renderDevice.gpuAllocator, Take( buf.buffer ), Take( buf.allocation ) );
buf.size = 0;
buf.mappedData = nullptr;
buf.index = innerIndex | ( generation + 1 ) << GENERATION_OFFSET;
// NOTE: DO NOT EDIT INNER INDEX.
ASSERT( innerIndex == ( buf.index & INDEX_MASK ) and "Index should not be modified" );
ASSERT( buf.index > index and "Generation should increase." );
m_freeList.pushBack( reinterpret_cast<FreeList::Node*>( &buf ) );
--m_count;
}
Buffer& BufferManager::fetchBufferUnchecked( BufferID const& rid )
{
uint32_t const index = *reinterpret_cast<uint32_t const*>( &rid );
uint32_t const innerIndex = index & INDEX_MASK;
return m_aBuffers[innerIndex];
}
void BufferManager::writeToBufferImpl( BufferID const& rid, void const* data, size_t const size )
{
ASSERT( isValidID( rid ) );
Buffer const& buffer = fetchBufferUnchecked( rid );
ASSERT( size <= buffer.size );
memcpy( buffer.mappedData, data, size );
}
bool BufferManager::isValidID( BufferID const& rid ) const
{
uint32_t const index = *reinterpret_cast<uint32_t const*>( &rid );
uint32_t const innerIndex = index & INDEX_MASK;
if ( innerIndex > m_capacity ) return false;
return m_aBuffers[innerIndex].index == index;
}
std::optional<BufferID> BufferManager::createVertexBuffer( size_t const size )
{
if ( m_freeList.empty() )
{
return std::nullopt;
}
Buffer* bufferSlot = reinterpret_cast<Buffer*>( m_freeList.popFront() );
++m_count;
ASSERT( m_pRenderDevice );
RenderDevice const& renderDevice = *m_pRenderDevice;
VkBufferCreateInfo const bufferCreateInfo = {
.sType = VK_STRUCTURE_TYPE_BUFFER_CREATE_INFO,
.pNext = nullptr,
.flags = 0,
.size = size,
.usage = VK_BUFFER_USAGE_VERTEX_BUFFER_BIT,
.sharingMode = VK_SHARING_MODE_EXCLUSIVE,
.queueFamilyIndexCount = 0,
.pQueueFamilyIndices = nullptr,
};
VmaAllocationCreateInfo constexpr allocationCreateInfo = {
.flags = VMA_ALLOCATION_CREATE_MAPPED_BIT | VMA_ALLOCATION_CREATE_HOST_ACCESS_SEQUENTIAL_WRITE_BIT,
.usage = VMA_MEMORY_USAGE_AUTO,
.requiredFlags = VK_MEMORY_PROPERTY_HOST_VISIBLE_BIT,
.preferredFlags = 0,
.memoryTypeBits = 0,
.pool = nullptr,
.pUserData = nullptr,
.priority = 1.0f,
};
VmaAllocationInfo allocationInfo;
VkBuffer vertexBuffer;
VmaAllocation vertexBufferAllocation;
VK_CHECK( vmaCreateBuffer(
renderDevice.gpuAllocator,
&bufferCreateInfo,
&allocationCreateInfo,
&vertexBuffer,
&vertexBufferAllocation,
&allocationInfo ) );
// NOTE: textureSlot preserves index between uses.
uint32_t index = bufferSlot->index;
new ( bufferSlot ) Buffer{
.buffer = vertexBuffer,
.allocation = vertexBufferAllocation,
.mappedData = static_cast<std::byte*>( allocationInfo.pMappedData ),
.deviceAddress = 0,
.size = size,
.index = index,
};
// NOTE: Memory hackery to create TextureID;
return std::move( *reinterpret_cast<BufferID*>( &index ) );
}
std::optional<BufferID> BufferManager::createIndexBuffer( size_t size )
{
if ( m_freeList.empty() )
{
return std::nullopt;
}
Buffer* bufferSlot = reinterpret_cast<Buffer*>( m_freeList.popFront() );
++m_count;
ASSERT( m_pRenderDevice );
RenderDevice const& renderDevice = *m_pRenderDevice;
VkBufferCreateInfo const bufferCreateInfo = {
.sType = VK_STRUCTURE_TYPE_BUFFER_CREATE_INFO,
.pNext = nullptr,
.flags = 0,
.size = size,
.usage = VK_BUFFER_USAGE_INDEX_BUFFER_BIT,
.sharingMode = VK_SHARING_MODE_EXCLUSIVE,
.queueFamilyIndexCount = 0,
.pQueueFamilyIndices = nullptr,
};
VmaAllocationCreateInfo constexpr allocationCreateInfo = {
.flags = VMA_ALLOCATION_CREATE_MAPPED_BIT | VMA_ALLOCATION_CREATE_HOST_ACCESS_SEQUENTIAL_WRITE_BIT,
.usage = VMA_MEMORY_USAGE_AUTO,
.requiredFlags = VK_MEMORY_PROPERTY_HOST_VISIBLE_BIT,
.preferredFlags = 0,
.memoryTypeBits = 0,
.pool = nullptr,
.pUserData = nullptr,
.priority = 1.0f,
};
VmaAllocationInfo allocationInfo;
VkBuffer indexBuffer;
VmaAllocation indexBufferAllocation;
VK_CHECK( vmaCreateBuffer(
renderDevice.gpuAllocator,
&bufferCreateInfo,
&allocationCreateInfo,
&indexBuffer,
&indexBufferAllocation,
&allocationInfo ) );
// NOTE: bufferSlot preserves index between uses.
uint32_t index = bufferSlot->index;
new ( bufferSlot ) Buffer{
.buffer = indexBuffer,
.allocation = indexBufferAllocation,
.mappedData = static_cast<std::byte*>( allocationInfo.pMappedData ),
.deviceAddress = 0,
.size = size,
.index = index,
};
// NOTE: Memory hackery to create BufferID;
return std::move( *reinterpret_cast<BufferID*>( &index ) );
}
std::optional<BufferID> BufferManager::createStorageBuffer( size_t size )
{
if ( m_freeList.empty() )
{
return std::nullopt;
}
Buffer* bufferSlot = reinterpret_cast<Buffer*>( m_freeList.popFront() );
++m_count;
ASSERT( m_pRenderDevice );
RenderDevice const& renderDevice = *m_pRenderDevice;
VkBufferCreateInfo const bufferCreateInfo = {
.sType = VK_STRUCTURE_TYPE_BUFFER_CREATE_INFO,
.pNext = nullptr,
.flags = 0,
.size = size,
.usage = VK_BUFFER_USAGE_STORAGE_BUFFER_BIT | VK_BUFFER_USAGE_SHADER_DEVICE_ADDRESS_BIT,
.sharingMode = VK_SHARING_MODE_EXCLUSIVE,
.queueFamilyIndexCount = 0,
.pQueueFamilyIndices = nullptr,
};
VmaAllocationCreateInfo constexpr allocationCreateInfo = {
.flags = VMA_ALLOCATION_CREATE_MAPPED_BIT | VMA_ALLOCATION_CREATE_HOST_ACCESS_SEQUENTIAL_WRITE_BIT,
.usage = VMA_MEMORY_USAGE_AUTO,
.requiredFlags = VK_MEMORY_PROPERTY_HOST_VISIBLE_BIT,
.preferredFlags = 0,
.memoryTypeBits = 0,
.pool = nullptr,
.pUserData = nullptr,
.priority = 1.0f,
};
VmaAllocationInfo allocationInfo;
VkBuffer storageBuffer;
VmaAllocation storageBufferAllocation;
VK_CHECK( vmaCreateBuffer(
renderDevice.gpuAllocator,
&bufferCreateInfo,
&allocationCreateInfo,
&storageBuffer,
&storageBufferAllocation,
&allocationInfo ) );
VkBufferDeviceAddressInfo const deviceAddressInfo = {
.sType = VK_STRUCTURE_TYPE_BUFFER_DEVICE_ADDRESS_INFO,
.pNext = nullptr,
.buffer = storageBuffer,
};
VkDeviceAddress const deviceAddress = vkGetBufferDeviceAddress( renderDevice.device, &deviceAddressInfo );
// NOTE: bufferSlot preserves index between uses.
uint32_t index = bufferSlot->index;
new ( bufferSlot ) Buffer{
.buffer = storageBuffer,
.allocation = storageBufferAllocation,
.mappedData = static_cast<std::byte*>( allocationInfo.pMappedData ),
.deviceAddress = deviceAddress,
.size = size,
.index = index,
};
// NOTE: Memory hackery to create BufferID;
return std::move( *reinterpret_cast<BufferID*>( &index ) );
}
void BufferManager::freeBuffer( BufferID&& rid )
{
if ( not isValidID( rid ) ) return;
Buffer& buffer = fetchBufferUnchecked( rid );
destroyBuffer( buffer );
auto _ = std::move( rid );
}
std::optional<VkBuffer> BufferManager::fetchBuffer( BufferID const& rid )
{
if ( not isValidID( rid ) ) return std::nullopt;
return fetchBufferUnchecked( rid ).buffer;
}
std::optional<VkDeviceAddress> BufferManager::fetchDeviceAddress( BufferID const& rid )
{
if ( not isValidID( rid ) ) return std::nullopt;
Buffer const& buffer = fetchBufferUnchecked( rid );
if ( buffer.deviceAddress == 0 ) return std::nullopt;
return buffer.deviceAddress;
}
BufferManager::BufferManager( RenderDevice* pRenderDevice, Buffer* aBuffers, uint32_t const capacity )
: m_pRenderDevice{ pRenderDevice }, m_aBuffers{ aBuffers }, m_count{ 0 }, m_capacity{ capacity }
{
uint32_t i = 0;
for ( Buffer& tex : std::span{ m_aBuffers, m_capacity } )
{
// Default Generation is 1
tex.index = i++ | ( 1 << GENERATION_OFFSET );
m_freeList.pushFront( reinterpret_cast<FreeList::Node*>( &tex ) );
}
}
void BufferManager::destroy()
{
#if defined( _DEBUG )
if ( m_count > 0 )
{
SDL_LogError( SDL_LOG_CATEGORY_ERROR, "%u buffers still allocated.", m_count );
}
#endif
while ( not m_freeList.empty() )
{
Buffer* buf = reinterpret_cast<Buffer*>( m_freeList.popFront() );
memset( buf, 0, sizeof *buf );
}
for ( Buffer& buf : std::span{ m_aBuffers, m_count } )
{
destroyBuffer( buf );
}
}
BufferManager::~BufferManager()
{
ASSERT( not m_aBuffers );
}
BufferManager* BufferManager_Create( GlobalMemory* mem, RenderDevice* renderDevice, uint32_t maxCount )
{
Buffer* buffers = reinterpret_cast<Buffer*>( mem->allocate( maxCount * sizeof( Buffer ), alignof( Buffer ) ) );
if ( not buffers ) return nullptr;
std::byte* allocation = mem->allocate( sizeof( BufferManager ), alignof( BufferManager ) );
if ( not allocation ) return nullptr;
return new ( allocation ) BufferManager{ renderDevice, buffers, maxCount };
}

View File

@ -1,87 +0,0 @@
#pragma once
#include <optional>
#include <span>
#include "FreeList.h"
#include "MacroUtils.h"
#include "RID.h"
#include "RenderDevice.h"
#include "VulkanHeader.h"
struct GlobalMemory;
struct RenderDevice;
struct Buffer
{
VkBuffer buffer;
VmaAllocation allocation;
std::byte* mappedData; // Assume the system has ReBAR/SAM enabled.
VkDeviceAddress deviceAddress;
size_t size;
uint32_t index;
};
static_assert( sizeof( Buffer ) > sizeof( FreeList::Node ) and "Buffer is used intrusively by FreeList" );
static_assert(
offsetof( Buffer, index ) >= sizeof( FreeList::Node ) and "Index should not be overwritten even in invalid state" );
extern template struct RID<Buffer>;
using BufferID = RID<Buffer>;
struct BufferManager
{
private:
constexpr static uint32_t INDEX_MASK = 0x0007FFFF;
constexpr static uint32_t GENERATION_MASK = ~INDEX_MASK;
constexpr static uint32_t GENERATION_OFFSET = 19;
static_assert(
( ( GENERATION_MASK >> GENERATION_OFFSET & 0x1 ) == 0x1 ) and
( ( GENERATION_MASK >> ( GENERATION_OFFSET - 1 ) & 0x1 ) != 0x1 ) and "Checks boundary" );
RenderDevice* m_pRenderDevice;
// Texture Manager
Buffer* m_aBuffers;
uint32_t m_count;
uint32_t m_capacity;
FreeList m_freeList;
void destroyBuffer( Buffer& buf );
Buffer& fetchBufferUnchecked( BufferID const& rid );
void writeToBufferImpl( BufferID const& rid, void const* data, size_t size );
public:
[[nodiscard]] bool isValidID( BufferID const& rid ) const;
std::optional<BufferID> createVertexBuffer( size_t size );
std::optional<BufferID> createIndexBuffer( size_t size );
std::optional<BufferID> createStorageBuffer( size_t size );
void freeBuffer( BufferID&& rid );
DEPRECATE_JULY_2025
std::optional<VkBuffer> fetchBuffer( BufferID const& rid );
std::optional<VkDeviceAddress> fetchDeviceAddress( BufferID const& rid );
void writeToBuffer( BufferID const& rid, std::ranges::contiguous_range auto const& data )
{
writeToBufferImpl(
rid,
std::ranges::data( data ),
std::ranges::size( data ) * sizeof( std::ranges::range_value_t<decltype( data )> ) );
}
//
BufferManager( RenderDevice* pRenderDevice, Buffer* aBuffers, uint32_t capacity );
void destroy();
BufferManager( BufferManager const& other ) = delete;
BufferManager( BufferManager&& other ) noexcept = delete;
BufferManager& operator=( BufferManager const& other ) = delete;
BufferManager& operator=( BufferManager&& other ) noexcept = delete;
~BufferManager();
};
BufferManager* BufferManager_Create( GlobalMemory* mem, RenderDevice* renderDevice, uint32_t maxCount );

View File

@ -1,3 +0,0 @@
#define CGLTF_IMPLEMENTATION
#include <cgltf.h>

View File

@ -5,143 +5,480 @@
#include "GlobalMemory.h" #include "GlobalMemory.h"
#include "RenderDevice.h" #include "RenderDevice.h"
#include <stb_image.h>
#include "Frame.h" #include "Frame.h"
#include "TextureManager.h" #include "TextureManager.h"
Entity& EntitySiblingIterable::Iterator::operator++() Entity* EntityManager::createEntity(
Transform const& transform, std::span<Vertex> const vertices, const char* textureFile )
{ {
current = current->nextSibling(); ASSERT( pRenderDevice );
return *current; RenderDevice& renderDevice = *pRenderDevice;
}
bool EntitySiblingIterable::Iterator::operator==( Iterator const& other ) const Mesh mesh;
{
return current == other.current;
}
Entity& EntitySiblingIterable::Iterator::operator*() const
{
return *current;
}
EntitySiblingIterable::Iterator EntitySiblingIterable::begin()
{
return { current };
}
EntitySiblingIterable::Iterator EntitySiblingIterable::end()
{
return {};
}
void Entity::setParent( Entity* parent )
{
ASSERT( parent );
if ( m_parent == parent ) return;
removeParent();
// Insert self into parent.
m_parent = parent;
Entity* oldHead = parent->m_firstChild;
if ( oldHead )
{ {
// Old head is next after this mesh.vertexCount = static_cast<uint32_t>( vertices.size() );
this->m_nextSibling = oldHead; mesh.vertexBufferSize = static_cast<uint32_t>( vertices.size_bytes() );
// This is prev to old head VkBufferCreateInfo const bufferCreateInfo = {
oldHead->m_prevSibling = this; .sType = VK_STRUCTURE_TYPE_BUFFER_CREATE_INFO,
} .pNext = nullptr,
// We are the head now. .flags = 0,
m_parent->m_firstChild = this; .size = mesh.vertexBufferSize,
} .usage = VK_BUFFER_USAGE_VERTEX_BUFFER_BIT,
void Entity::addChild( Entity* child ) .sharingMode = VK_SHARING_MODE_EXCLUSIVE,
{ .queueFamilyIndexCount = 0,
child->setParent( this ); .pQueueFamilyIndices = nullptr,
} };
void Entity::removeChild( Entity* child ) VmaAllocationCreateInfo constexpr allocationCreateInfo = {
{ .flags = VMA_ALLOCATION_CREATE_MAPPED_BIT | VMA_ALLOCATION_CREATE_HOST_ACCESS_SEQUENTIAL_WRITE_BIT,
ASSERT( child ); .usage = VMA_MEMORY_USAGE_AUTO,
child->removeParent(); .requiredFlags = VK_MEMORY_PROPERTY_HOST_VISIBLE_BIT,
} .preferredFlags = 0,
.memoryTypeBits = 0,
.pool = nullptr,
.pUserData = nullptr,
.priority = 1.0f,
};
void Entity::removeParent() VmaAllocationInfo allocationInfo;
{
if ( m_parent )
{
// Replace prev of next with prev of self VK_CHECK( vmaCreateBuffer(
if ( m_nextSibling ) m_nextSibling->m_prevSibling = m_prevSibling; pRenderDevice->gpuAllocator,
&bufferCreateInfo,
&allocationCreateInfo,
&mesh.vertexBuffer,
&mesh.vertexBufferAllocation,
&allocationInfo ) );
// Replace next of prev with next of self if ( allocationInfo.pMappedData )
if ( m_prevSibling )
{ {
m_prevSibling->m_nextSibling = m_nextSibling; memcpy( allocationInfo.pMappedData, vertices.data(), vertices.size_bytes() );
} }
else }
Material material;
{
VkSampler sampler;
uint32_t width;
uint32_t height;
uint32_t numChannels = 4;
stbi_uc* textureData;
{ {
// We are head of chain int w;
m_parent->m_firstChild = m_nextSibling; int h;
int nc;
int requestedChannels = static_cast<int>( numChannels );
textureData = stbi_load( textureFile, &w, &h, &nc, requestedChannels );
ASSERT( nc <= requestedChannels );
if ( not textureData )
{
vmaDestroyBuffer( pRenderDevice->gpuAllocator, Take( mesh.vertexBuffer ), Take( mesh.vertexBufferAllocation ) );
SDL_LogError( SDL_LOG_CATEGORY_ERROR, "%s", stbi_failure_reason() );
return nullptr;
}
width = static_cast<uint32_t>( w );
height = static_cast<uint32_t>( h );
} }
m_nextSibling = nullptr; VkSamplerCreateInfo constexpr samplerCreateInfo = {
m_prevSibling = nullptr; .sType = VK_STRUCTURE_TYPE_SAMPLER_CREATE_INFO,
m_parent = nullptr; .pNext = nullptr,
.flags = 0,
.magFilter = VK_FILTER_LINEAR,
.minFilter = VK_FILTER_LINEAR,
.mipmapMode = VK_SAMPLER_MIPMAP_MODE_LINEAR,
.addressModeU = VK_SAMPLER_ADDRESS_MODE_REPEAT,
.addressModeV = VK_SAMPLER_ADDRESS_MODE_REPEAT,
.addressModeW = VK_SAMPLER_ADDRESS_MODE_REPEAT,
.mipLodBias = 0.0,
.anisotropyEnable = true,
.maxAnisotropy = 1.0f,
.compareEnable = false,
.compareOp = VK_COMPARE_OP_NEVER,
.minLod = 0.0f,
.maxLod = VK_LOD_CLAMP_NONE,
.borderColor = VK_BORDER_COLOR_FLOAT_OPAQUE_BLACK,
.unnormalizedCoordinates = false,
};
VK_CHECK( vkCreateSampler( renderDevice.device, &samplerCreateInfo, nullptr, &sampler ) );
auto textureOpt = renderDevice.textureManager->createTexture( { width, height, 1 }, sampler );
if ( not textureOpt )
{
vmaDestroyBuffer( pRenderDevice->gpuAllocator, Take( mesh.vertexBuffer ), Take( mesh.vertexBufferAllocation ) );
SDL_LogError( SDL_LOG_CATEGORY_ERROR, "%s", stbi_failure_reason() );
stbi_image_free( textureData );
return nullptr;
}
TextureID texture = std::move( textureOpt.value() );
VkImage textureImage = renderDevice.textureManager->fetchImage( texture ).value();
// Staging Buffer Create
VkBuffer stagingBuffer;
VmaAllocation stagingAllocation;
{
VkBufferCreateInfo const stagingBufferCreateInfo = {
.sType = VK_STRUCTURE_TYPE_BUFFER_CREATE_INFO,
.pNext = nullptr,
.flags = 0,
.size = static_cast<VkDeviceSize>( width ) * height * numChannels * sizeof( textureData[0] ),
.usage = VK_BUFFER_USAGE_TRANSFER_SRC_BIT,
.sharingMode = VK_SHARING_MODE_EXCLUSIVE,
.queueFamilyIndexCount = 0,
.pQueueFamilyIndices = nullptr,
};
VmaAllocationCreateInfo constexpr stagingAllocationCreateInfo = {
.flags = VMA_ALLOCATION_CREATE_MAPPED_BIT | VMA_ALLOCATION_CREATE_HOST_ACCESS_SEQUENTIAL_WRITE_BIT,
.usage = VMA_MEMORY_USAGE_AUTO,
.requiredFlags = VK_MEMORY_PROPERTY_HOST_VISIBLE_BIT,
.preferredFlags = 0,
.memoryTypeBits = 0,
.pool = nullptr,
.pUserData = nullptr,
.priority = 1.0f,
};
VmaAllocationInfo allocationInfo;
VK_CHECK( vmaCreateBuffer(
renderDevice.gpuAllocator,
&stagingBufferCreateInfo,
&stagingAllocationCreateInfo,
&stagingBuffer,
&stagingAllocation,
&allocationInfo ) );
if ( allocationInfo.pMappedData )
{
memcpy( allocationInfo.pMappedData, textureData, stagingBufferCreateInfo.size );
}
}
// All data is copied to stagingBuffer, don't need this.
stbi_image_free( textureData );
// Staging -> Texture transfer
{
Frame& frameInUse = renderDevice.frames[renderDevice.frameIndex];
// This should just pass.
VK_CHECK( vkWaitForFences( renderDevice.device, 1, &frameInUse.frameReadyToReuse, VK_TRUE, INT64_MAX ) );
// Reset Frame
VK_CHECK( vkResetFences( renderDevice.device, 1, &frameInUse.frameReadyToReuse ) );
VK_CHECK( vkResetCommandPool( renderDevice.device, frameInUse.commandPool, 0 ) );
VkCommandBufferBeginInfo constexpr beginInfo = {
.sType = VK_STRUCTURE_TYPE_COMMAND_BUFFER_BEGIN_INFO,
.pNext = nullptr,
.flags = VK_COMMAND_BUFFER_USAGE_ONE_TIME_SUBMIT_BIT,
.pInheritanceInfo = nullptr,
};
uint32_t mipLevels = TextureManager::calculateRequiredMipLevels( width, height, 1 );
VkImageSubresourceRange const subresourceRange = {
.aspectMask = VK_IMAGE_ASPECT_COLOR_BIT,
.baseMipLevel = 0,
.levelCount = mipLevels,
.baseArrayLayer = 0,
.layerCount = 1,
};
VkImageMemoryBarrier2 const creationToTransferImageBarrier = {
.sType = VK_STRUCTURE_TYPE_IMAGE_MEMORY_BARRIER_2,
.pNext = nullptr,
.srcStageMask = VK_PIPELINE_STAGE_2_TOP_OF_PIPE_BIT,
.srcAccessMask = VK_ACCESS_2_NONE,
.dstStageMask = VK_PIPELINE_STAGE_2_COPY_BIT,
.dstAccessMask = VK_ACCESS_2_TRANSFER_WRITE_BIT,
.oldLayout = VK_IMAGE_LAYOUT_UNDEFINED,
.newLayout = VK_IMAGE_LAYOUT_TRANSFER_DST_OPTIMAL,
.srcQueueFamilyIndex = VK_QUEUE_FAMILY_IGNORED,
.dstQueueFamilyIndex = VK_QUEUE_FAMILY_IGNORED,
.image = renderDevice.textureManager->fetchImage( texture ).value(),
.subresourceRange = subresourceRange,
};
VkDependencyInfo const creationToTransferDependency = {
.sType = VK_STRUCTURE_TYPE_DEPENDENCY_INFO,
.pNext = nullptr,
.dependencyFlags = 0,
.memoryBarrierCount = 0,
.pMemoryBarriers = nullptr,
.bufferMemoryBarrierCount = 0,
.pBufferMemoryBarriers = nullptr,
.imageMemoryBarrierCount = 1,
.pImageMemoryBarriers = &creationToTransferImageBarrier,
};
std::array transferToReadyImageBarriers{
// transferToReadyImageBarrier
VkImageMemoryBarrier2{
.sType = VK_STRUCTURE_TYPE_IMAGE_MEMORY_BARRIER_2,
.pNext = nullptr,
.srcStageMask = VK_PIPELINE_STAGE_2_TRANSFER_BIT,
.srcAccessMask = VK_ACCESS_2_TRANSFER_WRITE_BIT,
.dstStageMask = VK_PIPELINE_STAGE_2_FRAGMENT_SHADER_BIT,
.dstAccessMask = VK_ACCESS_2_SHADER_SAMPLED_READ_BIT,
.oldLayout = VK_IMAGE_LAYOUT_TRANSFER_SRC_OPTIMAL,
.newLayout = VK_IMAGE_LAYOUT_SHADER_READ_ONLY_OPTIMAL,
.srcQueueFamilyIndex = VK_QUEUE_FAMILY_IGNORED,
.dstQueueFamilyIndex = VK_QUEUE_FAMILY_IGNORED,
.image = textureImage,
.subresourceRange = {
.aspectMask = VK_IMAGE_ASPECT_COLOR_BIT,
.baseMipLevel = 0,
.levelCount = mipLevels-1,
.baseArrayLayer = 0,
.layerCount = 1,
},
},
VkImageMemoryBarrier2{
.sType = VK_STRUCTURE_TYPE_IMAGE_MEMORY_BARRIER_2,
.pNext = nullptr,
.srcStageMask = VK_PIPELINE_STAGE_2_TRANSFER_BIT,
.srcAccessMask = VK_ACCESS_2_TRANSFER_WRITE_BIT,
.dstStageMask = VK_PIPELINE_STAGE_2_FRAGMENT_SHADER_BIT,
.dstAccessMask = VK_ACCESS_2_SHADER_SAMPLED_READ_BIT,
.oldLayout = VK_IMAGE_LAYOUT_TRANSFER_DST_OPTIMAL,
.newLayout = VK_IMAGE_LAYOUT_SHADER_READ_ONLY_OPTIMAL,
.srcQueueFamilyIndex = VK_QUEUE_FAMILY_IGNORED,
.dstQueueFamilyIndex = VK_QUEUE_FAMILY_IGNORED,
.image = textureImage,
.subresourceRange = {
.aspectMask = VK_IMAGE_ASPECT_COLOR_BIT,
.baseMipLevel = mipLevels-1,
.levelCount = 1,
.baseArrayLayer = 0,
.layerCount = 1,
},
}
};
VkDependencyInfo const transferToReadyDependency = {
.sType = VK_STRUCTURE_TYPE_DEPENDENCY_INFO,
.pNext = nullptr,
.dependencyFlags = 0,
.memoryBarrierCount = 0,
.pMemoryBarriers = nullptr,
.bufferMemoryBarrierCount = 0,
.pBufferMemoryBarriers = nullptr,
.imageMemoryBarrierCount = static_cast<uint32_t>( transferToReadyImageBarriers.size() ),
.pImageMemoryBarriers = transferToReadyImageBarriers.data(),
};
VkImageSubresourceRange const mipLevelSubresource = {
.aspectMask = VK_IMAGE_ASPECT_COLOR_BIT,
.baseMipLevel = 0,
.levelCount = 1,
.baseArrayLayer = 0,
.layerCount = 1,
};
std::array prepareNextMipLevelBarriers{
// prepareNextMipLevelSrcImageBarrier
VkImageMemoryBarrier2{
.sType = VK_STRUCTURE_TYPE_IMAGE_MEMORY_BARRIER_2,
.pNext = nullptr,
.srcStageMask = VK_PIPELINE_STAGE_2_TRANSFER_BIT,
.srcAccessMask = VK_ACCESS_2_TRANSFER_WRITE_BIT,
.dstStageMask = VK_PIPELINE_STAGE_2_TRANSFER_BIT,
.dstAccessMask = VK_ACCESS_2_TRANSFER_READ_BIT,
.oldLayout = VK_IMAGE_LAYOUT_TRANSFER_DST_OPTIMAL,
.newLayout = VK_IMAGE_LAYOUT_TRANSFER_SRC_OPTIMAL,
.srcQueueFamilyIndex = VK_QUEUE_FAMILY_IGNORED,
.dstQueueFamilyIndex = VK_QUEUE_FAMILY_IGNORED,
.image = textureImage,
.subresourceRange = mipLevelSubresource,
},
// prepareNextMipLevelDstImageBarrier
VkImageMemoryBarrier2{
.sType = VK_STRUCTURE_TYPE_IMAGE_MEMORY_BARRIER_2,
.pNext = nullptr,
.srcStageMask = VK_PIPELINE_STAGE_2_COPY_BIT,
.srcAccessMask = VK_ACCESS_2_TRANSFER_WRITE_BIT,
.dstStageMask = VK_PIPELINE_STAGE_2_BLIT_BIT,
.dstAccessMask = VK_ACCESS_2_TRANSFER_WRITE_BIT,
.oldLayout = VK_IMAGE_LAYOUT_UNDEFINED,
.newLayout = VK_IMAGE_LAYOUT_UNDEFINED,
.srcQueueFamilyIndex = VK_QUEUE_FAMILY_IGNORED,
.dstQueueFamilyIndex = VK_QUEUE_FAMILY_IGNORED,
.image = textureImage,
.subresourceRange = mipLevelSubresource,
}
};
VkDependencyInfo const prepareNextMipLevelDependency = {
.sType = VK_STRUCTURE_TYPE_DEPENDENCY_INFO,
.pNext = nullptr,
.dependencyFlags = 0,
.memoryBarrierCount = 0,
.pMemoryBarriers = nullptr,
.bufferMemoryBarrierCount = 0,
.pBufferMemoryBarriers = nullptr,
.imageMemoryBarrierCount = static_cast<uint32_t>( prepareNextMipLevelBarriers.size() ),
.pImageMemoryBarriers = prepareNextMipLevelBarriers.data(),
};
vkBeginCommandBuffer( frameInUse.commandBuffer, &beginInfo );
{
VkImageSubresourceLayers imageSubresourceLayers = {
.aspectMask = VK_IMAGE_ASPECT_COLOR_BIT,
.mipLevel = 0,
.baseArrayLayer = 0,
.layerCount = 1,
};
// TODO: Ensure `bufferRowLength` and `bufferImageHeight` are not required.
VkBufferImageCopy copyRegion = {
.bufferOffset = 0,
.bufferRowLength = 0,
.bufferImageHeight = 0,
.imageSubresource = imageSubresourceLayers,
.imageOffset = { 0, 0, 0 },
.imageExtent = { width, height, 1 }
};
// Start
vkCmdPipelineBarrier2( frameInUse.commandBuffer, &creationToTransferDependency );
// Staging -> Image L0
vkCmdCopyBufferToImage(
frameInUse.commandBuffer,
stagingBuffer,
textureImage,
VK_IMAGE_LAYOUT_TRANSFER_DST_OPTIMAL,
1,
&copyRegion );
prepareNextMipLevelBarriers[0].subresourceRange.baseMipLevel = 0;
prepareNextMipLevelBarriers[1].subresourceRange.baseMipLevel = 1;
int32_t mipSrcWidth = static_cast<int32_t>( width );
int32_t mipSrcHeight = static_cast<int32_t>( height );
int32_t mipDstWidth = std::max( mipSrcWidth / 2, 1 );
int32_t mipDstHeight = std::max( mipSrcHeight / 2, 1 );
VkImageSubresourceLayers constexpr mipSubresourceLayers = {
.aspectMask = VK_IMAGE_ASPECT_COLOR_BIT,
.mipLevel = 0,
.baseArrayLayer = 0,
.layerCount = 1,
};
VkImageBlit2 imageBlit = {
.sType = VK_STRUCTURE_TYPE_IMAGE_BLIT_2,
.pNext = nullptr,
.srcSubresource = mipSubresourceLayers,
.srcOffsets = { { 0, 0, 0 }, { mipSrcWidth, mipSrcHeight, 1 } },
.dstSubresource = mipSubresourceLayers,
.dstOffsets = { { 0, 0, 0 }, { mipDstWidth, mipDstHeight, 1 } },
};
imageBlit.srcSubresource.mipLevel = 0;
imageBlit.dstSubresource.mipLevel = 1;
imageBlit.srcOffsets[1].x = mipSrcWidth;
imageBlit.srcOffsets[1].y = mipSrcHeight;
imageBlit.dstOffsets[1].x = mipDstWidth;
imageBlit.dstOffsets[1].y = mipDstHeight;
VkBlitImageInfo2 blitInfo = {
.sType = VK_STRUCTURE_TYPE_BLIT_IMAGE_INFO_2,
.pNext = nullptr,
.srcImage = textureImage,
.srcImageLayout = VK_IMAGE_LAYOUT_TRANSFER_SRC_OPTIMAL,
.dstImage = textureImage,
.dstImageLayout = VK_IMAGE_LAYOUT_TRANSFER_DST_OPTIMAL,
.regionCount = 1,
.pRegions = &imageBlit,
.filter = VK_FILTER_LINEAR,
};
// MipMapping
for ( uint32_t dstMipLevel = 1; dstMipLevel < mipLevels; ++dstMipLevel )
{
vkCmdPipelineBarrier2( frameInUse.commandBuffer, &prepareNextMipLevelDependency );
vkCmdBlitImage2( frameInUse.commandBuffer, &blitInfo );
// Prep for NEXT iteration
mipSrcWidth = mipDstWidth;
mipSrcHeight = mipDstHeight;
mipDstWidth = std::max( mipSrcWidth / 2, 1 );
mipDstHeight = std::max( mipSrcHeight / 2, 1 );
imageBlit.srcSubresource.mipLevel = dstMipLevel;
imageBlit.dstSubresource.mipLevel = dstMipLevel + 1;
imageBlit.srcOffsets[1].x = mipSrcWidth;
imageBlit.srcOffsets[1].y = mipSrcHeight;
imageBlit.dstOffsets[1].x = mipDstWidth;
imageBlit.dstOffsets[1].y = mipDstHeight;
// Prep current mip level as source
prepareNextMipLevelBarriers[0].subresourceRange.baseMipLevel = dstMipLevel;
prepareNextMipLevelBarriers[1].subresourceRange.baseMipLevel = dstMipLevel + 1;
}
// End
vkCmdPipelineBarrier2( frameInUse.commandBuffer, &transferToReadyDependency );
}
vkEndCommandBuffer( frameInUse.commandBuffer );
VkSubmitInfo submitInfo = {
.sType = VK_STRUCTURE_TYPE_SUBMIT_INFO,
.pNext = nullptr,
.waitSemaphoreCount = 0,
.pWaitSemaphores = nullptr,
.pWaitDstStageMask = nullptr,
.commandBufferCount = 1,
.pCommandBuffers = &frameInUse.commandBuffer,
.signalSemaphoreCount = 0,
.pSignalSemaphores = nullptr,
};
VK_CHECK( vkQueueSubmit( renderDevice.directQueue, 1, &submitInfo, frameInUse.frameReadyToReuse ) );
// Do not reset this. Else, the frame will never be available to the main loop.
VK_CHECK( vkWaitForFences( renderDevice.device, 1, &frameInUse.frameReadyToReuse, VK_TRUE, UINT64_MAX ) );
renderDevice.frameIndex = ( renderDevice.frameIndex + 1 ) % renderDevice.getNumFrames();
}
vmaDestroyBuffer( renderDevice.gpuAllocator, stagingBuffer, stagingAllocation );
material = { std::move( texture ), sampler };
} }
}
EntitySiblingIterable Entity::children() const entities[count++] = Entity( transform, mesh, std::move( material ) );
{
return { m_firstChild };
}
Entity::Entity( Transform const& transform ) return entities + count;
: transform{ transform }
, model{}
, modelMesh{}
, m_parent{ nullptr }
, m_firstChild{ nullptr }
, m_prevSibling{ nullptr }
, m_nextSibling{ nullptr }
, m_flags{ 0 }
{}
Entity* EntityManager::createEntity( Transform const& transform )
{
ASSERT( count < capacity );
Entity& entity = entities[count++];
new ( &entity ) Entity{ transform };
return &entity;
} }
void EntityManager::destroyEntity( Entity* entity ) void EntityManager::destroyEntity( Entity* entity )
{ {
ASSERT( entity ); ASSERT( entity );
if ( !entity->isInit() ) return;
VkDevice const device = pRenderDevice->device; VkDevice const device = pRenderDevice->device;
VmaAllocator const allocator = pRenderDevice->gpuAllocator;
if ( not entity->model.isNull() ) vkDestroySampler( device, Take( entity->material().sampler ), nullptr );
{
for ( auto& material : entity->model.materials )
{
vkDestroySampler( device, Take( material.sampler ), nullptr );
pRenderDevice->textureManager->freeTexture( std::move( material.texture ) );
}
pRenderDevice->bufferManager->freeBuffer( std::move( entity->model.vertexBuffer ) ); pRenderDevice->textureManager->freeTexture( std::move( entity->material().texture ) );
pRenderDevice->bufferManager->freeBuffer( std::move( entity->model.indexBuffer ) ); vmaDestroyBuffer( allocator, Take( entity->mesh().vertexBuffer ), Take( entity->mesh().vertexBufferAllocation ) );
entity->model.primitives.clear();
entity->model.materials.clear();
}
entity->modelMesh = { 0, 0 }; // TODO: Leaking descriptor set.
} }
void EntityManager::destroy() void EntityManager::destroy()

View File

@ -2,84 +2,93 @@
#include <cstdint> #include <cstdint>
#include <volk.h>
#include <vma/vk_mem_alloc.h>
#include <DirectXMath.h> #include <DirectXMath.h>
#include <span> #include <span>
#include "VulkanHeader.h"
// TODO: Remove this dependency // TODO: Remove this dependency
#include "BufferManager.h"
#include "ModelLoader.h"
#include "TextureManager.h" #include "TextureManager.h"
struct Entity;
struct RenderDevice; struct RenderDevice;
struct GlobalMemory; struct GlobalMemory;
struct Transform struct Vertex
{ {
DirectX::XMVECTOR translation; DirectX::XMFLOAT3 position;
DirectX::XMVECTOR rotation; DirectX::XMFLOAT3 color;
DirectX::XMVECTOR scale; DirectX::XMFLOAT2 texCoord0;
}; };
struct EntitySiblingIterable struct Transform
{ {
Entity* current; DirectX::XMFLOAT3 position;
float scale;
DirectX::XMVECTOR rotation;
};
struct Iterator struct Mesh
{ {
Entity* current = nullptr; VkBuffer vertexBuffer;
VmaAllocation vertexBufferAllocation;
uint32_t vertexBufferSize;
uint32_t vertexCount;
};
Entity& operator++(); struct Material
bool operator==( Iterator const& ) const; {
Entity& operator*() const; TextureID texture;
}; VkSampler sampler; // TODO: Reuse
Iterator begin();
Iterator end();
}; };
struct Entity struct Entity
{ {
Transform transform;
Model model;
ModelMesh modelMesh;
private: private:
Entity* m_parent; // TODO: Switch to EntityIndex. Transform m_transform;
Entity* m_firstChild; Mesh m_mesh;
Entity* m_prevSibling; Material m_material;
Entity* m_nextSibling;
uint64_t m_flags; // FIXME: Wasting space.
public: public:
[[nodiscard]] bool isRoot() const [[nodiscard]] Transform& transform()
{ {
return not m_parent; return m_transform;
} }
[[nodiscard]] Entity* parent() const [[nodiscard]] Transform const& transform() const
{ {
return m_parent; return m_transform;
} }
[[nodiscard]] Entity* nextSibling() const [[nodiscard]] Mesh& mesh()
{ {
return m_nextSibling; return m_mesh;
} }
void setParent( Entity* parent ); [[nodiscard]] Mesh const& mesh() const
{
return m_mesh;
}
void addChild( Entity* child ); [[nodiscard]] Material& material()
{
return m_material;
}
void removeChild( Entity* child ); [[nodiscard]] Material const& material() const
{
return m_material;
}
// Remove self from parent [[nodiscard]] bool isInit() const
void removeParent(); {
return m_mesh.vertexBuffer or m_material.texture;
}
[[nodiscard]] EntitySiblingIterable children() const; Entity( Transform const& transform, Mesh const& mesh, Material&& material )
: m_transform{ transform }, m_mesh{ mesh }, m_material{ std::forward<Material>( material ) }
explicit Entity( Transform const& transform ); {}
}; };
struct EntityManager struct EntityManager
@ -120,7 +129,7 @@ struct EntityManager
} }
// Make Entities return ID, make it a sparse indexing system. // Make Entities return ID, make it a sparse indexing system.
Entity* createEntity( Transform const& transform ); Entity* createEntity( Transform const& transform, std::span<Vertex> vertices, const char* textureFile );
void destroyEntity( Entity* entity ); void destroyEntity( Entity* entity );

View File

@ -15,18 +15,12 @@ Frame::Frame(
VkCommandBuffer const commandBuffer, VkCommandBuffer const commandBuffer,
VkSemaphore const imageAcquiredSemaphore, VkSemaphore const imageAcquiredSemaphore,
VkSemaphore const renderFinishedSemaphore, VkSemaphore const renderFinishedSemaphore,
VkFence const frameReadyToReuse, VkFence const frameReadyToReuse )
VkImage const depthImage,
VmaAllocation const depthAllocation,
VkImageView const depthView )
: commandPool{ commandPool } : commandPool{ commandPool }
, commandBuffer{ commandBuffer } , commandBuffer{ commandBuffer }
, imageAcquiredSemaphore{ imageAcquiredSemaphore } , imageAcquiredSemaphore{ imageAcquiredSemaphore }
, renderFinishedSemaphore{ renderFinishedSemaphore } , renderFinishedSemaphore{ renderFinishedSemaphore }
, frameReadyToReuse{ frameReadyToReuse } , frameReadyToReuse{ frameReadyToReuse }
, depthImage{ depthImage }
, depthAllocation{ depthAllocation }
, depthView{ depthView }
{} {}
void Frame::destroy( RenderDevice const& renderDevice ) void Frame::destroy( RenderDevice const& renderDevice )
@ -35,9 +29,6 @@ void Frame::destroy( RenderDevice const& renderDevice )
VkDevice const device = renderDevice.device; VkDevice const device = renderDevice.device;
vkDestroyImageView( device, Take( depthView ), nullptr );
vmaDestroyImage( renderDevice.gpuAllocator, Take( depthImage ), Take( depthAllocation ) );
vkDestroyCommandPool( device, Take( commandPool ), nullptr ); vkDestroyCommandPool( device, Take( commandPool ), nullptr );
vkDestroyFence( device, Take( frameReadyToReuse ), nullptr ); vkDestroyFence( device, Take( frameReadyToReuse ), nullptr );
vkDestroySemaphore( device, Take( imageAcquiredSemaphore ), nullptr ); vkDestroySemaphore( device, Take( imageAcquiredSemaphore ), nullptr );
@ -50,12 +41,7 @@ Frame::~Frame()
ASSERT( !isInit() ); ASSERT( !isInit() );
} }
void Frame_Create( void Frame_Create( Frame* frame, VkDevice const device, uint32_t const directQueueFamilyIndex )
Frame* frame,
VkDevice const device,
VmaAllocator const gpuAllocator,
uint32_t const directQueueFamilyIndex,
VkExtent2D const swapchainExtent )
{ {
VkCommandPool commandPool; VkCommandPool commandPool;
VkCommandBuffer commandBuffer; VkCommandBuffer commandBuffer;
@ -63,10 +49,6 @@ void Frame_Create(
VkSemaphore renderFinishedSemaphore; VkSemaphore renderFinishedSemaphore;
VkFence frameReadyToReuse; VkFence frameReadyToReuse;
VkImage depthImage;
VmaAllocation depthAllocation;
VkImageView depthView;
{ {
VkCommandPoolCreateInfo const commandPoolCreateInfo = { VkCommandPoolCreateInfo const commandPoolCreateInfo = {
.sType = VK_STRUCTURE_TYPE_COMMAND_POOL_CREATE_INFO, .sType = VK_STRUCTURE_TYPE_COMMAND_POOL_CREATE_INFO,
@ -101,74 +83,9 @@ void Frame_Create(
VK_CHECK( vkCreateFence( device, &fenceCreateInfo, nullptr, &frameReadyToReuse ) ); VK_CHECK( vkCreateFence( device, &fenceCreateInfo, nullptr, &frameReadyToReuse ) );
} }
{
VkImageCreateInfo const depthImageCreateInfo = {
.sType = VK_STRUCTURE_TYPE_IMAGE_CREATE_INFO,
.pNext = nullptr,
.flags = 0,
.imageType = VK_IMAGE_TYPE_2D,
.format = VK_FORMAT_D32_SFLOAT,
.extent = { swapchainExtent.width, swapchainExtent.height, 1 },
.mipLevels = 1,
.arrayLayers = 1,
.samples = VK_SAMPLE_COUNT_1_BIT,
.tiling = VK_IMAGE_TILING_OPTIMAL,
.usage = VK_IMAGE_USAGE_DEPTH_STENCIL_ATTACHMENT_BIT,
.sharingMode = VK_SHARING_MODE_EXCLUSIVE,
.queueFamilyIndexCount = 0,
.pQueueFamilyIndices = nullptr,
.initialLayout = VK_IMAGE_LAYOUT_UNDEFINED
};
VmaAllocationCreateInfo constexpr depthAllocationCreateInfo = {
.flags = VMA_ALLOCATION_CREATE_DEDICATED_MEMORY_BIT,
.usage = VMA_MEMORY_USAGE_GPU_ONLY,
.requiredFlags = 0,
.preferredFlags = 0,
.memoryTypeBits = 0,
.pool = nullptr,
.pUserData = nullptr,
.priority = 1.0f,
};
VK_CHECK( vmaCreateImage(
gpuAllocator, &depthImageCreateInfo, &depthAllocationCreateInfo, &depthImage, &depthAllocation, nullptr ) );
VkImageSubresourceRange constexpr subresourceRange = {
.aspectMask = VK_IMAGE_ASPECT_DEPTH_BIT,
.baseMipLevel = 0,
.levelCount = 1,
.baseArrayLayer = 0,
.layerCount = 1,
};
VkComponentMapping constexpr componentMapping = {
.r = VK_COMPONENT_SWIZZLE_IDENTITY,
.g = VK_COMPONENT_SWIZZLE_IDENTITY,
.b = VK_COMPONENT_SWIZZLE_IDENTITY,
.a = VK_COMPONENT_SWIZZLE_IDENTITY,
};
VkImageViewCreateInfo const imageViewCreateInfo = {
.sType = VK_STRUCTURE_TYPE_IMAGE_VIEW_CREATE_INFO,
.pNext = nullptr,
.flags = 0,
.image = depthImage,
.viewType = VK_IMAGE_VIEW_TYPE_2D,
.format = depthImageCreateInfo.format,
.components = componentMapping,
.subresourceRange = subresourceRange,
};
VK_CHECK( vkCreateImageView( device, &imageViewCreateInfo, nullptr, &depthView ) );
}
frame->commandPool = commandPool; frame->commandPool = commandPool;
frame->commandBuffer = commandBuffer; frame->commandBuffer = commandBuffer;
frame->imageAcquiredSemaphore = imageAcquiredSemaphore; frame->imageAcquiredSemaphore = imageAcquiredSemaphore;
frame->renderFinishedSemaphore = renderFinishedSemaphore; frame->renderFinishedSemaphore = renderFinishedSemaphore;
frame->frameReadyToReuse = frameReadyToReuse; frame->frameReadyToReuse = frameReadyToReuse;
frame->depthImage = depthImage;
frame->depthView = depthView;
frame->depthAllocation = depthAllocation;
} }

View File

@ -1,8 +1,7 @@
#pragma once #pragma once
#include <utility> #include <utility>
#include <volk.h>
#include "VulkanHeader.h"
struct RenderDevice; struct RenderDevice;
@ -14,10 +13,6 @@ struct Frame
VkSemaphore renderFinishedSemaphore; VkSemaphore renderFinishedSemaphore;
VkFence frameReadyToReuse; VkFence frameReadyToReuse;
VkImage depthImage;
VmaAllocation depthAllocation;
VkImageView depthView;
[[nodiscard]] bool isInit() const; [[nodiscard]] bool isInit() const;
Frame( Frame(
@ -25,10 +20,7 @@ struct Frame
VkCommandBuffer commandBuffer, VkCommandBuffer commandBuffer,
VkSemaphore imageAcquiredSemaphore, VkSemaphore imageAcquiredSemaphore,
VkSemaphore renderFinishedSemaphore, VkSemaphore renderFinishedSemaphore,
VkFence frameReadyToReuse, VkFence frameReadyToReuse );
VkImage depthImage,
VmaAllocation depthAllocation,
VkImageView depthView );
void destroy( RenderDevice const& renderDevice ); void destroy( RenderDevice const& renderDevice );
@ -40,9 +32,4 @@ struct Frame
~Frame(); ~Frame();
}; };
void Frame_Create( void Frame_Create( Frame* frame, VkDevice device, uint32_t directQueueFamilyIndex );
Frame* frame,
VkDevice device,
VmaAllocator gpuAllocator,
uint32_t directQueueFamilyIndex,
VkExtent2D swapchainExtent );

View File

@ -1,72 +0,0 @@
#include "FreeList.h"
FreeList::Iterator& FreeList::Iterator::operator++()
{
pIter = pIter->pNext;
return *this;
}
bool FreeList::Iterator::operator==( Iterator const& other ) const
{
return this->pIter == other.pIter;
}
FreeList::Node& FreeList::Iterator::operator*()
{
return *pIter;
}
FreeList::FreeList() : m_head{ .pNext = &m_tail, .pPrev = nullptr }, m_tail{ .pNext = nullptr, .pPrev = &m_head }
{}
void FreeList::pushBack( Node* pNode )
{
Node* prev = m_tail.pPrev;
// Set prev as previous of pNode
prev->pNext = pNode;
pNode->pPrev = prev;
// Set tail as next of pNode
pNode->pNext = &m_tail;
m_tail.pPrev = pNode;
}
void FreeList::pushFront( Node* pNode )
{
Node* next = m_head.pNext;
// Set next as next of pNode
next->pPrev = pNode;
pNode->pNext = next;
// Set head as prev of pNode
pNode->pPrev = &m_head;
m_head.pNext = pNode;
}
FreeList::Node* FreeList::popFront()
{
ASSERT( not empty() );
Node* element = m_head.pNext;
element->pPrev->pNext = element->pNext;
element->pNext->pPrev = element->pPrev;
return element;
}
bool FreeList::empty() const
{
return m_head.pNext == &m_tail;
}
FreeList::Iterator FreeList::begin()
{
return { m_head.pNext };
}
FreeList::Iterator FreeList::end()
{
return { &m_tail };
}

View File

@ -1,42 +0,0 @@
#pragma once
#include "MacroUtils.h"
struct FreeList
{
struct Node
{
Node* pNext;
Node* pPrev;
};
struct Iterator
{
Node* pIter;
Iterator& operator++();
bool operator==( Iterator const& other ) const;
Node& operator*();
};
private:
Node m_head;
Node m_tail;
public:
FreeList();
void pushBack( Node* pNode );
void pushFront( Node* pNode );
Node* popFront();
[[nodiscard]] bool empty() const;
Iterator begin();
Iterator end();
FreeList( FreeList&& ) = delete;
FreeList( FreeList const& ) = delete;
FreeList& operator=( FreeList const& ) = delete;
FreeList& operator=( FreeList&& ) = delete;
~FreeList() = default;
};

View File

@ -25,7 +25,7 @@ std::byte* GlobalMemory::allocate( size_t const size )
std::byte* retVal = memory; std::byte* retVal = memory;
memset( retVal, 0, size ); memset( retVal, 0, size );
memory += size; memory += size;
available -= size; available -= size;
SDL_LogInfo( SDL_LogInfo(
SDL_LOG_CATEGORY_SYSTEM, SDL_LOG_CATEGORY_SYSTEM,

View File

@ -2,8 +2,6 @@
#include <utility> #include <utility>
using byte = std::byte;
template <std::totally_ordered T> template <std::totally_ordered T>
T Clamp( T const val, T const minVal, T const maxVal ) T Clamp( T const val, T const minVal, T const maxVal )
{ {

View File

@ -43,7 +43,7 @@ bool MiscData::init( RenderDevice const& renderDevice )
.binding = 0, .binding = 0,
.descriptorType = VK_DESCRIPTOR_TYPE_UNIFORM_BUFFER, .descriptorType = VK_DESCRIPTOR_TYPE_UNIFORM_BUFFER,
.descriptorCount = 1, .descriptorCount = 1,
.stageFlags = VK_SHADER_STAGE_VERTEX_BIT | VK_SHADER_STAGE_FRAGMENT_BIT, .stageFlags = VK_SHADER_STAGE_VERTEX_BIT,
.pImmutableSamplers = nullptr, .pImmutableSamplers = nullptr,
}; };
@ -60,7 +60,7 @@ bool MiscData::init( RenderDevice const& renderDevice )
VkPushConstantRange const pushConstantRange = { VkPushConstantRange const pushConstantRange = {
.stageFlags = VK_SHADER_STAGE_ALL_GRAPHICS, .stageFlags = VK_SHADER_STAGE_ALL_GRAPHICS,
.offset = 0, .offset = 0,
.size = sizeof( DirectX::XMMATRIX ) + Material::GPU_DATA_SIZE, .size = sizeof( DirectX::XMMATRIX ) + sizeof( uint32_t ),
}; };
std::array const descriptorSetLayouts = { std::array const descriptorSetLayouts = {
@ -118,7 +118,7 @@ bool MiscData::init( RenderDevice const& renderDevice )
.location = 1, .location = 1,
.binding = 0, .binding = 0,
.format = VK_FORMAT_R32G32B32_SFLOAT, .format = VK_FORMAT_R32G32B32_SFLOAT,
.offset = offsetof( Vertex, normal ), .offset = offsetof( Vertex, color ),
}, },
VkVertexInputAttributeDescription{ VkVertexInputAttributeDescription{
.location = 2, .location = 2,
@ -126,18 +126,6 @@ bool MiscData::init( RenderDevice const& renderDevice )
.format = VK_FORMAT_R32G32_SFLOAT, .format = VK_FORMAT_R32G32_SFLOAT,
.offset = offsetof( Vertex, texCoord0 ), .offset = offsetof( Vertex, texCoord0 ),
}, },
VkVertexInputAttributeDescription{
.location = 3,
.binding = 0,
.format = VK_FORMAT_R32G32_SFLOAT,
.offset = offsetof( Vertex, texCoord1 ),
},
VkVertexInputAttributeDescription{
.location = 4,
.binding = 0,
.format = VK_FORMAT_R32G32B32A32_SFLOAT,
.offset = offsetof( Vertex, color0 ),
},
}; };
VkPipelineVertexInputStateCreateInfo const vertexInputState = { VkPipelineVertexInputStateCreateInfo const vertexInputState = {
@ -154,7 +142,7 @@ bool MiscData::init( RenderDevice const& renderDevice )
.sType = VK_STRUCTURE_TYPE_PIPELINE_INPUT_ASSEMBLY_STATE_CREATE_INFO, .sType = VK_STRUCTURE_TYPE_PIPELINE_INPUT_ASSEMBLY_STATE_CREATE_INFO,
.pNext = nullptr, .pNext = nullptr,
.flags = 0, .flags = 0,
.topology = VK_PRIMITIVE_TOPOLOGY_TRIANGLE_LIST, .topology = VK_PRIMITIVE_TOPOLOGY_TRIANGLE_STRIP,
.primitiveRestartEnable = VK_FALSE, .primitiveRestartEnable = VK_FALSE,
}; };
@ -207,9 +195,9 @@ bool MiscData::init( RenderDevice const& renderDevice )
.sType = VK_STRUCTURE_TYPE_PIPELINE_DEPTH_STENCIL_STATE_CREATE_INFO, .sType = VK_STRUCTURE_TYPE_PIPELINE_DEPTH_STENCIL_STATE_CREATE_INFO,
.pNext = nullptr, .pNext = nullptr,
.flags = 0, .flags = 0,
.depthTestEnable = VK_TRUE, .depthTestEnable = VK_FALSE,
.depthWriteEnable = VK_TRUE, .depthWriteEnable = VK_FALSE,
.depthCompareOp = VK_COMPARE_OP_LESS, .depthCompareOp = VK_COMPARE_OP_ALWAYS,
.depthBoundsTestEnable = VK_FALSE, .depthBoundsTestEnable = VK_FALSE,
.stencilTestEnable = VK_FALSE, .stencilTestEnable = VK_FALSE,
.front = {}, .front = {},
@ -255,7 +243,6 @@ bool MiscData::init( RenderDevice const& renderDevice )
.sType = VK_STRUCTURE_TYPE_PIPELINE_RENDERING_CREATE_INFO_KHR, .sType = VK_STRUCTURE_TYPE_PIPELINE_RENDERING_CREATE_INFO_KHR,
.colorAttachmentCount = 1, .colorAttachmentCount = 1,
.pColorAttachmentFormats = &renderDevice.swapchainFormat, .pColorAttachmentFormats = &renderDevice.swapchainFormat,
.depthAttachmentFormat = VK_FORMAT_D32_SFLOAT,
}; };
VkGraphicsPipelineCreateInfo const graphicsPipelineCreateInfo = { VkGraphicsPipelineCreateInfo const graphicsPipelineCreateInfo = {
@ -289,37 +276,14 @@ bool MiscData::init( RenderDevice const& renderDevice )
// Camera // Camera
{ {
cameraData.cameraPosition = DirectX::XMVectorSet( 0.0f, 20.0f, -20.0f, 1.0f ); cameraPosition = DirectX::XMVectorSet( 0.0f, 0.0f, -4.0f, 1.0f );
cameraTarget = DirectX::XMVectorSet( 0.0f, 0.0f, 0.0f, 1.0f ); cameraTarget = DirectX::XMVectorSet( 0.0f, 0.0f, 0.0f, 1.0f );
cameraUp = DirectX::XMVectorSet( 0.0f, 1.0f, 0.0f, 1.0f ); cameraUp = DirectX::XMVectorSet( 0.0f, 1.0f, 0.0f, 1.0f );
cameraData.viewMatrix = DirectX::XMMatrixLookAtLH( cameraData.cameraPosition, cameraTarget, cameraUp ); cameraData.viewMatrix = DirectX::XMMatrixLookAtLH( cameraPosition, cameraTarget, cameraUp );
cameraData.projectionMatrix = cameraData.projectionMatrix =
DirectX::XMMatrixPerspectiveFovLH( DirectX::XMConvertToRadians( 70.0f ), 16.0f / 9.0f, 0.1f, 1000.0f ); DirectX::XMMatrixPerspectiveFovLH( DirectX::XMConvertToRadians( 70.0f ), 16.0f / 9.0f, 0.1f, 1000.0f );
cameraUniformBufferSize = sizeof( CameraData ) + sizeof( LightData ); cameraUniformBufferSize = sizeof( CameraData );
}
// Lights
{
auto pointLightsValue = renderDevice.bufferManager->createStorageBuffer( 10 * sizeof( PointLight ) );
if ( !pointLightsValue ) return false;
pointLights = std::move( pointLightsValue.value() );
auto dirLightsValue = renderDevice.bufferManager->createStorageBuffer( 10 * sizeof( DirectionalLight ) );
if ( !dirLightsValue ) return false;
directionalLights = std::move( dirLightsValue.value() );
lightData.pointLights = renderDevice.bufferManager->fetchDeviceAddress( pointLights ).value();
lightData.directionalLights = renderDevice.bufferManager->fetchDeviceAddress( directionalLights ).value();
lightData.dirLightCount = 0;
lightData.pointLightCount = 0;
}
// Uniform Buffer
{
VkBufferCreateInfo const bufferCreateInfo = { VkBufferCreateInfo const bufferCreateInfo = {
.sType = VK_STRUCTURE_TYPE_BUFFER_CREATE_INFO, .sType = VK_STRUCTURE_TYPE_BUFFER_CREATE_INFO,
@ -355,9 +319,8 @@ bool MiscData::init( RenderDevice const& renderDevice )
if ( allocationInfo.pMappedData ) if ( allocationInfo.pMappedData )
{ {
memcpy( allocationInfo.pMappedData, &cameraData, sizeof cameraData );
cameraUniformBufferPtr = static_cast<uint8_t*>( allocationInfo.pMappedData ); cameraUniformBufferPtr = static_cast<uint8_t*>( allocationInfo.pMappedData );
memcpy( cameraUniformBufferPtr, &cameraData, sizeof cameraData );
memcpy( cameraUniformBufferPtr + sizeof cameraData, &lightData, sizeof lightData );
} }
} }
@ -397,7 +360,7 @@ bool MiscData::init( RenderDevice const& renderDevice )
VkDescriptorBufferInfo const descriptorBufferInfo = { VkDescriptorBufferInfo const descriptorBufferInfo = {
.buffer = cameraUniformBuffer, .buffer = cameraUniformBuffer,
.offset = 0, .offset = 0,
.range = cameraUniformBufferSize, .range = sizeof CameraData,
}; };
std::array writeDescriptorSets = { std::array writeDescriptorSets = {
@ -482,12 +445,6 @@ bool MiscData::init( RenderDevice const& renderDevice )
}; };
} }
// Frame Time
frameTimeEntryCount = 16;
memset( frameTime, 0, frameTimeEntryCount * sizeof( float ) );
frameTimeSum = 0;
frameTimeWriteHead = 0;
return true; return true;
} }
@ -498,9 +455,6 @@ void MiscData::destroy( RenderDevice const& renderDevice )
vkDestroyDescriptorPool( device, Take( descriptorPool ), nullptr ); vkDestroyDescriptorPool( device, Take( descriptorPool ), nullptr );
vmaDestroyBuffer( renderDevice.gpuAllocator, Take( cameraUniformBuffer ), Take( cameraUniformBufferAllocation ) ); vmaDestroyBuffer( renderDevice.gpuAllocator, Take( cameraUniformBuffer ), Take( cameraUniformBufferAllocation ) );
renderDevice.bufferManager->freeBuffer( std::move( pointLights ) );
renderDevice.bufferManager->freeBuffer( std::move( directionalLights ) );
vkDestroyPipeline( device, Take( meshPipeline ), nullptr ); vkDestroyPipeline( device, Take( meshPipeline ), nullptr );
vkDestroyPipelineLayout( device, Take( pipelineLayout ), nullptr ); vkDestroyPipelineLayout( device, Take( pipelineLayout ), nullptr );
vkDestroyDescriptorSetLayout( device, Take( descriptorSetLayout ), nullptr ); vkDestroyDescriptorSetLayout( device, Take( descriptorSetLayout ), nullptr );

View File

@ -1,13 +1,12 @@
#pragma once #pragma once
#include <array> #include <array>
#include <volk.h>
#include "VulkanHeader.h" #include <vma/vk_mem_alloc.h>
#include <DirectXMath.h> #include <DirectXMath.h>
#include "BufferManager.h"
struct GlobalMemory; struct GlobalMemory;
struct RenderDevice; struct RenderDevice;
@ -17,31 +16,6 @@ struct MiscData
{ {
DirectX::XMMATRIX viewMatrix; DirectX::XMMATRIX viewMatrix;
DirectX::XMMATRIX projectionMatrix; DirectX::XMMATRIX projectionMatrix;
DirectX::XMVECTOR cameraPosition;
};
struct PointLight
{
DirectX::XMFLOAT3 position;
float range;
DirectX::XMFLOAT3 color;
float attenuation;
};
struct DirectionalLight
{
DirectX::XMFLOAT3 direction;
float _padding0;
DirectX::XMFLOAT3 color;
float _padding1;
};
struct LightData
{
VkDeviceAddress pointLights;
VkDeviceAddress directionalLights;
uint32_t pointLightCount;
uint32_t dirLightCount;
}; };
uint64_t previousCounter; uint64_t previousCounter;
@ -50,14 +24,12 @@ struct MiscData
VkPipelineLayout pipelineLayout; VkPipelineLayout pipelineLayout;
VkPipeline meshPipeline; VkPipeline meshPipeline;
uint64_t _padding; // TODO: Optimize out?
DirectX::XMVECTOR cameraPosition;
DirectX::XMVECTOR cameraTarget; DirectX::XMVECTOR cameraTarget;
DirectX::XMVECTOR cameraUp; DirectX::XMVECTOR cameraUp;
CameraData cameraData; CameraData cameraData;
BufferID pointLights;
BufferID directionalLights;
LightData lightData;
VkBuffer cameraUniformBuffer; VkBuffer cameraUniformBuffer;
VmaAllocation cameraUniformBufferAllocation; VmaAllocation cameraUniformBufferAllocation;
size_t cameraUniformBufferSize; size_t cameraUniformBufferSize;
@ -70,11 +42,6 @@ struct MiscData
VkImageMemoryBarrier2 renderToPresentBarrier; VkImageMemoryBarrier2 renderToPresentBarrier;
VkDependencyInfo renderToPresentDependency; VkDependencyInfo renderToPresentDependency;
double frameTime[16];
double frameTimeSum;
uint8_t frameTimeWriteHead;
uint8_t frameTimeEntryCount;
bool init( RenderDevice const& renderDevice ); bool init( RenderDevice const& renderDevice );
void destroy( RenderDevice const& renderDevice ); void destroy( RenderDevice const& renderDevice );
}; };

View File

@ -1,755 +0,0 @@
#include "ModelLoader.h"
#include <algorithm>
#include <array>
#include <memory_resource>
#include <string_view>
#include <DirectXMath.h>
#include <SDL3/SDL_log.h>
#include <cgltf.h>
#include <stb_image.h>
#include "EntityManager.h"
#include "Frame.h"
#include "GlobalMemory.h"
#include "MacroUtils.h"
#include "MathUtil.h"
// TODO: Cache materials while loading.
uint32_t ProcessMaterial( RenderDevice* renderDevice, Model* model, cgltf_material const& material )
{
ASSERT( material.has_pbr_metallic_roughness );
DirectX::XMFLOAT4 const baseColorFactor = DirectX::XMFLOAT4{ material.pbr_metallic_roughness.base_color_factor };
VkSampler sampler = nullptr;
TextureID baseColorTexture;
if ( material.pbr_metallic_roughness.base_color_texture.texture )
{
cgltf_image* baseColorImage = material.pbr_metallic_roughness.base_color_texture.texture->image;
{
byte* data;
if ( baseColorImage->buffer_view->data )
{
data = static_cast<byte*>( baseColorImage->buffer_view->data );
}
else
{
data = static_cast<byte*>( baseColorImage->buffer_view->buffer->data ) + baseColorImage->buffer_view->offset;
}
size_t size = baseColorImage->buffer_view->size;
uint32_t width;
uint32_t height;
uint32_t numChannels = 4;
stbi_uc* textureData;
{
int w;
int h;
int nc;
int requestedChannels = static_cast<int>( numChannels );
textureData = stbi_load_from_memory(
reinterpret_cast<stbi_uc const*>( data ), static_cast<int>( size ), &w, &h, &nc, requestedChannels );
ASSERT( nc <= requestedChannels );
if ( not textureData )
{
return UINT32_MAX;
}
width = static_cast<uint32_t>( w );
height = static_cast<uint32_t>( h );
}
VkSamplerCreateInfo constexpr samplerCreateInfo = {
.sType = VK_STRUCTURE_TYPE_SAMPLER_CREATE_INFO,
.pNext = nullptr,
.flags = 0,
.magFilter = VK_FILTER_LINEAR,
.minFilter = VK_FILTER_LINEAR,
.mipmapMode = VK_SAMPLER_MIPMAP_MODE_LINEAR,
.addressModeU = VK_SAMPLER_ADDRESS_MODE_REPEAT,
.addressModeV = VK_SAMPLER_ADDRESS_MODE_REPEAT,
.addressModeW = VK_SAMPLER_ADDRESS_MODE_REPEAT,
.mipLodBias = 0.0,
.anisotropyEnable = true,
.maxAnisotropy = 1.0f,
.compareEnable = false,
.compareOp = VK_COMPARE_OP_NEVER,
.minLod = 0.0f,
.maxLod = VK_LOD_CLAMP_NONE,
.borderColor = VK_BORDER_COLOR_FLOAT_OPAQUE_BLACK,
.unnormalizedCoordinates = false,
};
VK_CHECK( vkCreateSampler( renderDevice->device, &samplerCreateInfo, nullptr, &sampler ) );
auto textureOpt = renderDevice->textureManager->createTexture( { width, height, 1 }, sampler );
if ( not textureOpt )
{
return UINT32_MAX;
}
baseColorTexture = std::move( textureOpt.value() );
VkImage textureImage = renderDevice->textureManager->fetchImage( baseColorTexture ).value();
// Staging Buffer Create
VkBuffer stagingBuffer;
VmaAllocation stagingAllocation;
{
VkBufferCreateInfo const stagingBufferCreateInfo = {
.sType = VK_STRUCTURE_TYPE_BUFFER_CREATE_INFO,
.pNext = nullptr,
.flags = 0,
.size = static_cast<VkDeviceSize>( width ) * height * numChannels * sizeof( textureData[0] ),
.usage = VK_BUFFER_USAGE_TRANSFER_SRC_BIT,
.sharingMode = VK_SHARING_MODE_EXCLUSIVE,
.queueFamilyIndexCount = 0,
.pQueueFamilyIndices = nullptr,
};
VmaAllocationCreateInfo constexpr stagingAllocationCreateInfo = {
.flags = VMA_ALLOCATION_CREATE_MAPPED_BIT | VMA_ALLOCATION_CREATE_HOST_ACCESS_SEQUENTIAL_WRITE_BIT,
.usage = VMA_MEMORY_USAGE_AUTO,
.requiredFlags = VK_MEMORY_PROPERTY_HOST_VISIBLE_BIT,
.preferredFlags = 0,
.memoryTypeBits = 0,
.pool = nullptr,
.pUserData = nullptr,
.priority = 1.0f,
};
VmaAllocationInfo allocationInfo;
VK_CHECK( vmaCreateBuffer(
renderDevice->gpuAllocator,
&stagingBufferCreateInfo,
&stagingAllocationCreateInfo,
&stagingBuffer,
&stagingAllocation,
&allocationInfo ) );
if ( allocationInfo.pMappedData )
{
memcpy( allocationInfo.pMappedData, textureData, stagingBufferCreateInfo.size );
}
}
// All data is copied to stagingBuffer, don't need this.
stbi_image_free( textureData );
// Staging -> Texture transfer
{
Frame& frameInUse = renderDevice->frames[renderDevice->frameIndex];
// This should just pass.
VK_CHECK( vkWaitForFences( renderDevice->device, 1, &frameInUse.frameReadyToReuse, VK_TRUE, INT64_MAX ) );
// Reset Frame
VK_CHECK( vkResetFences( renderDevice->device, 1, &frameInUse.frameReadyToReuse ) );
VK_CHECK( vkResetCommandPool( renderDevice->device, frameInUse.commandPool, 0 ) );
VkCommandBufferBeginInfo constexpr beginInfo = {
.sType = VK_STRUCTURE_TYPE_COMMAND_BUFFER_BEGIN_INFO,
.pNext = nullptr,
.flags = VK_COMMAND_BUFFER_USAGE_ONE_TIME_SUBMIT_BIT,
.pInheritanceInfo = nullptr,
};
uint32_t mipLevels = TextureManager::calculateRequiredMipLevels( width, height, 1 );
VkImageSubresourceRange const subresourceRange = {
.aspectMask = VK_IMAGE_ASPECT_COLOR_BIT,
.baseMipLevel = 0,
.levelCount = mipLevels,
.baseArrayLayer = 0,
.layerCount = 1,
};
VkImageMemoryBarrier2 const creationToTransferImageBarrier = {
.sType = VK_STRUCTURE_TYPE_IMAGE_MEMORY_BARRIER_2,
.pNext = nullptr,
.srcStageMask = VK_PIPELINE_STAGE_2_TOP_OF_PIPE_BIT,
.srcAccessMask = VK_ACCESS_2_NONE,
.dstStageMask = VK_PIPELINE_STAGE_2_COPY_BIT,
.dstAccessMask = VK_ACCESS_2_TRANSFER_WRITE_BIT,
.oldLayout = VK_IMAGE_LAYOUT_UNDEFINED,
.newLayout = VK_IMAGE_LAYOUT_TRANSFER_DST_OPTIMAL,
.srcQueueFamilyIndex = VK_QUEUE_FAMILY_IGNORED,
.dstQueueFamilyIndex = VK_QUEUE_FAMILY_IGNORED,
.image = renderDevice->textureManager->fetchImage( baseColorTexture ).value(),
.subresourceRange = subresourceRange,
};
VkDependencyInfo const creationToTransferDependency = {
.sType = VK_STRUCTURE_TYPE_DEPENDENCY_INFO,
.pNext = nullptr,
.dependencyFlags = 0,
.memoryBarrierCount = 0,
.pMemoryBarriers = nullptr,
.bufferMemoryBarrierCount = 0,
.pBufferMemoryBarriers = nullptr,
.imageMemoryBarrierCount = 1,
.pImageMemoryBarriers = &creationToTransferImageBarrier,
};
std::array transferToReadyImageBarriers{
// transferToReadyImageBarrier
VkImageMemoryBarrier2{
.sType = VK_STRUCTURE_TYPE_IMAGE_MEMORY_BARRIER_2,
.pNext = nullptr,
.srcStageMask = VK_PIPELINE_STAGE_2_TRANSFER_BIT,
.srcAccessMask = VK_ACCESS_2_TRANSFER_WRITE_BIT,
.dstStageMask = VK_PIPELINE_STAGE_2_FRAGMENT_SHADER_BIT,
.dstAccessMask = VK_ACCESS_2_SHADER_SAMPLED_READ_BIT,
.oldLayout = VK_IMAGE_LAYOUT_TRANSFER_SRC_OPTIMAL,
.newLayout = VK_IMAGE_LAYOUT_SHADER_READ_ONLY_OPTIMAL,
.srcQueueFamilyIndex = VK_QUEUE_FAMILY_IGNORED,
.dstQueueFamilyIndex = VK_QUEUE_FAMILY_IGNORED,
.image = textureImage,
.subresourceRange = {
.aspectMask = VK_IMAGE_ASPECT_COLOR_BIT,
.baseMipLevel = 0,
.levelCount = mipLevels-1,
.baseArrayLayer = 0,
.layerCount = 1,
},
},
VkImageMemoryBarrier2{
.sType = VK_STRUCTURE_TYPE_IMAGE_MEMORY_BARRIER_2,
.pNext = nullptr,
.srcStageMask = VK_PIPELINE_STAGE_2_TRANSFER_BIT,
.srcAccessMask = VK_ACCESS_2_TRANSFER_WRITE_BIT,
.dstStageMask = VK_PIPELINE_STAGE_2_FRAGMENT_SHADER_BIT,
.dstAccessMask = VK_ACCESS_2_SHADER_SAMPLED_READ_BIT,
.oldLayout = VK_IMAGE_LAYOUT_TRANSFER_DST_OPTIMAL,
.newLayout = VK_IMAGE_LAYOUT_SHADER_READ_ONLY_OPTIMAL,
.srcQueueFamilyIndex = VK_QUEUE_FAMILY_IGNORED,
.dstQueueFamilyIndex = VK_QUEUE_FAMILY_IGNORED,
.image = textureImage,
.subresourceRange = {
.aspectMask = VK_IMAGE_ASPECT_COLOR_BIT,
.baseMipLevel = mipLevels-1,
.levelCount = 1,
.baseArrayLayer = 0,
.layerCount = 1,
},
}
};
VkDependencyInfo const transferToReadyDependency = {
.sType = VK_STRUCTURE_TYPE_DEPENDENCY_INFO,
.pNext = nullptr,
.dependencyFlags = 0,
.memoryBarrierCount = 0,
.pMemoryBarriers = nullptr,
.bufferMemoryBarrierCount = 0,
.pBufferMemoryBarriers = nullptr,
.imageMemoryBarrierCount = static_cast<uint32_t>( transferToReadyImageBarriers.size() ),
.pImageMemoryBarriers = transferToReadyImageBarriers.data(),
};
VkImageSubresourceRange const mipLevelSubresource = {
.aspectMask = VK_IMAGE_ASPECT_COLOR_BIT,
.baseMipLevel = 0,
.levelCount = 1,
.baseArrayLayer = 0,
.layerCount = 1,
};
std::array prepareNextMipLevelBarriers{
// prepareNextMipLevelSrcImageBarrier
VkImageMemoryBarrier2{
.sType = VK_STRUCTURE_TYPE_IMAGE_MEMORY_BARRIER_2,
.pNext = nullptr,
.srcStageMask = VK_PIPELINE_STAGE_2_TRANSFER_BIT,
.srcAccessMask = VK_ACCESS_2_TRANSFER_WRITE_BIT,
.dstStageMask = VK_PIPELINE_STAGE_2_TRANSFER_BIT,
.dstAccessMask = VK_ACCESS_2_TRANSFER_READ_BIT,
.oldLayout = VK_IMAGE_LAYOUT_TRANSFER_DST_OPTIMAL,
.newLayout = VK_IMAGE_LAYOUT_TRANSFER_SRC_OPTIMAL,
.srcQueueFamilyIndex = VK_QUEUE_FAMILY_IGNORED,
.dstQueueFamilyIndex = VK_QUEUE_FAMILY_IGNORED,
.image = textureImage,
.subresourceRange = mipLevelSubresource,
},
// prepareNextMipLevelDstImageBarrier
VkImageMemoryBarrier2{
.sType = VK_STRUCTURE_TYPE_IMAGE_MEMORY_BARRIER_2,
.pNext = nullptr,
.srcStageMask = VK_PIPELINE_STAGE_2_COPY_BIT,
.srcAccessMask = VK_ACCESS_2_TRANSFER_WRITE_BIT,
.dstStageMask = VK_PIPELINE_STAGE_2_BLIT_BIT,
.dstAccessMask = VK_ACCESS_2_TRANSFER_WRITE_BIT,
.oldLayout = VK_IMAGE_LAYOUT_UNDEFINED,
.newLayout = VK_IMAGE_LAYOUT_UNDEFINED,
.srcQueueFamilyIndex = VK_QUEUE_FAMILY_IGNORED,
.dstQueueFamilyIndex = VK_QUEUE_FAMILY_IGNORED,
.image = textureImage,
.subresourceRange = mipLevelSubresource,
}
};
VkDependencyInfo const prepareNextMipLevelDependency = {
.sType = VK_STRUCTURE_TYPE_DEPENDENCY_INFO,
.pNext = nullptr,
.dependencyFlags = 0,
.memoryBarrierCount = 0,
.pMemoryBarriers = nullptr,
.bufferMemoryBarrierCount = 0,
.pBufferMemoryBarriers = nullptr,
.imageMemoryBarrierCount = static_cast<uint32_t>( prepareNextMipLevelBarriers.size() ),
.pImageMemoryBarriers = prepareNextMipLevelBarriers.data(),
};
vkBeginCommandBuffer( frameInUse.commandBuffer, &beginInfo );
{
VkImageSubresourceLayers imageSubresourceLayers = {
.aspectMask = VK_IMAGE_ASPECT_COLOR_BIT,
.mipLevel = 0,
.baseArrayLayer = 0,
.layerCount = 1,
};
// TODO: Ensure `bufferRowLength` and `bufferImageHeight` are not required.
VkBufferImageCopy copyRegion = {
.bufferOffset = 0,
.bufferRowLength = 0,
.bufferImageHeight = 0,
.imageSubresource = imageSubresourceLayers,
.imageOffset = { 0, 0, 0 },
.imageExtent = { width, height, 1 }
};
// Start
vkCmdPipelineBarrier2( frameInUse.commandBuffer, &creationToTransferDependency );
// Staging -> Image L0
vkCmdCopyBufferToImage(
frameInUse.commandBuffer,
stagingBuffer,
textureImage,
VK_IMAGE_LAYOUT_TRANSFER_DST_OPTIMAL,
1,
&copyRegion );
prepareNextMipLevelBarriers[0].subresourceRange.baseMipLevel = 0;
prepareNextMipLevelBarriers[1].subresourceRange.baseMipLevel = 1;
int32_t mipSrcWidth = static_cast<int32_t>( width );
int32_t mipSrcHeight = static_cast<int32_t>( height );
int32_t mipDstWidth = std::max( mipSrcWidth / 2, 1 );
int32_t mipDstHeight = std::max( mipSrcHeight / 2, 1 );
VkImageSubresourceLayers constexpr mipSubresourceLayers = {
.aspectMask = VK_IMAGE_ASPECT_COLOR_BIT,
.mipLevel = 0,
.baseArrayLayer = 0,
.layerCount = 1,
};
VkImageBlit2 imageBlit = {
.sType = VK_STRUCTURE_TYPE_IMAGE_BLIT_2,
.pNext = nullptr,
.srcSubresource = mipSubresourceLayers,
.srcOffsets = { { 0, 0, 0 }, { mipSrcWidth, mipSrcHeight, 1 } },
.dstSubresource = mipSubresourceLayers,
.dstOffsets = { { 0, 0, 0 }, { mipDstWidth, mipDstHeight, 1 } },
};
imageBlit.srcSubresource.mipLevel = 0;
imageBlit.dstSubresource.mipLevel = 1;
imageBlit.srcOffsets[1].x = mipSrcWidth;
imageBlit.srcOffsets[1].y = mipSrcHeight;
imageBlit.dstOffsets[1].x = mipDstWidth;
imageBlit.dstOffsets[1].y = mipDstHeight;
VkBlitImageInfo2 blitInfo = {
.sType = VK_STRUCTURE_TYPE_BLIT_IMAGE_INFO_2,
.pNext = nullptr,
.srcImage = textureImage,
.srcImageLayout = VK_IMAGE_LAYOUT_TRANSFER_SRC_OPTIMAL,
.dstImage = textureImage,
.dstImageLayout = VK_IMAGE_LAYOUT_TRANSFER_DST_OPTIMAL,
.regionCount = 1,
.pRegions = &imageBlit,
.filter = VK_FILTER_LINEAR,
};
// MipMapping
for ( uint32_t dstMipLevel = 1; dstMipLevel < mipLevels; ++dstMipLevel )
{
vkCmdPipelineBarrier2( frameInUse.commandBuffer, &prepareNextMipLevelDependency );
vkCmdBlitImage2( frameInUse.commandBuffer, &blitInfo );
// Prep for NEXT iteration
mipSrcWidth = mipDstWidth;
mipSrcHeight = mipDstHeight;
mipDstWidth = std::max( mipSrcWidth / 2, 1 );
mipDstHeight = std::max( mipSrcHeight / 2, 1 );
imageBlit.srcSubresource.mipLevel = dstMipLevel;
imageBlit.dstSubresource.mipLevel = dstMipLevel + 1;
imageBlit.srcOffsets[1].x = mipSrcWidth;
imageBlit.srcOffsets[1].y = mipSrcHeight;
imageBlit.dstOffsets[1].x = mipDstWidth;
imageBlit.dstOffsets[1].y = mipDstHeight;
// Prep current mip level as source
prepareNextMipLevelBarriers[0].subresourceRange.baseMipLevel = dstMipLevel;
prepareNextMipLevelBarriers[1].subresourceRange.baseMipLevel = dstMipLevel + 1;
}
// End
vkCmdPipelineBarrier2( frameInUse.commandBuffer, &transferToReadyDependency );
}
vkEndCommandBuffer( frameInUse.commandBuffer );
VkSubmitInfo submitInfo = {
.sType = VK_STRUCTURE_TYPE_SUBMIT_INFO,
.pNext = nullptr,
.waitSemaphoreCount = 0,
.pWaitSemaphores = nullptr,
.pWaitDstStageMask = nullptr,
.commandBufferCount = 1,
.pCommandBuffers = &frameInUse.commandBuffer,
.signalSemaphoreCount = 0,
.pSignalSemaphores = nullptr,
};
VK_CHECK( vkQueueSubmit( renderDevice->directQueue, 1, &submitInfo, frameInUse.frameReadyToReuse ) );
// Do not reset this. Else, the frame will never be available to the main loop.
VK_CHECK( vkWaitForFences( renderDevice->device, 1, &frameInUse.frameReadyToReuse, VK_TRUE, UINT64_MAX ) );
renderDevice->frameIndex = ( renderDevice->frameIndex + 1 ) % renderDevice->getNumFrames();
}
vmaDestroyBuffer( renderDevice->gpuAllocator, stagingBuffer, stagingAllocation );
}
}
float const metallic = material.pbr_metallic_roughness.metallic_factor;
float const roughness = material.pbr_metallic_roughness.roughness_factor;
uint32_t const materialIdx = static_cast<uint32_t>( model->materials.size() );
model->materials.push_back( { sampler, std::move( baseColorTexture ), {}, roughness, metallic, baseColorFactor } );
return materialIdx;
}
ModelMesh ProcessMesh(
RenderDevice* renderDevice,
Model* model,
std::pmr::vector<Vertex>* pVertices,
std::pmr::vector<uint32_t>* pIndices,
cgltf_mesh const& mesh )
{
using namespace std::string_view_literals;
uint32_t const primitiveStart = static_cast<uint32_t>( model->primitives.size() );
uint32_t const primitiveCount = static_cast<uint32_t>( mesh.primitives_count );
cgltf_primitive const* primitives = mesh.primitives;
for ( uint32_t primitiveIndex = 0; primitiveIndex < mesh.primitives_count; ++primitiveIndex )
{
// VertexStart is per-primitive
int32_t const vertexStart = static_cast<int32_t>( pVertices->size() );
cgltf_primitive const& primitive = primitives[primitiveIndex];
ASSERT( primitive.type == cgltf_primitive_type_triangles );
// Index Buffer
size_t const indexStart = pIndices->size();
size_t const indexCount = cgltf_accessor_unpack_indices( primitive.indices, nullptr, sizeof pIndices->at( 0 ), 0 );
ASSERT( indexCount > 0 );
pIndices->resize( indexStart + indexCount );
cgltf_accessor_unpack_indices(
primitive.indices, pIndices->data() + indexStart, sizeof pIndices->at( 0 ), indexCount );
// Material
uint32_t materialIdx = UINT32_MAX;
if ( primitive.material )
{
materialIdx = ProcessMaterial( renderDevice, model, *primitive.material );
}
model->primitives.push_back( Primitive{
.indexStart = static_cast<uint32_t>( indexStart ),
.indexCount = static_cast<uint32_t>( indexCount ),
.material = materialIdx,
.vertexOffset = vertexStart,
} );
cgltf_attribute const* attributes = primitive.attributes;
for ( uint32_t attribIndex = 0; attribIndex < primitive.attributes_count; ++attribIndex )
{
if ( "POSITION"sv == attributes[attribIndex].name )
{
cgltf_attribute const& positionAttr = attributes[attribIndex];
ASSERT( positionAttr.data->component_type == cgltf_component_type_r_32f );
ASSERT( positionAttr.data->type == cgltf_type_vec3 );
std::pmr::vector<DirectX::XMFLOAT3> positions{ pVertices->get_allocator() };
size_t const floatCount = cgltf_accessor_unpack_floats( positionAttr.data, nullptr, 0 );
positions.resize( floatCount / 3 );
cgltf_accessor_unpack_floats(
positionAttr.data, reinterpret_cast<cgltf_float*>( positions.data() ), floatCount );
// Guaranteed to have space for these vertices.
pVertices->resize( vertexStart + positions.size() );
auto vertexIter = pVertices->begin() + vertexStart;
for ( DirectX::XMFLOAT3 const& position : positions )
{
vertexIter->position = position;
++vertexIter;
}
}
if ( "NORMAL"sv == attributes[attribIndex].name )
{
cgltf_attribute const& normalAttr = attributes[attribIndex];
ASSERT( normalAttr.data->component_type == cgltf_component_type_r_32f );
ASSERT( normalAttr.data->type == cgltf_type_vec3 );
std::pmr::vector<DirectX::XMFLOAT3> normals{ pVertices->get_allocator() };
size_t const floatCount = cgltf_accessor_unpack_floats( normalAttr.data, nullptr, 0 );
normals.resize( floatCount / 3 );
cgltf_accessor_unpack_floats( normalAttr.data, reinterpret_cast<cgltf_float*>( normals.data() ), floatCount );
// Guaranteed to have space for these vertices.
pVertices->resize( vertexStart + normals.size() );
auto vertexIter = pVertices->begin() + vertexStart;
for ( DirectX::XMFLOAT3 const& normal : normals )
{
vertexIter->normal = normal;
++vertexIter;
}
}
if ( "TEXCOORD_0"sv == attributes[attribIndex].name )
{
cgltf_attribute const& texCoordAttr = attributes[attribIndex];
ASSERT( texCoordAttr.data->component_type == cgltf_component_type_r_32f );
ASSERT( texCoordAttr.data->type == cgltf_type_vec2 );
std::pmr::vector<DirectX::XMFLOAT2> texCoords{ pVertices->get_allocator() };
size_t const floatCount = cgltf_accessor_unpack_floats( texCoordAttr.data, nullptr, 0 );
texCoords.resize( floatCount / 2 );
cgltf_accessor_unpack_floats(
texCoordAttr.data, reinterpret_cast<cgltf_float*>( texCoords.data() ), floatCount );
// Guaranteed to have space for these vertices.
pVertices->resize( vertexStart + texCoords.size() );
auto vertexIter = pVertices->begin() + vertexStart;
for ( DirectX::XMFLOAT2 const& texCoord : texCoords )
{
vertexIter->texCoord0 = texCoord;
++vertexIter;
}
}
if ( "TEXCOORD_1"sv == attributes[attribIndex].name )
{
cgltf_attribute const& texCoordAttr = attributes[attribIndex];
ASSERT( texCoordAttr.data->component_type == cgltf_component_type_r_32f );
ASSERT( texCoordAttr.data->type == cgltf_type_vec2 );
std::pmr::vector<DirectX::XMFLOAT2> texCoords{ pVertices->get_allocator() };
size_t const floatCount = cgltf_accessor_unpack_floats( texCoordAttr.data, nullptr, 0 );
texCoords.resize( floatCount / 2 );
cgltf_accessor_unpack_floats(
texCoordAttr.data, reinterpret_cast<cgltf_float*>( texCoords.data() ), floatCount );
// Guaranteed to have space for these vertices.
pVertices->resize( vertexStart + texCoords.size() );
auto vertexIter = pVertices->begin() + vertexStart;
for ( DirectX::XMFLOAT2 const& texCoord : texCoords )
{
vertexIter->texCoord1 = texCoord;
++vertexIter;
}
}
if ( "COLOR_0"sv == attributes[attribIndex].name )
{
cgltf_attribute const& colorAttr = attributes[attribIndex];
ASSERT( colorAttr.data->component_type == cgltf_component_type_r_32f );
ASSERT( colorAttr.data->type == cgltf_type_vec3 or colorAttr.data->type == cgltf_type_vec4 );
if ( colorAttr.data->type == cgltf_type_vec3 )
{
std::pmr::vector<DirectX::XMFLOAT3> colors{ pVertices->get_allocator() };
size_t const floatCount = cgltf_accessor_unpack_floats( colorAttr.data, nullptr, 0 );
colors.resize( floatCount / 3 );
cgltf_accessor_unpack_floats( colorAttr.data, reinterpret_cast<cgltf_float*>( colors.data() ), floatCount );
// Guaranteed to have space for these vertices.
pVertices->resize( vertexStart + colors.size() );
auto vertexIter = pVertices->begin() + vertexStart;
for ( DirectX::XMFLOAT3 const& color : colors )
{
vertexIter->color0 = { color.x, color.y, color.z, 1.0f };
++vertexIter;
}
}
else // Since only two options
{
std::pmr::vector<DirectX::XMFLOAT4> colors{ pVertices->get_allocator() };
size_t const floatCount = cgltf_accessor_unpack_floats( colorAttr.data, nullptr, 0 );
colors.resize( floatCount / 4 );
cgltf_accessor_unpack_floats( colorAttr.data, reinterpret_cast<cgltf_float*>( colors.data() ), floatCount );
// Guaranteed to have space for these vertices.
pVertices->resize( vertexStart + colors.size() );
auto vertexIter = pVertices->begin() + vertexStart;
for ( DirectX::XMFLOAT4 const& color : colors )
{
vertexIter->color0 = color;
++vertexIter;
}
}
}
// TODO: Grab other attributes.
}
}
return { primitiveStart, primitiveCount };
}
Entity* ProcessNode(
RenderDevice* renderDevice,
EntityManager* entityManager,
Model* model,
std::pmr::vector<Vertex>* vertices,
std::pmr::vector<uint32_t>* indices,
cgltf_node const& node )
{
DirectX::XMVECTOR vTranslation;
DirectX::XMVECTOR qRotation;
DirectX::XMVECTOR vScale;
if ( node.has_matrix )
{
DirectX::XMMATRIX const mat = DirectX::XMMATRIX{ node.matrix };
ASSERT( DirectX::XMMatrixDecompose( &vScale, &qRotation, &vTranslation, mat ) );
}
else
{
vTranslation = node.has_translation
? DirectX::XMVectorSet( node.translation[0], node.translation[1], node.translation[2], 1.0f )
: DirectX::XMVectorZero();
qRotation = node.has_rotation
? DirectX::XMVectorSet( node.rotation[0], node.rotation[1], node.rotation[2], node.rotation[3] )
: DirectX::XMQuaternionIdentity();
vScale = node.has_scale ? DirectX::XMVectorSet( node.scale[0], node.scale[1], node.scale[2], 1.0f )
: DirectX::XMVectorSplatOne();
}
auto tx = Transform{
.translation = vTranslation,
.rotation = qRotation,
.scale = vScale,
};
Entity* entity = entityManager->createEntity( tx );
if ( node.mesh )
{
entity->modelMesh = ProcessMesh( renderDevice, model, vertices, indices, *node.mesh );
}
for ( uint32_t childIdx = 0; childIdx < node.children_count; ++childIdx )
{
entity->addChild( ProcessNode( renderDevice, entityManager, model, vertices, indices, *node.children[childIdx] ) );
}
return entity;
}
Entity* LoadModel( RenderDevice* renderDevice, EntityManager* entityManager, const char* filename )
{
cgltf_data* gltfModel = nullptr;
cgltf_options options = {};
cgltf_result result = cgltf_parse_file( &options, filename, &gltfModel );
if ( result != cgltf_result_success )
{
SDL_LogError( SDL_LOG_CATEGORY_APPLICATION, "%s failed to load", filename );
cgltf_free( gltfModel );
return nullptr;
}
result = cgltf_validate( gltfModel );
if ( result != cgltf_result_success )
{
SDL_LogError( SDL_LOG_CATEGORY_APPLICATION, "%s is invalid.", filename );
cgltf_free( gltfModel );
return nullptr;
}
result = cgltf_load_buffers( &options, gltfModel, filename );
if ( result != cgltf_result_success )
{
SDL_LogError( SDL_LOG_CATEGORY_APPLICATION, "%s buffers failed to load.", filename );
cgltf_free( gltfModel );
return nullptr;
}
Entity* entity = entityManager->createEntity( {
.translation = DirectX::XMVectorZero(),
.rotation = DirectX::XMQuaternionIdentity(),
.scale = DirectX::XMVectorSplatOne(),
} );
// Output data
std::pmr::vector<Vertex> vertices;
std::pmr::vector<uint32_t> indices;
cgltf_scene const* currentScene = gltfModel->scene;
for ( uint32_t nodeIdx = 0; nodeIdx < currentScene->nodes_count; ++nodeIdx )
{
entity->addChild( ProcessNode(
renderDevice, entityManager, &entity->model, &vertices, &indices, *currentScene->nodes[nodeIdx] ) );
}
auto vertexBuffer = renderDevice->bufferManager->createVertexBuffer( vertices.size() * sizeof vertices[0] );
if ( not vertexBuffer )
{
return nullptr;
}
entity->model.vertexBuffer = std::move( vertexBuffer.value() );
renderDevice->bufferManager->writeToBuffer( entity->model.vertexBuffer, vertices );
auto indexBuffer = renderDevice->bufferManager->createIndexBuffer( indices.size() * sizeof indices[0] );
if ( not indexBuffer )
{
return nullptr;
}
entity->model.indexBuffer = std::move( indexBuffer.value() );
renderDevice->bufferManager->writeToBuffer( entity->model.indexBuffer, std::span{ indices } );
cgltf_free( gltfModel );
return entity;
}

View File

@ -1,82 +0,0 @@
#pragma once
#include <DirectXMath.h>
#include <memory_resource>
#include <vector>
#include "BufferManager.h"
#include "TextureManager.h"
struct RenderDevice;
struct EntityManager;
struct Entity;
struct GlobalMemory;
struct Vertex
{
DirectX::XMFLOAT3 position = { 0.0f, 0.0f, 0.0f };
DirectX::XMFLOAT3 normal = { 1.0f, 1.0f, 1.0f };
DirectX::XMFLOAT2 texCoord0 = { 0.0f, 0.0f };
DirectX::XMFLOAT2 texCoord1 = { 0.0f, 0.0f };
DirectX::XMFLOAT4 color0 = { 1.0f, 1.0f, 1.0f, 1.0f };
};
struct Primitive
{
uint32_t indexStart;
uint32_t indexCount;
uint32_t material;
int32_t vertexOffset;
};
struct ModelMesh
{
uint32_t primitiveStart = 0;
uint32_t primitiveCount = 0;
[[nodiscard]] bool isNull() const
{
return primitiveCount == 0;
}
};
struct Material
{
constexpr static size_t GPU_DATA_OFFSET = sizeof( VkSampler );
constexpr static size_t GPU_DATA_SIZE =
sizeof( TextureID ) + sizeof( uint32_t ) + 2 * sizeof( float ) + sizeof( DirectX::XMFLOAT4 );
VkSampler sampler; // TODO: Reuse
// To copy directly.
TextureID texture;
uint32_t padding0; // FIXME: Wasting space.
float roughness = 1.0f;
float metallic = 1.0f;
DirectX::XMFLOAT4 baseColor = { 1.0f, 1.0f, 1.0f, 1.0f };
[[nodiscard]] bool isNull() const
{
return texture.isNull() or sampler;
}
};
static_assert( sizeof( Material ) == Material::GPU_DATA_OFFSET + Material::GPU_DATA_SIZE );
static constexpr Material DEFAULT_MATERIAL = {};
struct Model
{
std::pmr::monotonic_buffer_resource mem;
BufferID vertexBuffer;
BufferID indexBuffer;
std::pmr::vector<Material> materials;
std::pmr::vector<Primitive> primitives;
[[nodiscard]] bool isNull() const
{
return vertexBuffer.isNull();
}
};
Entity* LoadModel( RenderDevice* renderDevice, EntityManager* entityManager, const char* filename );

View File

View File

@ -1,55 +0,0 @@
#pragma once
#include <cstdint>
template <typename T>
struct RID
{
private:
uint32_t m_index = 0;
explicit RID( uint32_t const index ) : m_index{ index }
{}
public:
RID() = default;
// No copy
RID( RID const& ) = delete;
RID& operator=( RID const& ) = delete;
// Move allowed
RID( RID&& other ) noexcept;
RID& operator=( RID&& other ) noexcept;
[[nodiscard]] bool isNull() const
{
return m_index == 0;
}
static RID null()
{
return {};
}
operator bool() const
{
return m_index != 0;
}
};
template <typename T>
RID<T>::RID( RID&& other ) noexcept : m_index{ other.m_index }
{
other.m_index = 0;
}
template <typename T>
RID<T>& RID<T>::operator=( RID&& other ) noexcept
{
if ( this == &other ) return *this;
m_index = other.m_index;
other.m_index = 0;
return *this;
}

View File

@ -8,7 +8,6 @@
#include <optional> #include <optional>
#include <span> #include <span>
#include "BufferManager.h"
#include "Frame.h" #include "Frame.h"
#include "GlobalMemory.h" #include "GlobalMemory.h"
#include "MathUtil.h" #include "MathUtil.h"
@ -192,7 +191,6 @@ RenderDevice* RenderDevice_Create( GlobalMemory* mem, RenderDevice::CreateInfo c
.descriptorBindingPartiallyBound = true, .descriptorBindingPartiallyBound = true,
.descriptorBindingVariableDescriptorCount = true, .descriptorBindingVariableDescriptorCount = true,
.runtimeDescriptorArray = true, .runtimeDescriptorArray = true,
.bufferDeviceAddress = true,
}; };
VkPhysicalDeviceFeatures features = { VkPhysicalDeviceFeatures features = {
@ -219,7 +217,7 @@ RenderDevice* RenderDevice_Create( GlobalMemory* mem, RenderDevice::CreateInfo c
volkLoadDevice( device ); volkLoadDevice( device );
VmaAllocatorCreateInfo allocatorCreateInfo = { VmaAllocatorCreateInfo allocatorCreateInfo = {
.flags = VMA_ALLOCATOR_CREATE_BUFFER_DEVICE_ADDRESS_BIT, .flags = 0,
.physicalDevice = physicalDeviceInUse, .physicalDevice = physicalDeviceInUse,
.device = device, .device = device,
.preferredLargeHeapBlockSize = 0, .preferredLargeHeapBlockSize = 0,
@ -391,7 +389,7 @@ RenderDevice* RenderDevice_Create( GlobalMemory* mem, RenderDevice::CreateInfo c
Frame* frames = reinterpret_cast<Frame*>( mem->allocate( sizeof( Frame ) * swapchainImageCount ) ); Frame* frames = reinterpret_cast<Frame*>( mem->allocate( sizeof( Frame ) * swapchainImageCount ) );
for ( uint32_t i = 0; i != swapchainImageCount; ++i ) for ( uint32_t i = 0; i != swapchainImageCount; ++i )
{ {
Frame_Create( frames + i, device, gpuAllocator, directQueueFamilyIndex.value(), swapchainExtent ); Frame_Create( frames + i, device, directQueueFamilyIndex.value() );
} }
std::byte* allocation = mem->allocate( sizeof( RenderDevice ), alignof( RenderDevice ) ); std::byte* allocation = mem->allocate( sizeof( RenderDevice ), alignof( RenderDevice ) );
@ -426,18 +424,6 @@ RenderDevice* RenderDevice_Create( GlobalMemory* mem, RenderDevice::CreateInfo c
ASSERT( renderDevice->textureManager ); ASSERT( renderDevice->textureManager );
BufferManager* bufferManager = BufferManager_Create( mem, renderDevice, 10000 );
if ( !bufferManager )
{
SDL_LogError( SDL_LOG_CATEGORY_APPLICATION, "BufferManager failed to init" );
renderDevice->destroy();
return nullptr;
}
renderDevice->bufferManager = bufferManager;
ASSERT( renderDevice->bufferManager );
return renderDevice; return renderDevice;
} }
@ -450,7 +436,6 @@ void RenderDevice::destroy()
{ {
if ( not isInit() ) return; if ( not isInit() ) return;
Take( bufferManager )->destroy();
Take( textureManager )->destroy(); Take( textureManager )->destroy();
for ( Frame& frame : std::span{ Take( frames ), swapchainImageCount } ) for ( Frame& frame : std::span{ Take( frames ), swapchainImageCount } )

View File

@ -1,12 +1,14 @@
#pragma once #pragma once
#include <volk.h>
#define VMA_STATIC_VULKAN_FUNCTIONS 0
#define VMA_DYNAMIC_VULKAN_FUNCTIONS 0
#include <vma/vk_mem_alloc.h>
#include <SDL3/SDL_video.h> #include <SDL3/SDL_video.h>
#include <SDL3/SDL_vulkan.h> #include <SDL3/SDL_vulkan.h>
#include "VulkanHeader.h"
struct BufferManager;
struct GlobalMemory; struct GlobalMemory;
struct Frame; struct Frame;
struct TextureManager; struct TextureManager;
@ -43,7 +45,6 @@ struct RenderDevice
uint32_t frameIndex = 0; uint32_t frameIndex = 0;
TextureManager* textureManager; TextureManager* textureManager;
BufferManager* bufferManager;
[[nodiscard]] bool isInit() const; [[nodiscard]] bool isInit() const;
void destroy(); void destroy();

View File

@ -1,12 +1,9 @@
#include "TextureManager.h" #include "TextureManager.h"
#include "FreeList.h"
#include "GlobalMemory.h" #include "GlobalMemory.h"
#include "RenderDevice.h" #include "RenderDevice.h"
template struct RID<Texture>;
std::optional<TextureID> TextureManager::createTexture( VkExtent3D const extent, VkSampler sampler ) std::optional<TextureID> TextureManager::createTexture( VkExtent3D const extent, VkSampler sampler )
{ {
if ( m_freeList.empty() ) if ( m_freeList.empty() )
@ -15,7 +12,6 @@ std::optional<TextureID> TextureManager::createTexture( VkExtent3D const extent,
} }
Texture* textureSlot = reinterpret_cast<Texture*>( m_freeList.popFront() ); Texture* textureSlot = reinterpret_cast<Texture*>( m_freeList.popFront() );
++m_count;
ASSERT( m_pRenderDevice ); ASSERT( m_pRenderDevice );
RenderDevice const& renderDevice = *m_pRenderDevice; RenderDevice const& renderDevice = *m_pRenderDevice;
@ -230,7 +226,6 @@ void TextureManager::destroyTexture( Texture& tex )
ASSERT( tex.index > index and "Generation should increase." ); ASSERT( tex.index > index and "Generation should increase." );
m_freeList.pushBack( reinterpret_cast<FreeList::Node*>( &tex ) ); m_freeList.pushBack( reinterpret_cast<FreeList::Node*>( &tex ) );
--m_count;
} }
uint32_t TextureManager::calculateRequiredMipLevels( uint32_t const w, uint32_t const h, uint32_t const d ) uint32_t TextureManager::calculateRequiredMipLevels( uint32_t const w, uint32_t const h, uint32_t const d )

View File

@ -2,16 +2,162 @@
#include <optional> #include <optional>
#include <span> #include <span>
#include <volk.h>
#include <vma/vk_mem_alloc.h>
#include "FreeList.h"
#include "MacroUtils.h" #include "MacroUtils.h"
#include "RID.h"
#include "RenderDevice.h" #include "RenderDevice.h"
#include "VulkanHeader.h"
struct GlobalMemory; struct GlobalMemory;
struct RenderDevice; struct RenderDevice;
struct FreeListNode
{
FreeListNode* pNext;
FreeListNode* pPrev;
};
struct FreeList
{
using Node = FreeListNode;
struct Iterator
{
FreeListNode* pIter;
Iterator& operator++()
{
pIter = pIter->pNext;
return *this;
}
bool operator==( Iterator const& other ) const
{
return this->pIter == other.pIter;
}
FreeListNode& operator*()
{
return *pIter;
}
};
private:
FreeListNode m_head;
FreeListNode m_tail;
public:
FreeList() : m_head{ .pNext = &m_tail, .pPrev = nullptr }, m_tail{ .pNext = nullptr, .pPrev = &m_head }
{}
void pushBack( Node* pNode )
{
Node* prev = m_tail.pPrev;
// Set prev as previous of pNode
prev->pNext = pNode;
pNode->pPrev = prev;
// Set tail as next of pNode
pNode->pNext = &m_tail;
m_tail.pPrev = pNode;
}
void pushFront( Node* pNode )
{
Node* next = m_head.pNext;
// Set next as next of pNode
next->pPrev = pNode;
pNode->pNext = next;
// Set head as prev of pNode
pNode->pPrev = &m_head;
m_head.pNext = pNode;
}
Node* popFront()
{
ASSERT( not empty() );
Node* element = m_head.pNext;
element->pPrev->pNext = element->pNext;
element->pNext->pPrev = element->pPrev;
return element;
}
[[nodiscard]] bool empty() const
{
return m_head.pNext == &m_tail;
}
Iterator begin()
{
return { m_head.pNext };
}
Iterator end()
{
return { &m_tail };
}
FreeList( FreeList&& ) = delete;
FreeList( FreeList const& ) = delete;
FreeList& operator=( FreeList const& ) = delete;
FreeList& operator=( FreeList&& ) = delete;
~FreeList() = default;
};
template <typename T>
struct RID
{
private:
uint32_t m_index = 0;
explicit RID( uint32_t const index ) : m_index{ index }
{}
public:
RID() = default;
// No copy
RID( RID const& ) = delete;
RID& operator=( RID const& ) = delete;
// Move allowed
RID( RID&& other ) noexcept;
RID& operator=( RID&& other ) noexcept;
static RID null()
{
return {};
}
operator bool() const
{
return m_index == 0;
}
};
template <typename T>
RID<T>::RID( RID&& other ) noexcept : m_index{ other.m_index }
{
other.m_index = 0;
}
template <typename T>
RID<T>& RID<T>::operator=( RID&& other ) noexcept
{
if ( this == &other ) return *this;
m_index = other.m_index;
other.m_index = 0;
return *this;
}
struct Texture struct Texture
{ {
VkImage image; VkImage image;
@ -22,12 +168,10 @@ struct Texture
uint32_t index; uint32_t index;
}; };
static_assert( sizeof( Texture ) > sizeof( FreeList::Node ) and "Texture is used intrusively by FreeList" ); static_assert( sizeof( Texture ) > sizeof( FreeListNode ) and "Texture is used intrusively by FreeList" );
static_assert( static_assert(
offsetof( Texture, index ) >= sizeof( FreeList::Node ) and offsetof( Texture, index ) >= sizeof( FreeListNode ) and "Index should not be overwritten even in invalid state" );
"Index should not be overwritten even in invalid state" );
extern template struct RID<Texture>;
using TextureID = RID<Texture>; using TextureID = RID<Texture>;
struct TextureManager struct TextureManager

View File

@ -1,7 +0,0 @@
#pragma once
#include <volk.h>
#define VMA_STATIC_VULKAN_FUNCTIONS 0
#define VMA_DYNAMIC_VULKAN_FUNCTIONS 0
#include <vma/vk_mem_alloc.h>

View File

@ -17,10 +17,10 @@
- [X] Create a Triangle - [X] Create a Triangle
- [X] Create pipeline - [X] Create pipeline
- [X] Draw - [X] Draw
- [X] Create a Box - [ ] Create a Box
- [X] Create Vertex buffer - [ ] Create Vertex buffer
- [X] Load texture - [ ] Load texture
- [X] Draw - [ ] Draw
- [ ] Refactor - [ ] Refactor
## Features ## Features

View File

@ -4,7 +4,6 @@
"shader-slang", "shader-slang",
"vulkan-memory-allocator", "vulkan-memory-allocator",
"directxmath", "directxmath",
"cgltf",
"stb", "stb",
{ {
"name": "sdl3", "name": "sdl3",