Fix subpass dependency and do other things

This commit is contained in:
bailwillharr 2023-03-15 01:49:03 +00:00
parent 2f46fe21ef
commit e1cb168cce
14 changed files with 295 additions and 186 deletions

View File

@ -15,6 +15,7 @@ namespace engine::gfx {
struct DrawBuffer; struct DrawBuffer;
struct DescriptorSetLayout; struct DescriptorSetLayout;
struct DescriptorSet; struct DescriptorSet;
struct Image;
enum class MSAALevel { enum class MSAALevel {
MSAA_OFF, MSAA_OFF,
@ -29,13 +30,16 @@ namespace engine::gfx {
GraphicsSettings() GraphicsSettings()
{ {
// sane defaults // sane defaults
enableValidation = true;
vsync = true; vsync = true;
waitForPresent = true; // not all GPUs/drivers support immediate present with V-sync enabled waitForPresent = true; // not all GPUs/drivers support immediate present with V-sync enabled
msaaLevel = MSAALevel::MSAA_OFF; msaaLevel = MSAALevel::MSAA_OFF;
} }
bool enableValidation;
bool vsync; bool vsync;
bool waitForPresent; // idle CPU after render until the frame has been presented (no affect with V-sync disabled) // idle CPU after render until the frame has been presented (no affect with V-sync disabled)
bool waitForPresent;
MSAALevel msaaLevel; MSAALevel msaaLevel;
}; };
@ -98,7 +102,7 @@ namespace engine::gfx {
struct PipelineInfo { struct PipelineInfo {
const char* vertShaderPath; const char* vertShaderPath;
const char* fragShaderPath; const char* fragShaderPath;
gfx::VertexFormat vertexFormat; VertexFormat vertexFormat;
bool alphaBlending; bool alphaBlending;
bool backfaceCulling; bool backfaceCulling;
std::vector<const DescriptorSetLayout*> descriptorSetLayouts; std::vector<const DescriptorSetLayout*> descriptorSetLayouts;

View File

@ -48,6 +48,8 @@ namespace engine {
gfx::Buffer* createBuffer(gfx::BufferType type, uint64_t size, const void* data); gfx::Buffer* createBuffer(gfx::BufferType type, uint64_t size, const void* data);
void destroyBuffer(const gfx::Buffer* buffer); void destroyBuffer(const gfx::Buffer* buffer);
gfx::Image* createImage(uint32_t w, uint32_t h, const void* imageData);
gfx::Texture* createTexture( gfx::Texture* createTexture(
const void* imageData, const void* imageData,
uint32_t width, uint32_t width,

View File

@ -10,7 +10,7 @@ void main() {
gl_FragDepth = 0.9999; gl_FragDepth = 0.9999;
//outColor = texture(texSampler, fragUV); //outColor = texture(texSampler, fragUV);
outColor = vec4(0.1, 0.1, 0.1, 1.0); outColor = vec4(fragUV, 0.0, 1.0);
} }

View File

@ -51,6 +51,7 @@ namespace engine {
VkFence renderFence = VK_NULL_HANDLE; VkFence renderFence = VK_NULL_HANDLE;
VkSemaphore presentSemaphore = VK_NULL_HANDLE; VkSemaphore presentSemaphore = VK_NULL_HANDLE;
VkSemaphore renderSemaphore = VK_NULL_HANDLE; VkSemaphore renderSemaphore = VK_NULL_HANDLE;
VkCommandPool commandPool = VK_NULL_HANDLE;
VkCommandBuffer drawBuf = VK_NULL_HANDLE; VkCommandBuffer drawBuf = VK_NULL_HANDLE;
}; };
@ -80,9 +81,9 @@ namespace engine {
}; };
struct gfx::DrawBuffer { struct gfx::DrawBuffer {
FrameData frameData; FrameData frameData{};
uint32_t currentFrameIndex; // corresponds to the frameData uint32_t currentFrameIndex = 0; // corresponds to the frameData
uint32_t imageIndex; // for swapchain present uint32_t imageIndex = 0; // for swapchain present
}; };
struct gfx::DescriptorSetLayout { struct gfx::DescriptorSetLayout {
@ -559,6 +560,7 @@ namespace engine {
Swapchain swapchain{}; Swapchain swapchain{};
VkDescriptorPool descriptorPool; VkDescriptorPool descriptorPool;
VkCommandPool transferCommandPool = VK_NULL_HANDLE;
std::array<std::unordered_set<gfx::DescriptorBuffer*>, FRAMES_IN_FLIGHT> descriptorBufferWriteQueues{}; std::array<std::unordered_set<gfx::DescriptorBuffer*>, FRAMES_IN_FLIGHT> descriptorBufferWriteQueues{};
uint64_t FRAMECOUNT = 0; uint64_t FRAMECOUNT = 0;
@ -592,13 +594,7 @@ namespace engine {
throw std::runtime_error("The loaded Vulkan version must be at least 1.3"); throw std::runtime_error("The loaded Vulkan version must be at least 1.3");
} }
#ifdef NDEBUG pimpl->instance = createVulkanInstance(pimpl->window, appName, appVersion, pimpl->graphicsSettings.enableValidation, MessageSeverity::SEV_WARNING);
bool useValidation = false;
#else
bool useValidation = true;
#endif
pimpl->instance = createVulkanInstance(pimpl->window, appName, appVersion, useValidation, MessageSeverity::SEV_WARNING);
if (SDL_Vulkan_CreateSurface(pimpl->window, pimpl->instance.instance, &pimpl->surface) == false) { if (SDL_Vulkan_CreateSurface(pimpl->window, pimpl->instance.instance, &pimpl->surface) == false) {
throw std::runtime_error("Unable to create window surface"); throw std::runtime_error("Unable to create window surface");
@ -607,7 +603,58 @@ namespace engine {
DeviceRequirements deviceRequirements{}; DeviceRequirements deviceRequirements{};
deviceRequirements.requiredExtensions = { VK_KHR_SWAPCHAIN_EXTENSION_NAME }; deviceRequirements.requiredExtensions = { VK_KHR_SWAPCHAIN_EXTENSION_NAME };
deviceRequirements.requiredFeatures.samplerAnisotropy = VK_TRUE; deviceRequirements.requiredFeatures.samplerAnisotropy = VK_TRUE;
deviceRequirements.sampledImageLinearFilter = true; deviceRequirements.requiredFeatures.fillModeNonSolid = VK_TRUE;
deviceRequirements.formats.push_back(
FormatRequirements{
.format = VK_FORMAT_R8G8B8A8_SRGB,
.properties = VkFormatProperties{
.linearTilingFeatures = {},
.optimalTilingFeatures = VK_FORMAT_FEATURE_SAMPLED_IMAGE_FILTER_LINEAR_BIT,
.bufferFeatures = {},
}
}
);
deviceRequirements.formats.push_back(
FormatRequirements{
.format = VK_FORMAT_R32G32_SFLOAT,
.properties = VkFormatProperties{
.linearTilingFeatures = {},
.optimalTilingFeatures = {},
.bufferFeatures = VK_FORMAT_FEATURE_VERTEX_BUFFER_BIT,
}
}
);
deviceRequirements.formats.push_back(
FormatRequirements{
.format = VK_FORMAT_R32G32B32_SFLOAT,
.properties = VkFormatProperties{
.linearTilingFeatures = {},
.optimalTilingFeatures = {},
.bufferFeatures = VK_FORMAT_FEATURE_VERTEX_BUFFER_BIT,
}
}
);
deviceRequirements.formats.push_back(
FormatRequirements{
.format = VK_FORMAT_R32G32B32A32_SFLOAT,
.properties = VkFormatProperties{
.linearTilingFeatures = {},
.optimalTilingFeatures = {},
.bufferFeatures = VK_FORMAT_FEATURE_VERTEX_BUFFER_BIT,
}
}
);
deviceRequirements.formats.push_back( // Depth buffer format
FormatRequirements{
.format = VK_FORMAT_D16_UNORM,
.properties = VkFormatProperties{
.linearTilingFeatures = {},
.optimalTilingFeatures = VK_FORMAT_FEATURE_DEPTH_STENCIL_ATTACHMENT_BIT,
.bufferFeatures = {},
}
}
);
pimpl->device = createDevice(pimpl->instance.instance, deviceRequirements, pimpl->surface); pimpl->device = createDevice(pimpl->instance.instance, deviceRequirements, pimpl->surface);
pimpl->allocator = createAllocator(pimpl->instance.instance, pimpl->device.device, pimpl->device.physicalDevice); pimpl->allocator = createAllocator(pimpl->instance.instance, pimpl->device.device, pimpl->device.physicalDevice);
@ -642,27 +689,44 @@ namespace engine {
res = vkCreateSemaphore(pimpl->device.device, &smphInfo, nullptr, &pimpl->frameData[i].renderSemaphore); res = vkCreateSemaphore(pimpl->device.device, &smphInfo, nullptr, &pimpl->frameData[i].renderSemaphore);
if (res != VK_SUCCESS) throw std::runtime_error("Failed to create semaphore!"); if (res != VK_SUCCESS) throw std::runtime_error("Failed to create semaphore!");
VkCommandPoolCreateInfo poolInfo{
.sType = VK_STRUCTURE_TYPE_COMMAND_POOL_CREATE_INFO,
.pNext = nullptr,
.flags = 0, // Command buffers cannot be individually reset (more performant this way)
.queueFamilyIndex = pimpl->device.queues.presentAndDrawQueueFamily
};
VKCHECK(vkCreateCommandPool(pimpl->device.device, &poolInfo, nullptr, &pimpl->frameData[i].commandPool));
VkCommandBufferAllocateInfo cmdAllocInfo{ VkCommandBufferAllocateInfo cmdAllocInfo{
.sType = VK_STRUCTURE_TYPE_COMMAND_BUFFER_ALLOCATE_INFO, .sType = VK_STRUCTURE_TYPE_COMMAND_BUFFER_ALLOCATE_INFO,
.pNext = nullptr, .pNext = nullptr,
.commandPool = pimpl->device.commandPools.draw, .commandPool = pimpl->frameData[i].commandPool,
.level = VK_COMMAND_BUFFER_LEVEL_PRIMARY, .level = VK_COMMAND_BUFFER_LEVEL_PRIMARY,
.commandBufferCount = 1 .commandBufferCount = 1
}; };
VKCHECK(vkAllocateCommandBuffers(pimpl->device.device, &cmdAllocInfo, &pimpl->frameData[i].drawBuf)); VKCHECK(vkAllocateCommandBuffers(pimpl->device.device, &cmdAllocInfo, &pimpl->frameData[i].drawBuf));
} }
/* create command pool for transfer operations */
VkCommandPoolCreateInfo transferPoolInfo{
.sType = VK_STRUCTURE_TYPE_COMMAND_POOL_CREATE_INFO,
.pNext = nullptr,
.flags = VK_COMMAND_POOL_CREATE_TRANSIENT_BIT, // These command buffers don't last very long
.queueFamilyIndex = pimpl->device.queues.transferQueueFamily
};
VKCHECK(vkCreateCommandPool(pimpl->device.device, &transferPoolInfo, nullptr, &pimpl->transferCommandPool));
/* create a global descriptor pool */ /* create a global descriptor pool */
std::vector<VkDescriptorPoolSize> poolSizes{}; std::vector<VkDescriptorPoolSize> poolSizes{};
poolSizes.emplace_back(VK_DESCRIPTOR_TYPE_UNIFORM_BUFFER, 5); // purposely low limit poolSizes.emplace_back(VK_DESCRIPTOR_TYPE_UNIFORM_BUFFER, 100); // purposely low limit
VkDescriptorPoolCreateInfo descriptorPoolInfo{}; VkDescriptorPoolCreateInfo descriptorPoolInfo{};
descriptorPoolInfo.sType = VK_STRUCTURE_TYPE_DESCRIPTOR_POOL_CREATE_INFO; descriptorPoolInfo.sType = VK_STRUCTURE_TYPE_DESCRIPTOR_POOL_CREATE_INFO;
descriptorPoolInfo.pNext = nullptr; descriptorPoolInfo.pNext = nullptr;
descriptorPoolInfo.flags = 0; descriptorPoolInfo.flags = 0;
descriptorPoolInfo.maxSets = 5; // purposely low limit descriptorPoolInfo.maxSets = 100; // purposely low limit
descriptorPoolInfo.poolSizeCount = poolSizes.size(); descriptorPoolInfo.poolSizeCount = (uint32_t)poolSizes.size();
descriptorPoolInfo.pPoolSizes = poolSizes.data(); descriptorPoolInfo.pPoolSizes = poolSizes.data();
VKCHECK(vkCreateDescriptorPool(pimpl->device.device, &descriptorPoolInfo, nullptr, &pimpl->descriptorPool)); VKCHECK(vkCreateDescriptorPool(pimpl->device.device, &descriptorPoolInfo, nullptr, &pimpl->descriptorPool));
@ -672,8 +736,10 @@ namespace engine {
{ {
vkDestroyDescriptorPool(pimpl->device.device, pimpl->descriptorPool, nullptr); vkDestroyDescriptorPool(pimpl->device.device, pimpl->descriptorPool, nullptr);
vkDestroyCommandPool(pimpl->device.device, pimpl->transferCommandPool, nullptr);
for (uint32_t i = 0; i < FRAMES_IN_FLIGHT; i++) { for (uint32_t i = 0; i < FRAMES_IN_FLIGHT; i++) {
vkFreeCommandBuffers(pimpl->device.device, pimpl->device.commandPools.draw, 1, &pimpl->frameData[i].drawBuf); vkDestroyCommandPool(pimpl->device.device, pimpl->frameData[i].commandPool, nullptr);
vkDestroySemaphore(pimpl->device.device, pimpl->frameData[i].renderSemaphore, nullptr); vkDestroySemaphore(pimpl->device.device, pimpl->frameData[i].renderSemaphore, nullptr);
vkDestroySemaphore(pimpl->device.device, pimpl->frameData[i].presentSemaphore, nullptr); vkDestroySemaphore(pimpl->device.device, pimpl->frameData[i].presentSemaphore, nullptr);
vkDestroyFence(pimpl->device.device, pimpl->frameData[i].renderFence, nullptr); vkDestroyFence(pimpl->device.device, pimpl->frameData[i].renderFence, nullptr);
@ -707,10 +773,17 @@ namespace engine {
const uint32_t currentFrameIndex = pimpl->FRAMECOUNT % FRAMES_IN_FLIGHT; const uint32_t currentFrameIndex = pimpl->FRAMECOUNT % FRAMES_IN_FLIGHT;
const FrameData frameData = pimpl->frameData[currentFrameIndex]; const FrameData frameData = pimpl->frameData[currentFrameIndex];
/* wait until the previous frame RENDERING has finished */
res = vkWaitForFences(pimpl->device.device, 1, &frameData.renderFence, VK_TRUE, 1000000000LL);
VKCHECK(res);
res = vkResetFences(pimpl->device.device, 1, &frameData.renderFence);
VKCHECK(res);
/* first empty the descriptor buffer write queue */ /* first empty the descriptor buffer write queue */
auto& writeQueue = pimpl->descriptorBufferWriteQueues[currentFrameIndex]; auto& writeQueue = pimpl->descriptorBufferWriteQueues[currentFrameIndex];
//if (writeQueue.empty() == false) vkQueueWaitIdle(pimpl->device.queues.drawQueues[0]);
for (gfx::DescriptorBuffer* buffer : writeQueue) { for (gfx::DescriptorBuffer* buffer : writeQueue) {
copyBuffer(pimpl->device.device, pimpl->device.commandPools.transfer, pimpl->device.queues.transferQueues[0], buffer->stagingBuffer.buffer, buffer->gpuBuffers[currentFrameIndex].buffer, buffer->stagingBuffer.size); copyBuffer(pimpl->device.device, pimpl->transferCommandPool, pimpl->device.queues.transferQueues[0], buffer->stagingBuffer.buffer, buffer->gpuBuffers[currentFrameIndex].buffer, buffer->stagingBuffer.size);
} }
writeQueue.clear(); writeQueue.clear();
@ -731,14 +804,8 @@ namespace engine {
if (res == VK_SUCCESS) pimpl->swapchainIsOutOfDate = false; if (res == VK_SUCCESS) pimpl->swapchainIsOutOfDate = false;
} while (pimpl->swapchainIsOutOfDate); } while (pimpl->swapchainIsOutOfDate);
/* wait until the previous frame RENDERING has finished */
res = vkWaitForFences(pimpl->device.device, 1, &frameData.renderFence, VK_TRUE, 1000000000LL);
VKCHECK(res);
res = vkResetFences(pimpl->device.device, 1, &frameData.renderFence);
VKCHECK(res);
/* record command buffer */ /* record command buffer */
res = vkResetCommandBuffer(frameData.drawBuf, 0); res = vkResetCommandPool(pimpl->device.device, frameData.commandPool, 0);
VKCHECK(res); VKCHECK(res);
VkCommandBufferBeginInfo beginInfo{ VkCommandBufferBeginInfo beginInfo{
@ -763,10 +830,10 @@ namespace engine {
passBegin.sType = VK_STRUCTURE_TYPE_RENDER_PASS_BEGIN_INFO; passBegin.sType = VK_STRUCTURE_TYPE_RENDER_PASS_BEGIN_INFO;
passBegin.pNext = nullptr; passBegin.pNext = nullptr;
passBegin.renderPass = pimpl->swapchain.renderpass; passBegin.renderPass = pimpl->swapchain.renderpass;
passBegin.framebuffer = std::get<2>(pimpl->swapchain.images[swapchainImageIndex]); passBegin.framebuffer = pimpl->swapchain.framebuffers[swapchainImageIndex];
passBegin.renderArea.extent = pimpl->swapchain.extent; passBegin.renderArea.extent = pimpl->swapchain.extent;
passBegin.renderArea.offset = { 0, 0 }; passBegin.renderArea.offset = { 0, 0 };
passBegin.clearValueCount = clearValues.size(); passBegin.clearValueCount = (uint32_t)clearValues.size();
passBegin.pClearValues = clearValues.data(); passBegin.pClearValues = clearValues.data();
vkCmdBeginRenderPass(frameData.drawBuf, &passBegin, VK_SUBPASS_CONTENTS_INLINE); vkCmdBeginRenderPass(frameData.drawBuf, &passBegin, VK_SUBPASS_CONTENTS_INLINE);
@ -824,6 +891,7 @@ namespace engine {
.pSignalSemaphores = &drawBuffer->frameData.renderSemaphore, .pSignalSemaphores = &drawBuffer->frameData.renderSemaphore,
}; };
res = vkQueueSubmit(pimpl->device.queues.drawQueues[0], 1, &submitInfo, drawBuffer->frameData.renderFence); res = vkQueueSubmit(pimpl->device.queues.drawQueues[0], 1, &submitInfo, drawBuffer->frameData.renderFence);
assert(res == VK_SUCCESS);
// VKCHECK(res); // expensive operation for some reason // VKCHECK(res); // expensive operation for some reason
// PRESENT // PRESENT
@ -913,10 +981,10 @@ namespace engine {
attribDescs.reserve(info.vertexFormat.attributeDescriptions.size()); attribDescs.reserve(info.vertexFormat.attributeDescriptions.size());
for (const auto& desc : info.vertexFormat.attributeDescriptions) { for (const auto& desc : info.vertexFormat.attributeDescriptions) {
VkVertexInputAttributeDescription vulkanAttribDesc{}; VkVertexInputAttributeDescription vulkanAttribDesc{};
vulkanAttribDesc.binding = 0;
vulkanAttribDesc.location = desc.location; vulkanAttribDesc.location = desc.location;
vulkanAttribDesc.offset = desc.offset; vulkanAttribDesc.binding = 0;
vulkanAttribDesc.format = vkinternal::getVertexAttribFormat(desc.format); vulkanAttribDesc.format = vkinternal::getVertexAttribFormat(desc.format);
vulkanAttribDesc.offset = desc.offset;
attribDescs.push_back(vulkanAttribDesc); attribDescs.push_back(vulkanAttribDesc);
} }
@ -982,7 +1050,7 @@ namespace engine {
VkPipelineRasterizationStateCreateInfo rasterizer{}; VkPipelineRasterizationStateCreateInfo rasterizer{};
rasterizer.sType = VK_STRUCTURE_TYPE_PIPELINE_RASTERIZATION_STATE_CREATE_INFO; rasterizer.sType = VK_STRUCTURE_TYPE_PIPELINE_RASTERIZATION_STATE_CREATE_INFO;
rasterizer.depthClampEnable = VK_FALSE; rasterizer.depthClampEnable = VK_FALSE;
rasterizer.rasterizerDiscardEnable = VK_FALSE; rasterizer.rasterizerDiscardEnable = VK_FALSE; // enabling this will not run the fragment shaders at all
rasterizer.polygonMode = VK_POLYGON_MODE_FILL; rasterizer.polygonMode = VK_POLYGON_MODE_FILL;
rasterizer.lineWidth = 1.0f; rasterizer.lineWidth = 1.0f;
if (info.backfaceCulling == true) { if (info.backfaceCulling == true) {
@ -1060,7 +1128,7 @@ namespace engine {
VkPipelineLayoutCreateInfo layoutInfo{}; VkPipelineLayoutCreateInfo layoutInfo{};
layoutInfo.sType = VK_STRUCTURE_TYPE_PIPELINE_LAYOUT_CREATE_INFO; layoutInfo.sType = VK_STRUCTURE_TYPE_PIPELINE_LAYOUT_CREATE_INFO;
layoutInfo.setLayoutCount = descriptorSetLayouts.size(); layoutInfo.setLayoutCount = (uint32_t)descriptorSetLayouts.size();
layoutInfo.pSetLayouts = descriptorSetLayouts.data(); layoutInfo.pSetLayouts = descriptorSetLayouts.data();
layoutInfo.pushConstantRangeCount = 1; layoutInfo.pushConstantRangeCount = 1;
layoutInfo.pPushConstantRanges = &pushConstantRange; layoutInfo.pPushConstantRanges = &pushConstantRange;
@ -1119,7 +1187,7 @@ namespace engine {
info.sType = VK_STRUCTURE_TYPE_DESCRIPTOR_SET_LAYOUT_CREATE_INFO; info.sType = VK_STRUCTURE_TYPE_DESCRIPTOR_SET_LAYOUT_CREATE_INFO;
info.pNext = nullptr; info.pNext = nullptr;
info.flags = 0; info.flags = 0;
info.bindingCount = bindings.size(); info.bindingCount = (uint32_t)bindings.size();
info.pBindings = bindings.data(); info.pBindings = bindings.data();
VKCHECK(vkCreateDescriptorSetLayout(pimpl->device.device, &info, nullptr, &out->layout)); VKCHECK(vkCreateDescriptorSetLayout(pimpl->device.device, &info, nullptr, &out->layout));
@ -1230,7 +1298,7 @@ namespace engine {
VKCHECK(vmaCreateBuffer(pimpl->allocator, &gpuBufferInfo, &gpuAllocationInfo, &out->gpuBuffers[i].buffer, &out->gpuBuffers[i].allocation, nullptr)); VKCHECK(vmaCreateBuffer(pimpl->allocator, &gpuBufferInfo, &gpuAllocationInfo, &out->gpuBuffers[i].buffer, &out->gpuBuffers[i].allocation, nullptr));
/* copy staging buffer into both */ /* copy staging buffer into both */
copyBuffer(pimpl->device.device, pimpl->device.commandPools.transfer, pimpl->device.queues.transferQueues[0], out->stagingBuffer.buffer, out->gpuBuffers[i].buffer, out->stagingBuffer.size); copyBuffer(pimpl->device.device, pimpl->transferCommandPool, pimpl->device.queues.transferQueues[0], out->stagingBuffer.buffer, out->gpuBuffers[i].buffer, out->stagingBuffer.size);
} }
return out; return out;
@ -1320,7 +1388,7 @@ namespace engine {
} }
// copy the data from the staging buffer to the gpu buffer // copy the data from the staging buffer to the gpu buffer
copyBuffer(pimpl->device.device, pimpl->device.commandPools.transfer, pimpl->device.queues.transferQueues[0], stagingBuffer, out->buffer, out->size); copyBuffer(pimpl->device.device, pimpl->transferCommandPool, pimpl->device.queues.transferQueues[0], stagingBuffer, out->buffer, out->size);
// destroy staging buffer // destroy staging buffer
vmaDestroyBuffer(pimpl->allocator, stagingBuffer, stagingAllocation); vmaDestroyBuffer(pimpl->allocator, stagingBuffer, stagingAllocation);
@ -1334,6 +1402,11 @@ namespace engine {
delete buffer; delete buffer;
} }
gfx::Image* GFXDevice::createImage(uint32_t w, uint32_t h, const void* imageData)
{
return nullptr;
}
gfx::Texture* GFXDevice::createTexture( gfx::Texture* GFXDevice::createTexture(
const void* imageData, const void* imageData,
uint32_t width, uint32_t width,

View File

@ -38,7 +38,7 @@ namespace engine {
const float NZdistance = glm::abs(subjectCentre.z - object.pos1.z); const float NZdistance = glm::abs(subjectCentre.z - object.pos1.z);
const std::array<float, 6> distances { PXdistance, NXdistance, PYdistance, NYdistance, PZdistance, NZdistance }; const std::array<float, 6> distances { PXdistance, NXdistance, PYdistance, NYdistance, PZdistance, NZdistance };
const auto minDistance = std::min_element(distances.begin(), distances.end()); const auto minDistance = std::min_element(distances.begin(), distances.end());
const int index = minDistance - distances.begin(); const int index = static_cast<int>(minDistance - distances.begin());
switch (index) { switch (index) {
case 0: case 0:
// P_X // P_X
@ -97,7 +97,7 @@ namespace engine {
const glm::vec3 globalPosition = t->worldMatrix[3]; const glm::vec3 globalPosition = t->worldMatrix[3];
const AABB localBoundingBox = c->aabb; const AABB localBoundingBox = c->aabb;
AABB globalBoundingBox; AABB globalBoundingBox{};
globalBoundingBox.pos1 = globalPosition + localBoundingBox.pos1; globalBoundingBox.pos1 = globalPosition + localBoundingBox.pos1;
globalBoundingBox.pos2 = globalPosition + localBoundingBox.pos2; globalBoundingBox.pos2 = globalPosition + localBoundingBox.pos2;
@ -117,8 +117,8 @@ namespace engine {
// Check every static collider against every dynamic collider, and every dynamic collider against every other one // Check every static collider against every dynamic collider, and every dynamic collider against every other one
// This technique is inefficient for many entities. // This technique is inefficient for many entities.
for (auto [staticEntity, staticAABB, staticTrigger] : m_staticAABBs) { for (const auto& [staticEntity, staticAABB, staticTrigger] : m_staticAABBs) {
for (auto [dynamicEntity, dynamicAABB, dynamicTrigger] : m_dynamicAABBs) { for (const auto& [dynamicEntity, dynamicAABB, dynamicTrigger] : m_dynamicAABBs) {
if (checkCollisionFast(staticAABB, dynamicAABB)) { if (checkCollisionFast(staticAABB, dynamicAABB)) {
if (staticTrigger || dynamicTrigger) { // only check collisions involved with triggers if (staticTrigger || dynamicTrigger) { // only check collisions involved with triggers
m_possibleCollisions.emplace_back( m_possibleCollisions.emplace_back(
@ -131,7 +131,7 @@ namespace engine {
} }
// get collision details and submit events // get collision details and submit events
for (auto possibleCollision : m_possibleCollisions) { for (const auto& possibleCollision : m_possibleCollisions) {
if (possibleCollision.staticTrigger) { if (possibleCollision.staticTrigger) {
CollisionEvent info{}; CollisionEvent info{};
info.isCollisionEnter = true; info.isCollisionEnter = true;
@ -156,7 +156,7 @@ namespace engine {
} }
} }
for (auto [entity, info] : m_collisionInfos) { for (const auto& [entity, info] : m_collisionInfos) {
m_scene->events()->queueEvent<CollisionEvent>(EventSubscriberKind::ENTITY, entity, info); m_scene->events()->queueEvent<CollisionEvent>(EventSubscriberKind::ENTITY, entity, info);
} }
} }

View File

@ -35,7 +35,7 @@ namespace engine::util {
{ {
// convert to glm column major // convert to glm column major
glm::mat4 transform; glm::mat4 transform{};
for (int i = 0; i < 4; i++) { for (int i = 0; i < 4; i++) {
for (int j = 0; j < 4; j++) { for (int j = 0; j < 4; j++) {
transform[i][j] = parentNode->mTransformation[j][i]; transform[i][j] = parentNode->mTransformation[j][i];
@ -196,7 +196,7 @@ namespace engine::util {
const aiMesh* m = scene->mMeshes[i]; const aiMesh* m = scene->mMeshes[i];
meshMaterialIndices.push_back(m->mMaterialIndex); meshMaterialIndices.push_back(m->mMaterialIndex);
std::vector<Vertex> vertices(m->mNumVertices); std::vector<Vertex> vertices(m->mNumVertices);
std::vector<uint32_t> indices(m->mNumFaces * 3); std::vector<uint32_t> indices((size_t)m->mNumFaces * 3);
LOG_TRACE("Mesh {}: vertex count {}", i, vertices.size()); LOG_TRACE("Mesh {}: vertex count {}", i, vertices.size());
LOG_TRACE("Mesh {}: index count {}", i, indices.size()); LOG_TRACE("Mesh {}: index count {}", i, indices.size());
@ -219,9 +219,9 @@ namespace engine::util {
} }
for (uint32_t j = 0; j < indices.size() / 3; j++) { for (uint32_t j = 0; j < indices.size() / 3; j++) {
indices[j * 3 + 0] = m->mFaces[j].mIndices[0]; indices[(size_t)j * 3 + 0] = m->mFaces[j].mIndices[0];
indices[j * 3 + 1] = m->mFaces[j].mIndices[1]; indices[(size_t)j * 3 + 1] = m->mFaces[j].mIndices[1];
indices[j * 3 + 2] = m->mFaces[j].mIndices[2]; indices[(size_t)j * 3 + 2] = m->mFaces[j].mIndices[2];
} }
meshes.push_back(std::make_shared<resources::Mesh>(parent->app()->gfx(), vertices, indices)); meshes.push_back(std::make_shared<resources::Mesh>(parent->app()->gfx(), vertices, indices));
} }

View File

@ -20,8 +20,8 @@ namespace engine {
return supportsPresent; return supportsPresent;
} }
/* chooses a device, creates it, gets its function pointers, and creates command pools */ /* chooses a device, creates it, gets its function pointers, and retrieves queues */
Device createDevice(VkInstance instance, DeviceRequirements requirements, VkSurfaceKHR surface) Device createDevice(VkInstance instance, const DeviceRequirements& requirements, VkSurfaceKHR surface)
{ {
Device d{}; Device d{};
@ -187,14 +187,21 @@ namespace engine {
if (devFeatures.inheritedQueries == VK_FALSE) continue; if (devFeatures.inheritedQueries == VK_FALSE) continue;
} }
if (requirements.sampledImageLinearFilter == true) { bool formatsSupported = true;
// check for linear filtering for mipmaps for (const FormatRequirements& formatRequirements : requirements.formats) {
VkFormatProperties formatProperties{}; VkFormatFeatureFlags requiredLinearFlags = formatRequirements.properties.linearTilingFeatures;
vkGetPhysicalDeviceFormatProperties(physDev, VK_FORMAT_R8G8B8A8_SRGB, &formatProperties); VkFormatFeatureFlags requiredOptimalFlags = formatRequirements.properties.optimalTilingFeatures;
if (!(formatProperties.optimalTilingFeatures & VK_FORMAT_FEATURE_SAMPLED_IMAGE_FILTER_LINEAR_BIT)) { VkFormatFeatureFlags requiredBufferFlags = formatRequirements.properties.bufferFeatures;
continue; VkFormatProperties deviceFormatProperties{};
vkGetPhysicalDeviceFormatProperties(physDev, formatRequirements.format, &deviceFormatProperties);
if ((deviceFormatProperties.linearTilingFeatures & requiredLinearFlags) != requiredLinearFlags ||
(deviceFormatProperties.optimalTilingFeatures & requiredOptimalFlags) != requiredOptimalFlags ||
(deviceFormatProperties.bufferFeatures & requiredBufferFlags) != requiredBufferFlags) {
formatsSupported = false;
break;
} }
} }
if (formatsSupported == false) continue;
/* USE THIS PHYSICAL DEVICE */ /* USE THIS PHYSICAL DEVICE */
d.physicalDevice = physDev; d.physicalDevice = physDev;
@ -223,7 +230,7 @@ namespace engine {
if (p.queueCount < 2) continue; // ideally have one queue for presenting and at least one other for rendering if (p.queueCount < 2) continue; // ideally have one queue for presenting and at least one other for rendering
if (p.queueFlags & VK_QUEUE_GRAPHICS_BIT) { if (p.queueFlags & VK_QUEUE_GRAPHICS_BIT) {
if (checkQueueFamilySupportsPresent(d.physicalDevice, surface, i)) { if (checkQueueFamilySupportsPresent(d.physicalDevice, surface, static_cast<uint32_t>(i))) {
graphicsFamily = static_cast<uint32_t>(i); graphicsFamily = static_cast<uint32_t>(i);
break; break;
} }
@ -233,7 +240,7 @@ namespace engine {
for (size_t i = 0; i < queueFamilies.size(); i++) { for (size_t i = 0; i < queueFamilies.size(); i++) {
VkQueueFamilyProperties p = queueFamilies[i]; VkQueueFamilyProperties p = queueFamilies[i];
if (p.queueFlags & VK_QUEUE_GRAPHICS_BIT) { if (p.queueFlags & VK_QUEUE_GRAPHICS_BIT) {
if (checkQueueFamilySupportsPresent(d.physicalDevice, surface, i)) { if (checkQueueFamilySupportsPresent(d.physicalDevice, surface, static_cast<uint32_t>(i))) {
graphicsFamily = static_cast<uint32_t>(i); graphicsFamily = static_cast<uint32_t>(i);
} }
} }
@ -313,7 +320,7 @@ namespace engine {
if (transferFamily != graphicsFamily) { if (transferFamily != graphicsFamily) {
vkGetDeviceQueue(d.device, graphicsFamily, 0, &d.queues.presentQueue); vkGetDeviceQueue(d.device, graphicsFamily, 0, &d.queues.presentQueue);
if (queueFamilies[graphicsFamily].queueCount >= 2) { if (queueFamilies[graphicsFamily].queueCount >= 2) {
d.queues.drawQueues.resize(queueFamilies[graphicsFamily].queueCount - 1); d.queues.drawQueues.resize((size_t)queueFamilies[graphicsFamily].queueCount - 1);
for (uint32_t i = 0; i < d.queues.drawQueues.size(); i++) { for (uint32_t i = 0; i < d.queues.drawQueues.size(); i++) {
vkGetDeviceQueue(d.device, graphicsFamily, i + 1, &d.queues.drawQueues[i]); vkGetDeviceQueue(d.device, graphicsFamily, i + 1, &d.queues.drawQueues[i]);
} }
@ -334,7 +341,7 @@ namespace engine {
vkGetDeviceQueue(d.device, graphicsFamily, 1, &d.queues.transferQueues[0]); vkGetDeviceQueue(d.device, graphicsFamily, 1, &d.queues.transferQueues[0]);
// use the remaining queues for drawing // use the remaining queues for drawing
if (queueCount >= 3) { if (queueCount >= 3) {
d.queues.drawQueues.resize(queueCount - 2); d.queues.drawQueues.resize((size_t)queueCount - 2);
for (uint32_t i = 0; i < queueCount - 2; i++) { for (uint32_t i = 0; i < queueCount - 2; i++) {
vkGetDeviceQueue(d.device, graphicsFamily, i + 2, &d.queues.drawQueues[i]); vkGetDeviceQueue(d.device, graphicsFamily, i + 2, &d.queues.drawQueues[i]);
} }
@ -357,37 +364,12 @@ namespace engine {
d.queues.presentAndDrawQueueFamily = graphicsFamily; d.queues.presentAndDrawQueueFamily = graphicsFamily;
d.queues.transferQueueFamily = transferFamily; d.queues.transferQueueFamily = transferFamily;
/* generate command pools */
VkCommandPoolCreateInfo poolCreateInfo{
.sType = VK_STRUCTURE_TYPE_COMMAND_POOL_CREATE_INFO,
.pNext = nullptr,
.flags = 0, // set individually after
.queueFamilyIndex = 0, // set individually after
};
// present queue does not need a command pool as it does not use command buffers
// draw command pools:
poolCreateInfo.flags = VK_COMMAND_POOL_CREATE_RESET_COMMAND_BUFFER_BIT;
poolCreateInfo.queueFamilyIndex = graphicsFamily;
res = vkCreateCommandPool(d.device, &poolCreateInfo, nullptr, &d.commandPools.draw);
if (res != VK_SUCCESS) throw std::runtime_error("Failed to create command pool");
// transfer command pools:
poolCreateInfo.flags = VK_COMMAND_POOL_CREATE_TRANSIENT_BIT; // buffers from this pool are often short-lived,
// as is usually the case for transfer operations
poolCreateInfo.queueFamilyIndex = transferFamily;
res = vkCreateCommandPool(d.device, &poolCreateInfo, nullptr, &d.commandPools.transfer);
if (res != VK_SUCCESS) throw std::runtime_error("Failed to create command pool");
return d; return d;
} }
void destroyDevice(Device device) void destroyDevice(Device device)
{ {
vkDestroyCommandPool(device.device, device.commandPools.transfer, nullptr);
vkDestroyCommandPool(device.device, device.commandPools.draw, nullptr);
vkDestroyDevice(device.device, nullptr); vkDestroyDevice(device.device, nullptr);
} }

View File

@ -6,10 +6,15 @@
namespace engine { namespace engine {
struct FormatRequirements {
VkFormat format{};
VkFormatProperties properties{};
};
struct DeviceRequirements { struct DeviceRequirements {
std::vector<const char*> requiredExtensions; std::vector<const char*> requiredExtensions;
VkPhysicalDeviceFeatures requiredFeatures; VkPhysicalDeviceFeatures requiredFeatures;
bool sampledImageLinearFilter; std::vector<FormatRequirements> formats{};
}; };
struct Device { struct Device {
@ -27,13 +32,9 @@ namespace engine {
uint32_t transferQueueFamily = UINT32_MAX; uint32_t transferQueueFamily = UINT32_MAX;
} queues{}; } queues{};
struct CommandPools {
VkCommandPool draw = VK_NULL_HANDLE; // use with the drawQueues
VkCommandPool transfer = VK_NULL_HANDLE; // use with the transferQueues
} commandPools{};
}; };
Device createDevice(VkInstance instance, DeviceRequirements requirements, VkSurfaceKHR surface); Device createDevice(VkInstance instance, const DeviceRequirements& requirements, VkSurfaceKHR surface);
void destroyDevice(Device device); void destroyDevice(Device device);
} }

View File

@ -131,62 +131,6 @@ namespace engine {
vkDestroySwapchainKHR(info.device, scInfo.oldSwapchain, nullptr); vkDestroySwapchainKHR(info.device, scInfo.oldSwapchain, nullptr);
} }
{ /* create the depth buffer */
sc->depthStencil.format = VK_FORMAT_D32_SFLOAT;
if (sc->depthStencil.image != VK_NULL_HANDLE) {
vkDestroyImageView(info.device, sc->depthStencil.view, nullptr);
vmaDestroyImage(sc->allocator, sc->depthStencil.image, sc->depthStencil.allocation);
}
VkImageCreateInfo imageInfo{};
imageInfo.sType = VK_STRUCTURE_TYPE_IMAGE_CREATE_INFO;
imageInfo.pNext = nullptr;
imageInfo.flags = 0;
imageInfo.imageType = VK_IMAGE_TYPE_2D;
imageInfo.format = sc->depthStencil.format;
imageInfo.extent.width = sc->extent.width;
imageInfo.extent.height = sc->extent.height;
imageInfo.extent.depth = 1;
imageInfo.mipLevels = 1;
imageInfo.arrayLayers = 1;
imageInfo.samples = VK_SAMPLE_COUNT_1_BIT;
imageInfo.tiling = VK_IMAGE_TILING_OPTIMAL;
imageInfo.usage = VK_IMAGE_USAGE_DEPTH_STENCIL_ATTACHMENT_BIT;
imageInfo.sharingMode = VK_SHARING_MODE_EXCLUSIVE;
imageInfo.queueFamilyIndexCount = 0; // ignored
imageInfo.pQueueFamilyIndices = nullptr; // ignored
imageInfo.initialLayout = VK_IMAGE_LAYOUT_UNDEFINED;
VmaAllocationCreateInfo allocInfo{};
allocInfo.usage = VMA_MEMORY_USAGE_AUTO_PREFER_DEVICE;
allocInfo.flags = VMA_ALLOCATION_CREATE_DEDICATED_MEMORY_BIT;
allocInfo.priority = 1.0f;
res = vmaCreateImage(sc->allocator, &imageInfo, &allocInfo, &sc->depthStencil.image, &sc->depthStencil.allocation, nullptr);
if (res != VK_SUCCESS) throw std::runtime_error("Failed to create depth buffer image! Code: " + std::to_string(res));
VkImageViewCreateInfo viewInfo{};
viewInfo.sType = VK_STRUCTURE_TYPE_IMAGE_VIEW_CREATE_INFO;
viewInfo.pNext = nullptr;
viewInfo.flags = 0;
viewInfo.image = sc->depthStencil.image;
viewInfo.viewType = VK_IMAGE_VIEW_TYPE_2D;
viewInfo.format = sc->depthStencil.format;
viewInfo.components.r = VK_COMPONENT_SWIZZLE_IDENTITY;
viewInfo.components.g = VK_COMPONENT_SWIZZLE_IDENTITY;
viewInfo.components.b = VK_COMPONENT_SWIZZLE_IDENTITY;
viewInfo.components.a = VK_COMPONENT_SWIZZLE_IDENTITY;
viewInfo.subresourceRange.aspectMask = VK_IMAGE_ASPECT_DEPTH_BIT;
viewInfo.subresourceRange.baseMipLevel = 0;
viewInfo.subresourceRange.levelCount = 1;
viewInfo.subresourceRange.baseArrayLayer = 0;
viewInfo.subresourceRange.layerCount = 1;
res = vkCreateImageView(info.device, &viewInfo, nullptr, &sc->depthStencil.view);
assert(res == VK_SUCCESS);
}
/* create the render pass */ /* create the render pass */
if (sc->renderpass != VK_NULL_HANDLE) { if (sc->renderpass != VK_NULL_HANDLE) {
vkDestroyRenderPass(sc->device, sc->renderpass, nullptr); vkDestroyRenderPass(sc->device, sc->renderpass, nullptr);
@ -204,7 +148,7 @@ namespace engine {
}; };
VkAttachmentDescription depthStencilAttachment{ VkAttachmentDescription depthStencilAttachment{
.flags = 0, .flags = 0,
.format = sc->depthStencil.format, .format = sc->depthStencilFormat,
.samples = VK_SAMPLE_COUNT_1_BIT, .samples = VK_SAMPLE_COUNT_1_BIT,
.loadOp = VK_ATTACHMENT_LOAD_OP_CLEAR, .loadOp = VK_ATTACHMENT_LOAD_OP_CLEAR,
.storeOp = VK_ATTACHMENT_STORE_OP_DONT_CARE, // the depth buffer is not used after the fragment shader .storeOp = VK_ATTACHMENT_STORE_OP_DONT_CARE, // the depth buffer is not used after the fragment shader
@ -234,25 +178,38 @@ namespace engine {
.preserveAttachmentCount = 0, .preserveAttachmentCount = 0,
.pPreserveAttachments = nullptr, .pPreserveAttachments = nullptr,
}; };
VkSubpassDependency dependency{ VkSubpassDependency attachmentDependencies[2] = {
{
// Depth buffer
.srcSubpass = VK_SUBPASS_EXTERNAL, .srcSubpass = VK_SUBPASS_EXTERNAL,
.dstSubpass = 0, .dstSubpass = 0,
.srcStageMask = VK_PIPELINE_STAGE_COLOR_ATTACHMENT_OUTPUT_BIT | VK_PIPELINE_STAGE_EARLY_FRAGMENT_TESTS_BIT, .srcStageMask = VK_PIPELINE_STAGE_EARLY_FRAGMENT_TESTS_BIT | VK_PIPELINE_STAGE_LATE_FRAGMENT_TESTS_BIT,
.dstStageMask = VK_PIPELINE_STAGE_COLOR_ATTACHMENT_OUTPUT_BIT | VK_PIPELINE_STAGE_EARLY_FRAGMENT_TESTS_BIT, .dstStageMask = VK_PIPELINE_STAGE_EARLY_FRAGMENT_TESTS_BIT | VK_PIPELINE_STAGE_LATE_FRAGMENT_TESTS_BIT,
.srcAccessMask = VK_ACCESS_DEPTH_STENCIL_ATTACHMENT_WRITE_BIT,
.dstAccessMask = VK_ACCESS_DEPTH_STENCIL_ATTACHMENT_READ_BIT | VK_ACCESS_DEPTH_STENCIL_ATTACHMENT_WRITE_BIT,
.dependencyFlags = 0,
},
{
// Image Layout Transition
.srcSubpass = VK_SUBPASS_EXTERNAL,
.dstSubpass = 0,
.srcStageMask = VK_PIPELINE_STAGE_COLOR_ATTACHMENT_OUTPUT_BIT,
.dstStageMask = VK_PIPELINE_STAGE_COLOR_ATTACHMENT_OUTPUT_BIT,
.srcAccessMask = 0, .srcAccessMask = 0,
.dstAccessMask = VK_ACCESS_COLOR_ATTACHMENT_WRITE_BIT | VK_ACCESS_DEPTH_STENCIL_ATTACHMENT_WRITE_BIT, .dstAccessMask = VK_ACCESS_COLOR_ATTACHMENT_WRITE_BIT | VK_ACCESS_COLOR_ATTACHMENT_READ_BIT,
.dependencyFlags = 0 .dependencyFlags = 0,
},
}; };
VkRenderPassCreateInfo renderPassInfo{ VkRenderPassCreateInfo renderPassInfo{
.sType = VK_STRUCTURE_TYPE_RENDER_PASS_CREATE_INFO, .sType = VK_STRUCTURE_TYPE_RENDER_PASS_CREATE_INFO,
.pNext = nullptr, .pNext = nullptr,
.flags = 0, .flags = 0,
.attachmentCount = attachments.size(), .attachmentCount = (uint32_t)attachments.size(),
.pAttachments = attachments.data(), .pAttachments = attachments.data(),
.subpassCount = 1, .subpassCount = 1,
.pSubpasses = &subpass, .pSubpasses = &subpass,
.dependencyCount = 1, .dependencyCount = 2,
.pDependencies = &dependency, .pDependencies = attachmentDependencies,
}; };
res = vkCreateRenderPass(sc->device, &renderPassInfo, nullptr, &sc->renderpass); res = vkCreateRenderPass(sc->device, &renderPassInfo, nullptr, &sc->renderpass);
if (res != EXIT_SUCCESS) throw std::runtime_error("Failed to create renderpass!"); if (res != EXIT_SUCCESS) throw std::runtime_error("Failed to create renderpass!");
@ -266,21 +223,31 @@ namespace engine {
assert(res == VK_SUCCESS); assert(res == VK_SUCCESS);
/* create image view and framebuffer for each image */ /* create image view and framebuffer for each image */
sc->images.resize(swapchainImageCount); if (sc->swapchainImages.size() == 0) {
sc->swapchainImages.resize(swapchainImageCount);
sc->depthImages.resize(swapchainImageCount);
sc->framebuffers.resize(swapchainImageCount);
}
for (uint32_t i = 0; i < swapchainImageCount; i++) { for (uint32_t i = 0; i < swapchainImageCount; i++) {
auto& [image, imageView, framebuffer] = sc->images.at(i); auto& [swapchainImage, swapchainImageView] = sc->swapchainImages.at(i);
auto& [depthImage, depthAllocation, depthImageView] = sc->depthImages.at(i);
auto& framebuffer = sc->framebuffers.at(i);
if (imageView != VK_NULL_HANDLE) vkDestroyImageView(sc->device, imageView, nullptr); if (swapchainImageView != VK_NULL_HANDLE) vkDestroyImageView(sc->device, swapchainImageView, nullptr);
if (depthImageView != VK_NULL_HANDLE) {
vkDestroyImageView(sc->device, depthImageView, nullptr);
vmaDestroyImage(sc->allocator, depthImage, depthAllocation);
}
if (framebuffer != VK_NULL_HANDLE) vkDestroyFramebuffer(sc->device, framebuffer, nullptr); if (framebuffer != VK_NULL_HANDLE) vkDestroyFramebuffer(sc->device, framebuffer, nullptr);
image = swapchainImages[i]; swapchainImage = swapchainImages[i];
/* make the image view */ /* make the image view */
VkImageViewCreateInfo viewInfo{}; VkImageViewCreateInfo viewInfo{};
viewInfo.sType = VK_STRUCTURE_TYPE_IMAGE_VIEW_CREATE_INFO; viewInfo.sType = VK_STRUCTURE_TYPE_IMAGE_VIEW_CREATE_INFO;
viewInfo.pNext = nullptr; viewInfo.pNext = nullptr;
viewInfo.flags = 0; viewInfo.flags = 0;
viewInfo.image = image; viewInfo.image = swapchainImage;
viewInfo.viewType = VK_IMAGE_VIEW_TYPE_2D; viewInfo.viewType = VK_IMAGE_VIEW_TYPE_2D;
viewInfo.format = sc->surfaceFormat.format; viewInfo.format = sc->surfaceFormat.format;
viewInfo.components.r = VK_COMPONENT_SWIZZLE_IDENTITY; viewInfo.components.r = VK_COMPONENT_SWIZZLE_IDENTITY;
@ -292,11 +259,59 @@ namespace engine {
viewInfo.subresourceRange.levelCount = 1; viewInfo.subresourceRange.levelCount = 1;
viewInfo.subresourceRange.baseArrayLayer = 0; viewInfo.subresourceRange.baseArrayLayer = 0;
viewInfo.subresourceRange.layerCount = 1; viewInfo.subresourceRange.layerCount = 1;
VkResult res = vkCreateImageView(sc->device, &viewInfo, nullptr, &imageView); VkResult res = vkCreateImageView(sc->device, &viewInfo, nullptr, &swapchainImageView);
if (res != VK_SUCCESS) throw std::runtime_error("Failed to create image view from swapchain image!"); if (res != VK_SUCCESS) throw std::runtime_error("Failed to create image view from swapchain image!");
/* create the depth buffer */
VkImageCreateInfo depthImageInfo{};
depthImageInfo.sType = VK_STRUCTURE_TYPE_IMAGE_CREATE_INFO;
depthImageInfo.pNext = nullptr;
depthImageInfo.flags = 0;
depthImageInfo.imageType = VK_IMAGE_TYPE_2D;
depthImageInfo.format = sc->depthStencilFormat;
depthImageInfo.extent.width = sc->extent.width;
depthImageInfo.extent.height = sc->extent.height;
depthImageInfo.extent.depth = 1;
depthImageInfo.mipLevels = 1;
depthImageInfo.arrayLayers = 1;
depthImageInfo.samples = VK_SAMPLE_COUNT_1_BIT;
depthImageInfo.tiling = VK_IMAGE_TILING_OPTIMAL;
depthImageInfo.usage = VK_IMAGE_USAGE_DEPTH_STENCIL_ATTACHMENT_BIT;
depthImageInfo.sharingMode = VK_SHARING_MODE_EXCLUSIVE;
depthImageInfo.queueFamilyIndexCount = 0; // ignored
depthImageInfo.pQueueFamilyIndices = nullptr; // ignored
depthImageInfo.initialLayout = VK_IMAGE_LAYOUT_UNDEFINED;
VmaAllocationCreateInfo depthAllocInfo{};
depthAllocInfo.usage = VMA_MEMORY_USAGE_AUTO_PREFER_DEVICE;
depthAllocInfo.flags = VMA_ALLOCATION_CREATE_DEDICATED_MEMORY_BIT;
depthAllocInfo.priority = 1.0f;
res = vmaCreateImage(sc->allocator, &depthImageInfo, &depthAllocInfo, &depthImage, &depthAllocation, nullptr);
if (res != VK_SUCCESS) throw std::runtime_error("Failed to create depth buffer image! Code: " + std::to_string(res));
VkImageViewCreateInfo depthViewInfo{};
depthViewInfo.sType = VK_STRUCTURE_TYPE_IMAGE_VIEW_CREATE_INFO;
depthViewInfo.pNext = nullptr;
depthViewInfo.flags = 0;
depthViewInfo.image = depthImage;
depthViewInfo.viewType = VK_IMAGE_VIEW_TYPE_2D;
depthViewInfo.format = sc->depthStencilFormat;
depthViewInfo.components.r = VK_COMPONENT_SWIZZLE_IDENTITY;
depthViewInfo.components.g = VK_COMPONENT_SWIZZLE_IDENTITY;
depthViewInfo.components.b = VK_COMPONENT_SWIZZLE_IDENTITY;
depthViewInfo.components.a = VK_COMPONENT_SWIZZLE_IDENTITY;
depthViewInfo.subresourceRange.aspectMask = VK_IMAGE_ASPECT_DEPTH_BIT;
depthViewInfo.subresourceRange.baseMipLevel = 0;
depthViewInfo.subresourceRange.levelCount = 1;
depthViewInfo.subresourceRange.baseArrayLayer = 0;
depthViewInfo.subresourceRange.layerCount = 1;
res = vkCreateImageView(info.device, &depthViewInfo, nullptr, &depthImageView);
assert(res == VK_SUCCESS);
std::array<VkImageView, 2> attachments { std::array<VkImageView, 2> attachments {
imageView, sc->depthStencil.view swapchainImageView, depthImageView
}; };
VkFramebufferCreateInfo fbInfo{}; VkFramebufferCreateInfo fbInfo{};
@ -304,7 +319,7 @@ namespace engine {
fbInfo.pNext = nullptr; fbInfo.pNext = nullptr;
fbInfo.flags = 0; fbInfo.flags = 0;
fbInfo.renderPass = sc->renderpass; fbInfo.renderPass = sc->renderpass;
fbInfo.attachmentCount = attachments.size(); fbInfo.attachmentCount = (uint32_t)attachments.size();
fbInfo.pAttachments = attachments.data(); fbInfo.pAttachments = attachments.data();
fbInfo.width = sc->extent.width; fbInfo.width = sc->extent.width;
fbInfo.height = sc->extent.height; fbInfo.height = sc->extent.height;
@ -319,13 +334,18 @@ namespace engine {
void destroySwapchain(const Swapchain& sc) void destroySwapchain(const Swapchain& sc)
{ {
for (const auto& [image, view, framebuffer] : sc.images) { for (VkFramebuffer framebuffer : sc.framebuffers) {
vkDestroyFramebuffer(sc.device, framebuffer, nullptr); vkDestroyFramebuffer(sc.device, framebuffer, nullptr);
}
for (const auto& [image, view] : sc.swapchainImages) {
vkDestroyImageView(sc.device, view, nullptr); vkDestroyImageView(sc.device, view, nullptr);
} }
for (const auto& [image, allocation, view] : sc.depthImages) {
vkDestroyImageView(sc.device, view, nullptr);
vmaDestroyImage(sc.allocator, image, allocation);
}
vkDestroyRenderPass(sc.device, sc.renderpass, nullptr); vkDestroyRenderPass(sc.device, sc.renderpass, nullptr);
vkDestroyImageView(sc.device, sc.depthStencil.view, nullptr);
vmaDestroyImage(sc.allocator, sc.depthStencil.image, sc.depthStencil.allocation);
vkDestroySwapchainKHR(sc.device, sc.swapchain, nullptr); vkDestroySwapchainKHR(sc.device, sc.swapchain, nullptr);
} }

View File

@ -11,6 +11,12 @@
namespace engine { namespace engine {
struct DepthStencil {
VkImage image = VK_NULL_HANDLE;
VmaAllocation allocation = VK_NULL_HANDLE;
VkImageView view = VK_NULL_HANDLE;
};
struct Swapchain { struct Swapchain {
VkSwapchainKHR swapchain = VK_NULL_HANDLE; VkSwapchainKHR swapchain = VK_NULL_HANDLE;
VkDevice device = VK_NULL_HANDLE; // the associated device VkDevice device = VK_NULL_HANDLE; // the associated device
@ -20,13 +26,10 @@ namespace engine {
VkPresentModeKHR presentMode{}; VkPresentModeKHR presentMode{};
VkExtent2D extent{}; VkExtent2D extent{};
VkRenderPass renderpass = VK_NULL_HANDLE; VkRenderPass renderpass = VK_NULL_HANDLE;
std::vector<std::tuple<VkImage, VkImageView, VkFramebuffer>> images{}; const VkFormat depthStencilFormat = VK_FORMAT_D16_UNORM;
struct DepthStencil { std::vector<std::pair<VkImage, VkImageView>> swapchainImages{};
VkImage image = VK_NULL_HANDLE; std::vector<DepthStencil> depthImages{};
VmaAllocation allocation = VK_NULL_HANDLE; std::vector<VkFramebuffer> framebuffers{};
VkImageView view = VK_NULL_HANDLE;
VkFormat format{};
} depthStencil{};
}; };
struct SwapchainInfo { struct SwapchainInfo {

View File

@ -161,6 +161,10 @@ void CameraControllerSystem::onUpdate(float ts)
m_scene->app()->window()->toggleFullscreen(); m_scene->app()->window()->toggleFullscreen();
} }
if (m_scene->app()->inputManager()->getButtonPress("exit")) {
m_scene->app()->window()->setCloseFlag();
}
c->justCollided = false; c->justCollided = false;
} }

View File

@ -21,10 +21,13 @@
#include "util/model_loader.hpp" #include "util/model_loader.hpp"
#include "game.hpp"
static void configureInputs(engine::InputManager* inputManager) static void configureInputs(engine::InputManager* inputManager)
{ {
// user interface mappings // user interface mappings
inputManager->addInputButton("fullscreen", engine::inputs::Key::K_F11); inputManager->addInputButton("fullscreen", engine::inputs::Key::K_F11);
inputManager->addInputButton("exit", engine::inputs::Key::K_ESCAPE);
// game buttons // game buttons
inputManager->addInputButton("fire", engine::inputs::MouseButton::M_LEFT); inputManager->addInputButton("fire", engine::inputs::MouseButton::M_LEFT);
inputManager->addInputButton("aim", engine::inputs::MouseButton::M_RIGHT); inputManager->addInputButton("aim", engine::inputs::MouseButton::M_RIGHT);
@ -38,17 +41,19 @@ static void configureInputs(engine::InputManager* inputManager)
inputManager->addInputAxis("looky", engine::inputs::MouseAxis::Y); inputManager->addInputAxis("looky", engine::inputs::MouseAxis::Y);
} }
void playGame(bool enableFrameLimiter) void playGame(GameSettings settings)
{ {
LOG_INFO("FPS limiter: {}", enableFrameLimiter ? "ON" : "OFF"); LOG_INFO("FPS limiter: {}", settings.enableFrameLimiter ? "ON" : "OFF");
LOG_INFO("Graphics Validation: {}", settings.enableValidation ? "ON" : "OFF");
engine::gfx::GraphicsSettings graphicsSettings{}; engine::gfx::GraphicsSettings graphicsSettings{};
graphicsSettings.enableValidation = settings.enableValidation;
graphicsSettings.vsync = true; graphicsSettings.vsync = true;
graphicsSettings.waitForPresent = false; graphicsSettings.waitForPresent = false;
graphicsSettings.msaaLevel = engine::gfx::MSAALevel::MSAA_OFF; graphicsSettings.msaaLevel = engine::gfx::MSAALevel::MSAA_OFF;
engine::Application app(PROJECT_NAME, PROJECT_VERSION, graphicsSettings); engine::Application app(PROJECT_NAME, PROJECT_VERSION, graphicsSettings);
app.setFrameLimiter(enableFrameLimiter); app.setFrameLimiter(settings.enableFrameLimiter);
// configure window // configure window
app.window()->setRelativeMouseMode(true); app.window()->setRelativeMouseMode(true);
@ -100,8 +105,8 @@ void playGame(bool enableFrameLimiter)
skyboxRenderable->material = std::make_unique<engine::resources::Material>(app.getResource<engine::resources::Shader>("builtin.skybox")); skyboxRenderable->material = std::make_unique<engine::resources::Material>(app.getResource<engine::resources::Shader>("builtin.skybox"));
skyboxRenderable->material->m_texture = spaceTexture; skyboxRenderable->material->m_texture = spaceTexture;
// skyboxRenderable->mesh = genSphereMesh(app.gfx(), 1.0f, 50, true); // skyboxRenderable->mesh = genSphereMesh(app.gfx(), 1.0f, 50, true);
skyboxRenderable->mesh = genCuboidMesh(app.gfx(), 2.0f, 2.0f, 2.0f, 1.0f, true); skyboxRenderable->mesh = genCuboidMesh(app.gfx(), 10.0f, 10.0f, 10.0f, 1.0f, true);
myScene->getComponent<engine::TransformComponent>(skybox)->position = { -1.0f, -1.0f, -1.0f }; myScene->getComponent<engine::TransformComponent>(skybox)->position = { -5.0f, -5.0f, -5.0f };
} }
/* cube */ /* cube */
@ -127,6 +132,8 @@ void playGame(bool enableFrameLimiter)
floorCollider->aabb = { { 0.0f, 0.0f, 0.0f }, { 10000.0f, 1.0f, 10000.0f } }; floorCollider->aabb = { { 0.0f, 0.0f, 0.0f }, { 10000.0f, 1.0f, 10000.0f } };
} }
//engine::util::loadMeshFromFile(myScene, app.getResourcePath("models/astronaut/astronaut.dae"));
app.gameLoop(); app.gameLoop();
} }

View File

@ -1,3 +1,8 @@
#pragma once #pragma once
void playGame(bool enableFrameLimiter); struct GameSettings {
bool enableFrameLimiter;
bool enableValidation;
};
void playGame(GameSettings settings);

View File

@ -7,14 +7,22 @@
// standard library // standard library
#include <exception> #include <exception>
#include <unordered_set>
#include <string.h> #include <string.h>
int main(int argc, char* argv[]) int main(int argc, char* argv[])
{ {
bool enableFrameLimiter = true; GameSettings settings{};
if (argc == 2) { settings.enableFrameLimiter = true;
if (strcmp(argv[1], "nofpslimit") == 0) enableFrameLimiter = false; settings.enableValidation = false;
if (argc >= 2) {
std::unordered_set<std::string> args{};
for (size_t i = 1; i < argc; i++) {
args.insert(std::string(argv[i]));
}
if (args.contains("nofpslimit")) settings.enableFrameLimiter = false;
if (args.contains("gpuvalidation")) settings.enableValidation = true;
} }
engine::setupLog(PROJECT_NAME); engine::setupLog(PROJECT_NAME);
@ -22,7 +30,7 @@ int main(int argc, char* argv[])
LOG_INFO("{} v{}", PROJECT_NAME, PROJECT_VERSION); LOG_INFO("{} v{}", PROJECT_NAME, PROJECT_VERSION);
try { try {
playGame(enableFrameLimiter); playGame(settings);
} }
catch (const std::exception& e) { catch (const std::exception& e) {
LOG_CRITICAL("{}", e.what()); LOG_CRITICAL("{}", e.what());