diff --git a/Assets/Shaders/Mesh.slang b/Assets/Shaders/Mesh.slang index 7f8a565..427147b 100644 --- a/Assets/Shaders/Mesh.slang +++ b/Assets/Shaders/Mesh.slang @@ -2,8 +2,8 @@ import Bindless; struct VertexOut { float4 outPosition : SV_Position; - float4 screenPosition : ScreenPosition; - float4 normal : CoarseNormal; + float4 worldPosition : WorldPosition; + float4 normal : WorldNormal; float2 texCoord0 : TexCoord0; float2 texCoord1 : TexCoord1; float4 vertexColor0 : VertexColor; @@ -12,9 +12,46 @@ struct VertexOut { struct CameraData { float4x4 view; float4x4 proj; + float4 position; }; -uniform ParameterBlock camera; +struct PointLight { + float3 position; + float range; + float3 color; + float attenuation; +}; + +struct DirectionalLight { + 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 pfd; struct PerInstanceData { float4x4 transform; @@ -37,9 +74,11 @@ VertexOut VertexMain( float2 texCoord1, float4 vertexColor0, ) { + float4 worldPosition = mul(pcb.transform, float4(position, 1.0f)); + VertexOut output; - output.outPosition = mul(camera.proj, mul(camera.view, mul(pcb.transform, float4(position, 1.0f)))); - output.screenPosition = mul(camera.proj, mul(camera.view, mul(pcb.transform, float4(position, 1.0f)))); + output.outPosition = mul(pfd.camera.proj, mul(pfd.camera.view, worldPosition)); + output.worldPosition = worldPosition; output.normal = mul(pcb.transform, float4(normalize(normal.rgb), 0.0f)); output.texCoord0 = texCoord0; output.texCoord1 = texCoord1; @@ -49,20 +88,40 @@ VertexOut VertexMain( [shader("fragment")] float4 FragmentMain( - float4 interpolatePosition : ScreenPosition, - float4 interpolatedNormal : CoarseNormal, - float2 uv0 : TexCoord0, - float2 uv1 : TexCoord1, - float4 interpolatedColor : VertexColor, + float4 worldPosition : WorldPosition, + float4 normal : WorldNormal, + float2 uv0 : TexCoord0, + float2 uv1 : TexCoord1, + float4 color : VertexColor, ) : SV_Target0 { - let N = interpolatedNormal.xyz; - let L = dot(normalize(N), float3(1.0f, 1.0f, -1.0f)); + 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) { - return float4(texture.Sample(uv0).rgb, 1.0f) * pcb.baseColor * interpolatedColor * L; + return float4(texture.Sample(uv0).rgb, 1.0f) * pcb.baseColor * color * float4((diffuse + specular), 0.0f); } else { - return pcb.baseColor * interpolatedColor * L; + return pcb.baseColor * color * float4((diffuse + specular), 0.0f); } } diff --git a/Blaze.vcxproj b/Blaze.vcxproj index b786585..cb894ad 100644 --- a/Blaze.vcxproj +++ b/Blaze.vcxproj @@ -119,7 +119,7 @@ Console true - C:\Users\Eon\source\repos\Blaze\vcpkg_installed\x64-windows\x64-windows\bin;%(AdditionalLibraryDirectories) + %(AdditionalLibraryDirectories) @@ -144,7 +144,7 @@ true true true - C:\Users\Eon\source\repos\Blaze\vcpkg_installed\x64-windows\x64-windows\bin;%(AdditionalLibraryDirectories) + %(AdditionalLibraryDirectories) diff --git a/Blaze/Blaze.cpp b/Blaze/Blaze.cpp index 85a43cc..6797ff0 100644 --- a/Blaze/Blaze.cpp +++ b/Blaze/Blaze.cpp @@ -50,6 +50,37 @@ SDL_AppResult SDL_AppInit( void** appstate, int, char** ) LoadModel( appState.renderDevice, appState.entityManager, "Assets/Models/OrientationTest.glb" ); ASSERT( entity ); + std::array pointLight = { + MiscData::PointLight{ + .position = { 12.0f, 0.0f, 0.0f }, + .range = 12, + .color = { 1.0f, 0.0f, 0.0f }, + .attenuation = 1.0f, + }, + MiscData::PointLight{ + .position = { 0.0f, 12.0f, 0.0f }, + .range = 12, + .color = { 0.0f, 1.0f, 0.0f }, + .attenuation = 1.0f, + }, + MiscData::PointLight{ + .position = { 0.0f, 0.0f, -12.0f }, + .range = 6, + .color = { 0.0f, 0.0f, 1.0f }, + .attenuation = 1.0f, + }, + }; + + appState.miscData->lightData.pointLightCount = static_cast( pointLight.size() ); + + appState.renderDevice->bufferManager->writeToBuffer( + appState.miscData->pointLights, std::span{ pointLight.begin(), pointLight.end() } ); + + memcpy( + appState.miscData->cameraUniformBufferPtr + sizeof( MiscData::CameraData ), + &appState.miscData->lightData, + sizeof appState.miscData->lightData ); + return SDL_APP_CONTINUE; } @@ -260,7 +291,6 @@ SDL_AppResult SDL_AppIterate( void* appstate ) materialData ); vkCmdDrawIndexed( cmd, primitive.indexCount, 1, primitive.indexStart, primitive.vertexOffset, 0 ); - // vkCmdDrawIndexed( cmd, primitive.count, 1, primitive.start, 0 ); } } diff --git a/Blaze/BufferManager.cpp b/Blaze/BufferManager.cpp index 1bb429e..1bfaafa 100644 --- a/Blaze/BufferManager.cpp +++ b/Blaze/BufferManager.cpp @@ -46,7 +46,7 @@ void BufferManager::writeToBufferImpl( BufferID const& rid, void const* data, si Buffer const& buffer = fetchBufferUnchecked( rid ); - ASSERT( buffer.size <= size ); + ASSERT( size <= buffer.size ); memcpy( buffer.mappedData, data, size ); } @@ -112,11 +112,12 @@ std::optional BufferManager::createVertexBuffer( size_t const size ) // NOTE: textureSlot preserves index between uses. uint32_t index = bufferSlot->index; new ( bufferSlot ) Buffer{ - .buffer = vertexBuffer, - .allocation = vertexBufferAllocation, - .mappedData = static_cast( allocationInfo.pMappedData ), - .size = size, - .index = index, + .buffer = vertexBuffer, + .allocation = vertexBufferAllocation, + .mappedData = static_cast( allocationInfo.pMappedData ), + .deviceAddress = 0, + .size = size, + .index = index, }; // NOTE: Memory hackery to create TextureID; @@ -174,11 +175,83 @@ std::optional BufferManager::createIndexBuffer( size_t size ) // NOTE: bufferSlot preserves index between uses. uint32_t index = bufferSlot->index; new ( bufferSlot ) Buffer{ - .buffer = indexBuffer, - .allocation = indexBufferAllocation, - .mappedData = static_cast( allocationInfo.pMappedData ), - .size = size, - .index = index, + .buffer = indexBuffer, + .allocation = indexBufferAllocation, + .mappedData = static_cast( allocationInfo.pMappedData ), + .deviceAddress = 0, + .size = size, + .index = index, + }; + + // NOTE: Memory hackery to create BufferID; + return std::move( *reinterpret_cast( &index ) ); +} + +std::optional BufferManager::createStorageBuffer( size_t size ) +{ + if ( m_freeList.empty() ) + { + return std::nullopt; + } + + Buffer* bufferSlot = reinterpret_cast( 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( allocationInfo.pMappedData ), + .deviceAddress = deviceAddress, + .size = size, + .index = index, }; // NOTE: Memory hackery to create BufferID; @@ -203,6 +276,16 @@ std::optional BufferManager::fetchBuffer( BufferID const& rid ) return fetchBufferUnchecked( rid ).buffer; } +std::optional 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 } { diff --git a/Blaze/BufferManager.h b/Blaze/BufferManager.h index c861ca5..e85accf 100644 --- a/Blaze/BufferManager.h +++ b/Blaze/BufferManager.h @@ -14,11 +14,12 @@ struct RenderDevice; struct Buffer { - VkBuffer buffer; - VmaAllocation allocation; - std::byte* mappedData; // Assume the system has ReBAR/SAM enabled. - size_t size; - uint32_t index; + 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" ); @@ -55,18 +56,21 @@ public: [[nodiscard]] bool isValidID( BufferID const& rid ) const; std::optional createVertexBuffer( size_t size ); - std::optional createIndexBuffer( size_t size ); + std::optional createStorageBuffer( size_t size ); void freeBuffer( BufferID&& rid ); DEPRECATE_JULY_2025 - std::optional fetchBuffer( BufferID const& rid ); + std::optional fetchBuffer( BufferID const& rid ); + std::optional fetchDeviceAddress( BufferID const& rid ); - template - void writeToBuffer( BufferID const& rid, std::span const& data ) + void writeToBuffer( BufferID const& rid, std::ranges::contiguous_range auto const& data ) { - writeToBufferImpl( rid, data.data(), data.size_bytes() ); + writeToBufferImpl( + rid, + std::ranges::data( data ), + std::ranges::size( data ) * sizeof( std::ranges::range_value_t ) ); } // diff --git a/Blaze/MiscData.cpp b/Blaze/MiscData.cpp index 6d1a787..b6066d4 100644 --- a/Blaze/MiscData.cpp +++ b/Blaze/MiscData.cpp @@ -43,7 +43,7 @@ bool MiscData::init( RenderDevice const& renderDevice ) .binding = 0, .descriptorType = VK_DESCRIPTOR_TYPE_UNIFORM_BUFFER, .descriptorCount = 1, - .stageFlags = VK_SHADER_STAGE_VERTEX_BIT, + .stageFlags = VK_SHADER_STAGE_VERTEX_BIT | VK_SHADER_STAGE_FRAGMENT_BIT, .pImmutableSamplers = nullptr, }; @@ -60,7 +60,7 @@ bool MiscData::init( RenderDevice const& renderDevice ) VkPushConstantRange const pushConstantRange = { .stageFlags = VK_SHADER_STAGE_ALL_GRAPHICS, .offset = 0, - .size = sizeof( DirectX::XMMATRIX ) + Material::GPU_DATA_SIZE + .size = sizeof( DirectX::XMMATRIX ) + Material::GPU_DATA_SIZE, }; std::array const descriptorSetLayouts = { @@ -289,14 +289,37 @@ bool MiscData::init( RenderDevice const& renderDevice ) // Camera { - cameraPosition = DirectX::XMVectorSet( 0.0f, 20.0f, -20.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 ); - cameraData.viewMatrix = DirectX::XMMatrixLookAtLH( cameraPosition, cameraTarget, cameraUp ); + cameraData.cameraPosition = DirectX::XMVectorSet( 0.0f, 20.0f, -20.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 ); + cameraData.viewMatrix = DirectX::XMMatrixLookAtLH( cameraData.cameraPosition, cameraTarget, cameraUp ); cameraData.projectionMatrix = DirectX::XMMatrixPerspectiveFovLH( DirectX::XMConvertToRadians( 70.0f ), 16.0f / 9.0f, 0.1f, 1000.0f ); - cameraUniformBufferSize = sizeof( CameraData ); + cameraUniformBufferSize = sizeof( CameraData ) + sizeof( LightData ); + } + + + // 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 = { .sType = VK_STRUCTURE_TYPE_BUFFER_CREATE_INFO, @@ -332,8 +355,9 @@ bool MiscData::init( RenderDevice const& renderDevice ) if ( allocationInfo.pMappedData ) { - memcpy( allocationInfo.pMappedData, &cameraData, sizeof cameraData ); cameraUniformBufferPtr = static_cast( allocationInfo.pMappedData ); + memcpy( cameraUniformBufferPtr, &cameraData, sizeof cameraData ); + memcpy( cameraUniformBufferPtr + sizeof cameraData, &lightData, sizeof lightData ); } } @@ -373,7 +397,7 @@ bool MiscData::init( RenderDevice const& renderDevice ) VkDescriptorBufferInfo const descriptorBufferInfo = { .buffer = cameraUniformBuffer, .offset = 0, - .range = sizeof CameraData, + .range = cameraUniformBufferSize, }; std::array writeDescriptorSets = { @@ -474,6 +498,9 @@ void MiscData::destroy( RenderDevice const& renderDevice ) vkDestroyDescriptorPool( device, Take( descriptorPool ), nullptr ); vmaDestroyBuffer( renderDevice.gpuAllocator, Take( cameraUniformBuffer ), Take( cameraUniformBufferAllocation ) ); + renderDevice.bufferManager->freeBuffer( std::move( pointLights ) ); + renderDevice.bufferManager->freeBuffer( std::move( directionalLights ) ); + vkDestroyPipeline( device, Take( meshPipeline ), nullptr ); vkDestroyPipelineLayout( device, Take( pipelineLayout ), nullptr ); vkDestroyDescriptorSetLayout( device, Take( descriptorSetLayout ), nullptr ); diff --git a/Blaze/MiscData.h b/Blaze/MiscData.h index 4bfe827..4eed027 100644 --- a/Blaze/MiscData.h +++ b/Blaze/MiscData.h @@ -6,6 +6,8 @@ #include +#include "BufferManager.h" + struct GlobalMemory; struct RenderDevice; @@ -15,6 +17,31 @@ struct MiscData { DirectX::XMMATRIX viewMatrix; 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; @@ -23,10 +50,14 @@ struct MiscData VkPipelineLayout pipelineLayout; VkPipeline meshPipeline; - DirectX::XMVECTOR cameraPosition; DirectX::XMVECTOR cameraTarget; DirectX::XMVECTOR cameraUp; CameraData cameraData; + + BufferID pointLights; + BufferID directionalLights; + LightData lightData; + VkBuffer cameraUniformBuffer; VmaAllocation cameraUniformBufferAllocation; size_t cameraUniformBufferSize; diff --git a/Blaze/ModelLoader.cpp b/Blaze/ModelLoader.cpp index 4907468..044c1a5 100644 --- a/Blaze/ModelLoader.cpp +++ b/Blaze/ModelLoader.cpp @@ -31,7 +31,6 @@ uint32_t ProcessMaterial( RenderDevice* renderDevice, Model* model, cgltf_materi cgltf_image* baseColorImage = material.pbr_metallic_roughness.base_color_texture.texture->image; { - byte* data; if ( baseColorImage->buffer_view->data ) { @@ -740,7 +739,7 @@ Entity* LoadModel( RenderDevice* renderDevice, EntityManager* entityManager, con } entity->model.vertexBuffer = std::move( vertexBuffer.value() ); - renderDevice->bufferManager->writeToBuffer( entity->model.vertexBuffer, std::span{ vertices } ); + renderDevice->bufferManager->writeToBuffer( entity->model.vertexBuffer, vertices ); auto indexBuffer = renderDevice->bufferManager->createIndexBuffer( indices.size() * sizeof indices[0] ); if ( not indexBuffer ) diff --git a/Blaze/RenderDevice.cpp b/Blaze/RenderDevice.cpp index 5a4e029..1ee8f81 100644 --- a/Blaze/RenderDevice.cpp +++ b/Blaze/RenderDevice.cpp @@ -192,6 +192,7 @@ RenderDevice* RenderDevice_Create( GlobalMemory* mem, RenderDevice::CreateInfo c .descriptorBindingPartiallyBound = true, .descriptorBindingVariableDescriptorCount = true, .runtimeDescriptorArray = true, + .bufferDeviceAddress = true, }; VkPhysicalDeviceFeatures features = { @@ -218,7 +219,7 @@ RenderDevice* RenderDevice_Create( GlobalMemory* mem, RenderDevice::CreateInfo c volkLoadDevice( device ); VmaAllocatorCreateInfo allocatorCreateInfo = { - .flags = 0, + .flags = VMA_ALLOCATOR_CREATE_BUFFER_DEVICE_ADDRESS_BIT, .physicalDevice = physicalDeviceInUse, .device = device, .preferredLargeHeapBlockSize = 0,