Blaze/Blaze/Source/Blaze.cpp

392 lines
14 KiB
C++

// Blaze.cpp : This file contains the 'main' function. Program execution begins and ends there.
//
#include <array>
#include <cassert>
#include <functional>
#include <limits>
#include <span>
#define SDL_MAIN_USE_CALLBACKS 1
#include <SDL3/SDL.h>
#include <SDL3/SDL_main.h>
#include <SDL3/SDL_vulkan.h>
#include "VulkanHeader.h"
#include "AppState.h"
#include "EntityManager.h"
#include "Frame.h"
#include "GlobalMemory.h"
#include "MacroUtils.h"
#include "MiscData.h"
#include "RenderDevice.h"
#include "ModelLoader.h"
constexpr uint32_t WIDTH = 1280;
constexpr uint32_t HEIGHT = 720;
constexpr uint32_t NUM_FRAMES = 3;
namespace Blaze::Global
{
GlobalMemory g_Memory;
}
// ReSharper disable once CppInconsistentNaming
SDL_AppResult SDL_AppInit( void** appstate, int, char** )
{
SDL_Init( SDL_INIT_VIDEO | SDL_INIT_EVENTS );
using Blaze::operator""_MiB;
Blaze::Global::g_Memory.Initialize( 128_MiB );
*appstate = Blaze::AppState::Create( &Blaze::Global::g_Memory, WIDTH, HEIGHT );
if ( !*appstate ) return SDL_APP_FAILURE;
Blaze::AppState const& app_state = *static_cast<Blaze::AppState*>( *appstate );
Blaze::Entity const* entity =
LoadModel( app_state.renderDevice, app_state.entityManager, "Assets/Models/DamagedHelmet.glb" );
ASSERT( entity );
Blaze::MiscData::PointLight point_light[] = {
{
.position = { 12.0f, 0.0f, 0.0f },
.range = 12,
.color = { 1.0f, 0.0f, 0.0f },
.attenuation = 1.0f,
},
{
.position = { 0.0f, 3.0f, 0.0f },
.range = 12,
.color = { 12.0f, 12.0f, 12.0f },
.attenuation = 1.0f,
},
{
.position = { 0.0f, 0.0f, -12.0f },
.range = 6,
.color = { 0.0f, 0.0f, 1.0f },
.attenuation = 1.0f,
},
};
app_state.miscData->lightData.pointLightCount = _countof( point_light );
app_state.renderDevice->bufferManager->WriteToBuffer( app_state.miscData->pointLights, point_light );
Blaze::MiscData::DirectionalLight dir_light[] = {
{
.direction = { 1.0f, -1.0f, 0.0f },
.color = { 12.0f, 10.0f, 5.0f },
},
};
app_state.miscData->lightData.dirLightCount = _countof( dir_light );
app_state.renderDevice->bufferManager->WriteToBuffer( app_state.miscData->directionalLights, dir_light );
memcpy(
app_state.miscData->cameraUniformBufferPtr + sizeof( Blaze::MiscData::CameraData ),
&app_state.miscData->lightData,
sizeof app_state.miscData->lightData );
return SDL_APP_CONTINUE;
}
// ReSharper disable once CppInconsistentNaming
SDL_AppResult SDL_AppIterate( void* appstate )
{
Blaze::AppState& app_state = *static_cast<Blaze::AppState*>( appstate );
Blaze::RenderDevice& render_device = *app_state.renderDevice;
Blaze::EntityManager& entity_manager = *app_state.entityManager;
Blaze::MiscData& misc = *app_state.miscData;
Blaze::Frame& current_frame = render_device.frames[render_device.frameIndex];
VK_CHECK( vkWaitForFences( render_device.device, 1, &current_frame.frameReadyToReuse, VK_TRUE, UINT32_MAX ) );
// All resources of frame 'frameIndex' are free.
// time calc
uint64_t const previous_counter = misc.previousCounter;
uint64_t const current_counter = SDL_GetPerformanceCounter();
uint64_t const delta_count = current_counter - previous_counter;
uint64_t const perf_freq = SDL_GetPerformanceFrequency();
double const delta_time = static_cast<double>( delta_count ) / static_cast<double>( perf_freq );
misc.previousCounter = current_counter;
{
misc.frameTimeSum -= misc.frameTime[misc.frameTimeWriteHead];
misc.frameTime[misc.frameTimeWriteHead] = delta_time;
misc.frameTimeSum += delta_time;
misc.frameTimeWriteHead = ( misc.frameTimeWriteHead + 1 ) % misc.frameTimeEntryCount;
double avg_delta_time = ( misc.frameTimeSum / misc.frameTimeEntryCount );
double fps = 1.0 / avg_delta_time;
double avg_delta_time_ms = 1000.0 * avg_delta_time;
( void )sprintf_s<256>( app_state.sprintfBuffer, "%.2f fps %.2f ms", fps, avg_delta_time_ms );
SDL_SetWindowTitle( app_state.window, app_state.sprintfBuffer );
}
for ( Blaze::Entity& entity : entity_manager.Iterate() )
{
if ( not entity.IsRoot() ) continue;
entity.transform.rotation = DirectX::XMQuaternionMultiply(
DirectX::XMQuaternionRotationAxis(
DirectX::XMVectorSet( 0.0f, 1.0f, 0.0f, 0.0f ),
DirectX::XMConvertToRadians( 60.0f ) * static_cast<float>( delta_time ) ),
entity.transform.rotation );
}
uint32_t current_image_index;
VK_CHECK( vkAcquireNextImageKHR(
render_device.device,
render_device.swapchain,
std::numeric_limits<uint32_t>::max(),
current_frame.imageAcquiredSemaphore,
nullptr,
&current_image_index ) );
// TODO: Resize Swapchain if required.
VK_CHECK( vkResetFences( render_device.device, 1, &current_frame.frameReadyToReuse ) );
VK_CHECK( vkResetCommandPool( render_device.device, current_frame.commandPool, 0 ) );
misc.acquireToRenderBarrier.image = render_device.swapchainImages[current_image_index];
misc.renderToPresentBarrier.image = render_device.swapchainImages[current_image_index];
VkCommandBuffer cmd = current_frame.commandBuffer;
VkCommandBufferBeginInfo constexpr begin_info = {
.sType = VK_STRUCTURE_TYPE_COMMAND_BUFFER_BEGIN_INFO,
.pNext = nullptr,
.flags = 0,
.pInheritanceInfo = nullptr,
};
VkClearColorValue constexpr static kBlackClear = {
.float32 = { 0.0f, 0.0f, 0.0f, 1.0f },
};
VkClearDepthStencilValue constexpr static kDepthStencilClear = {
.depth = 1.0f,
.stencil = 0,
};
VK_CHECK( vkBeginCommandBuffer( cmd, &begin_info ) );
{
VkRenderingAttachmentInfo const depth_attachment_info = {
.sType = VK_STRUCTURE_TYPE_RENDERING_ATTACHMENT_INFO,
.pNext = nullptr,
.imageView = current_frame.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 = kDepthStencilClear },
};
VkRenderingAttachmentInfo const attachment_info = {
.sType = VK_STRUCTURE_TYPE_RENDERING_ATTACHMENT_INFO,
.pNext = nullptr,
.imageView = render_device.swapchainViews[current_image_index],
.imageLayout = VK_IMAGE_LAYOUT_COLOR_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 = { .color = kBlackClear },
};
VkRenderingInfo rendering_info = {
.sType = VK_STRUCTURE_TYPE_RENDERING_INFO,
.pNext = nullptr,
.flags = 0,
.renderArea = { .offset = { 0, 0 }, .extent = render_device.swapchainExtent },
.layerCount = 1,
.viewMask = 0,
.colorAttachmentCount = 1,
.pColorAttachments = &attachment_info,
.pDepthAttachment = &depth_attachment_info,
.pStencilAttachment = nullptr,
};
vkCmdPipelineBarrier2( cmd, &misc.acquireToRenderDependency );
vkCmdBeginRendering( cmd, &rendering_info );
{
VkViewport viewport = {
.x = 0,
.y = static_cast<float>( render_device.swapchainExtent.height ),
.width = static_cast<float>( render_device.swapchainExtent.width ),
.height = -static_cast<float>( render_device.swapchainExtent.height ),
.minDepth = 0.0f,
.maxDepth = 1.0f,
};
vkCmdSetViewport( cmd, 0, 1, &viewport );
VkRect2D scissor = {
.offset = { 0, 0 },
.extent = render_device.swapchainExtent,
};
vkCmdSetScissor( cmd, 0, 1, &scissor );
// Render Something?
vkCmdBindPipeline( cmd, VK_PIPELINE_BIND_POINT_GRAPHICS, misc.meshPipeline );
vkCmdBindDescriptorSets(
cmd,
VK_PIPELINE_BIND_POINT_GRAPHICS,
misc.pipelineLayout,
0,
1,
&render_device.textureManager->DescriptorSet(),
0,
nullptr );
vkCmdBindDescriptorSets(
cmd, VK_PIPELINE_BIND_POINT_GRAPHICS, misc.pipelineLayout, 1, 1, &misc.descriptorSet, 0, nullptr );
std::function<void( Blaze::Entity const&, DirectX::XMMATRIX const&, Blaze::Model const* )> draw_entity =
[&]( Blaze::Entity const& entity, DirectX::XMMATRIX const& parent, Blaze::Model const* current )
{
Blaze::Transform const& local_transform = entity.transform;
DirectX::XMMATRIX world_transform;
{
world_transform =
DirectX::XMMatrixAffineTransformation(
local_transform.scale, DirectX::XMVectorZero(), local_transform.rotation, local_transform.translation ) *
parent;
}
if ( not entity.model.IsNull() )
{
VkBuffer const vertex_buffer = render_device.bufferManager->FetchBuffer( entity.model.vertexBuffer ).value();
VkBuffer const index_buffer = render_device.bufferManager->FetchBuffer( entity.model.indexBuffer ).value();
VkDeviceSize constexpr offset = 0;
vkCmdBindVertexBuffers( cmd, 0, 1, &vertex_buffer, &offset );
vkCmdBindIndexBuffer( cmd, index_buffer, offset, VK_INDEX_TYPE_UINT32 );
}
vkCmdPushConstants(
cmd, misc.pipelineLayout, VK_SHADER_STAGE_ALL_GRAPHICS, 0, sizeof world_transform, &world_transform );
DirectX::XMMATRIX const inverse_transform = XMMatrixInverse( nullptr, world_transform );
vkCmdPushConstants(
cmd,
misc.pipelineLayout,
VK_SHADER_STAGE_ALL_GRAPHICS,
sizeof world_transform,
sizeof inverse_transform,
&inverse_transform );
if ( not entity.modelMesh.IsNull() )
{
ASSERT( current );
for ( Blaze::Primitive const& primitive : std::span{
current->primitives.data() + entity.modelMesh.primitiveStart, entity.modelMesh.primitiveCount } )
{
Blaze::byte const* material_data;
if ( primitive.material != UINT32_MAX )
{
Blaze::Material const* mat = &current->materials[primitive.material];
material_data = reinterpret_cast<Blaze::byte const*>( mat );
material_data += Blaze::Material::kGPUDataOffset;
}
else
{
material_data = reinterpret_cast<Blaze::byte const*>( &Blaze::DEFAULT_MATERIAL );
material_data += Blaze::Material::kGPUDataOffset;
}
vkCmdPushConstants(
cmd,
misc.pipelineLayout,
VK_SHADER_STAGE_ALL_GRAPHICS,
2 * sizeof world_transform,
Blaze::Material::kGPUDataSize,
material_data );
vkCmdDrawIndexed( cmd, primitive.indexCount, 1, primitive.indexStart, primitive.vertexOffset, 0 );
}
}
for ( Blaze::Entity& child : entity.IterChildren() )
{
draw_entity( child, world_transform, entity.model.IsNull() ? current : &entity.model );
}
};
for ( Blaze::Entity const& entity : entity_manager.Iterate() )
{
if ( not entity.IsRoot() )
{
continue;
}
draw_entity( entity, DirectX::XMMatrixIdentity(), nullptr );
}
}
vkCmdEndRendering( cmd );
vkCmdPipelineBarrier2( cmd, &misc.renderToPresentDependency );
}
VK_CHECK( vkEndCommandBuffer( cmd ) );
VkPipelineStageFlags stage_mask = VK_PIPELINE_STAGE_COLOR_ATTACHMENT_OUTPUT_BIT;
VkSubmitInfo const submit_info = {
.sType = VK_STRUCTURE_TYPE_SUBMIT_INFO,
.pNext = nullptr,
.waitSemaphoreCount = 1,
.pWaitSemaphores = &current_frame.imageAcquiredSemaphore,
.pWaitDstStageMask = &stage_mask,
.commandBufferCount = 1,
.pCommandBuffers = &cmd,
.signalSemaphoreCount = 1,
.pSignalSemaphores = &current_frame.renderFinishedSemaphore,
};
VK_CHECK( vkQueueSubmit( render_device.directQueue, 1, &submit_info, current_frame.frameReadyToReuse ) );
VkPresentInfoKHR const present_info = {
.sType = VK_STRUCTURE_TYPE_PRESENT_INFO_KHR,
.pNext = nullptr,
.waitSemaphoreCount = 1,
.pWaitSemaphores = &current_frame.renderFinishedSemaphore,
.swapchainCount = 1,
.pSwapchains = &render_device.swapchain,
.pImageIndices = &current_image_index,
.pResults = nullptr,
};
VK_CHECK( vkQueuePresentKHR( render_device.directQueue, &present_info ) );
render_device.frameIndex = ( render_device.frameIndex + 1 ) % NUM_FRAMES;
return SDL_APP_CONTINUE;
}
// ReSharper disable once CppInconsistentNaming
SDL_AppResult SDL_AppEvent( void*, SDL_Event* event )
{
if ( event->type == SDL_EVENT_QUIT )
{
return SDL_APP_SUCCESS;
}
return SDL_APP_CONTINUE;
}
// ReSharper disable once CppInconsistentNaming
void SDL_AppQuit( void* appstate, SDL_AppResult )
{
auto* app_state = static_cast<Blaze::AppState*>( appstate );
if ( app_state ) app_state->Destroy();
Blaze::Global::g_Memory.Destroy();
}