392 lines
14 KiB
C++
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, ¤t_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,
|
|
¤t_image_index ) );
|
|
|
|
// TODO: Resize Swapchain if required.
|
|
|
|
VK_CHECK( vkResetFences( render_device.device, 1, ¤t_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 = ¤t->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 = ¤t_frame.imageAcquiredSemaphore,
|
|
.pWaitDstStageMask = &stage_mask,
|
|
.commandBufferCount = 1,
|
|
.pCommandBuffers = &cmd,
|
|
.signalSemaphoreCount = 1,
|
|
.pSignalSemaphores = ¤t_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 = ¤t_frame.renderFinishedSemaphore,
|
|
.swapchainCount = 1,
|
|
.pSwapchains = &render_device.swapchain,
|
|
.pImageIndices = ¤t_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();
|
|
}
|