Initial procedural 3D engine setup
- Updated README.md with modern project structure and features - Cleaned up Android build files (not needed for desktop engine) - Restructured as procedural 3D engine with ImGui integration - Based on Sascha Willems Vulkan framework with dynamic rendering - Added comprehensive build instructions and camera system docs 🤖 Generated with [Claude Code](https://claude.ai/code) Co-Authored-By: Claude <noreply@anthropic.com>
This commit is contained in:
parent
ca9be0c589
commit
09ba229353
2429 changed files with 7751 additions and 112835 deletions
523
base/foundation/VulkanUIOverlay.cpp
Normal file
523
base/foundation/VulkanUIOverlay.cpp
Normal file
|
|
@ -0,0 +1,523 @@
|
|||
|
||||
/*
|
||||
* UI overlay class using ImGui
|
||||
*
|
||||
* Copyright (C) 2017-2024 by Sascha Willems - www.saschawillems.de
|
||||
*
|
||||
* This code is licensed under the MIT license (MIT) (http://opensource.org/licenses/MIT)
|
||||
*/
|
||||
|
||||
#include "VulkanUIOverlay.h"
|
||||
|
||||
namespace vks
|
||||
{
|
||||
UIOverlay::UIOverlay()
|
||||
{
|
||||
#if defined(__ANDROID__)
|
||||
if (vks::android::screenDensity >= ACONFIGURATION_DENSITY_XXHIGH) {
|
||||
scale = 3.5f;
|
||||
}
|
||||
else if (vks::android::screenDensity >= ACONFIGURATION_DENSITY_XHIGH) {
|
||||
scale = 2.5f;
|
||||
}
|
||||
else if (vks::android::screenDensity >= ACONFIGURATION_DENSITY_HIGH) {
|
||||
scale = 2.0f;
|
||||
};
|
||||
#endif
|
||||
|
||||
// Init ImGui
|
||||
ImGui::CreateContext();
|
||||
// Color scheme
|
||||
ImGuiStyle& style = ImGui::GetStyle();
|
||||
style.Colors[ImGuiCol_TitleBg] = ImVec4(1.0f, 0.0f, 0.0f, 1.0f);
|
||||
style.Colors[ImGuiCol_TitleBgActive] = ImVec4(1.0f, 0.0f, 0.0f, 1.0f);
|
||||
style.Colors[ImGuiCol_TitleBgCollapsed] = ImVec4(1.0f, 0.0f, 0.0f, 0.1f);
|
||||
style.Colors[ImGuiCol_MenuBarBg] = ImVec4(1.0f, 0.0f, 0.0f, 0.4f);
|
||||
style.Colors[ImGuiCol_Header] = ImVec4(0.8f, 0.0f, 0.0f, 0.4f);
|
||||
style.Colors[ImGuiCol_HeaderActive] = ImVec4(1.0f, 0.0f, 0.0f, 0.4f);
|
||||
style.Colors[ImGuiCol_HeaderHovered] = ImVec4(1.0f, 0.0f, 0.0f, 0.4f);
|
||||
style.Colors[ImGuiCol_FrameBg] = ImVec4(0.0f, 0.0f, 0.0f, 0.8f);
|
||||
style.Colors[ImGuiCol_CheckMark] = ImVec4(1.0f, 0.0f, 0.0f, 0.8f);
|
||||
style.Colors[ImGuiCol_SliderGrab] = ImVec4(1.0f, 0.0f, 0.0f, 0.4f);
|
||||
style.Colors[ImGuiCol_SliderGrabActive] = ImVec4(1.0f, 0.0f, 0.0f, 0.8f);
|
||||
style.Colors[ImGuiCol_FrameBgHovered] = ImVec4(1.0f, 1.0f, 1.0f, 0.1f);
|
||||
style.Colors[ImGuiCol_FrameBgActive] = ImVec4(1.0f, 1.0f, 1.0f, 0.2f);
|
||||
style.Colors[ImGuiCol_Button] = ImVec4(1.0f, 0.0f, 0.0f, 0.4f);
|
||||
style.Colors[ImGuiCol_ButtonHovered] = ImVec4(1.0f, 0.0f, 0.0f, 0.6f);
|
||||
style.Colors[ImGuiCol_ButtonActive] = ImVec4(1.0f, 0.0f, 0.0f, 0.8f);
|
||||
// Dimensions
|
||||
ImGuiIO& io = ImGui::GetIO();
|
||||
io.FontGlobalScale = scale;
|
||||
}
|
||||
|
||||
UIOverlay::~UIOverlay() {
|
||||
if (ImGui::GetCurrentContext()) {
|
||||
ImGui::DestroyContext();
|
||||
}
|
||||
}
|
||||
|
||||
/** Prepare all vulkan resources required to render the UI overlay */
|
||||
void UIOverlay::prepareResources()
|
||||
{
|
||||
ImGuiIO& io = ImGui::GetIO();
|
||||
|
||||
// Create font texture
|
||||
unsigned char* fontData;
|
||||
int texWidth, texHeight;
|
||||
#if defined(__ANDROID__)
|
||||
float scale = (float)vks::android::screenDensity / (float)ACONFIGURATION_DENSITY_MEDIUM;
|
||||
AAsset* asset = AAssetManager_open(androidApp->activity->assetManager, "Roboto-Medium.ttf", AASSET_MODE_STREAMING);
|
||||
if (asset) {
|
||||
size_t size = AAsset_getLength(asset);
|
||||
assert(size > 0);
|
||||
char *fontAsset = new char[size];
|
||||
AAsset_read(asset, fontAsset, size);
|
||||
AAsset_close(asset);
|
||||
io.Fonts->AddFontFromMemoryTTF(fontAsset, size, 14.0f * scale);
|
||||
delete[] fontAsset;
|
||||
}
|
||||
#else
|
||||
const std::string filename = getAssetPath() + "Roboto-Medium.ttf";
|
||||
io.Fonts->AddFontFromFileTTF(filename.c_str(), 16.0f * scale);
|
||||
#endif
|
||||
io.Fonts->GetTexDataAsRGBA32(&fontData, &texWidth, &texHeight);
|
||||
VkDeviceSize uploadSize = texWidth*texHeight * 4 * sizeof(char);
|
||||
|
||||
//SRS - Set ImGui style scale factor to handle retina and other HiDPI displays (same as font scaling above)
|
||||
ImGuiStyle& style = ImGui::GetStyle();
|
||||
style.ScaleAllSizes(scale);
|
||||
|
||||
// Create target image for copy
|
||||
VkImageCreateInfo imageInfo = vks::initializers::imageCreateInfo();
|
||||
imageInfo.imageType = VK_IMAGE_TYPE_2D;
|
||||
imageInfo.format = VK_FORMAT_R8G8B8A8_UNORM;
|
||||
imageInfo.extent.width = texWidth;
|
||||
imageInfo.extent.height = texHeight;
|
||||
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_SAMPLED_BIT | VK_IMAGE_USAGE_TRANSFER_DST_BIT;
|
||||
imageInfo.sharingMode = VK_SHARING_MODE_EXCLUSIVE;
|
||||
imageInfo.initialLayout = VK_IMAGE_LAYOUT_UNDEFINED;
|
||||
VK_CHECK_RESULT(vkCreateImage(device->logicalDevice, &imageInfo, nullptr, &fontImage));
|
||||
VkMemoryRequirements memReqs;
|
||||
vkGetImageMemoryRequirements(device->logicalDevice, fontImage, &memReqs);
|
||||
VkMemoryAllocateInfo memAllocInfo = vks::initializers::memoryAllocateInfo();
|
||||
memAllocInfo.allocationSize = memReqs.size;
|
||||
memAllocInfo.memoryTypeIndex = device->getMemoryType(memReqs.memoryTypeBits, VK_MEMORY_PROPERTY_DEVICE_LOCAL_BIT);
|
||||
VK_CHECK_RESULT(vkAllocateMemory(device->logicalDevice, &memAllocInfo, nullptr, &fontMemory));
|
||||
VK_CHECK_RESULT(vkBindImageMemory(device->logicalDevice, fontImage, fontMemory, 0));
|
||||
|
||||
// Image view
|
||||
VkImageViewCreateInfo viewInfo = vks::initializers::imageViewCreateInfo();
|
||||
viewInfo.image = fontImage;
|
||||
viewInfo.viewType = VK_IMAGE_VIEW_TYPE_2D;
|
||||
viewInfo.format = VK_FORMAT_R8G8B8A8_UNORM;
|
||||
viewInfo.subresourceRange.aspectMask = VK_IMAGE_ASPECT_COLOR_BIT;
|
||||
viewInfo.subresourceRange.levelCount = 1;
|
||||
viewInfo.subresourceRange.layerCount = 1;
|
||||
VK_CHECK_RESULT(vkCreateImageView(device->logicalDevice, &viewInfo, nullptr, &fontView));
|
||||
|
||||
// Staging buffers for font data upload
|
||||
vks::Buffer stagingBuffer;
|
||||
|
||||
VK_CHECK_RESULT(device->createBuffer(
|
||||
VK_BUFFER_USAGE_TRANSFER_SRC_BIT,
|
||||
VK_MEMORY_PROPERTY_HOST_VISIBLE_BIT | VK_MEMORY_PROPERTY_HOST_COHERENT_BIT,
|
||||
&stagingBuffer,
|
||||
uploadSize));
|
||||
|
||||
stagingBuffer.map();
|
||||
memcpy(stagingBuffer.mapped, fontData, uploadSize);
|
||||
stagingBuffer.unmap();
|
||||
|
||||
// Copy buffer data to font image
|
||||
VkCommandBuffer copyCmd = device->createCommandBuffer(VK_COMMAND_BUFFER_LEVEL_PRIMARY, true);
|
||||
|
||||
// Prepare for transfer
|
||||
vks::tools::setImageLayout(
|
||||
copyCmd,
|
||||
fontImage,
|
||||
VK_IMAGE_ASPECT_COLOR_BIT,
|
||||
VK_IMAGE_LAYOUT_UNDEFINED,
|
||||
VK_IMAGE_LAYOUT_TRANSFER_DST_OPTIMAL,
|
||||
VK_PIPELINE_STAGE_HOST_BIT,
|
||||
VK_PIPELINE_STAGE_TRANSFER_BIT);
|
||||
|
||||
// Copy
|
||||
VkBufferImageCopy bufferCopyRegion = {};
|
||||
bufferCopyRegion.imageSubresource.aspectMask = VK_IMAGE_ASPECT_COLOR_BIT;
|
||||
bufferCopyRegion.imageSubresource.layerCount = 1;
|
||||
bufferCopyRegion.imageExtent.width = texWidth;
|
||||
bufferCopyRegion.imageExtent.height = texHeight;
|
||||
bufferCopyRegion.imageExtent.depth = 1;
|
||||
|
||||
vkCmdCopyBufferToImage(
|
||||
copyCmd,
|
||||
stagingBuffer.buffer,
|
||||
fontImage,
|
||||
VK_IMAGE_LAYOUT_TRANSFER_DST_OPTIMAL,
|
||||
1,
|
||||
&bufferCopyRegion
|
||||
);
|
||||
|
||||
// Prepare for shader read
|
||||
vks::tools::setImageLayout(
|
||||
copyCmd,
|
||||
fontImage,
|
||||
VK_IMAGE_ASPECT_COLOR_BIT,
|
||||
VK_IMAGE_LAYOUT_TRANSFER_DST_OPTIMAL,
|
||||
VK_IMAGE_LAYOUT_SHADER_READ_ONLY_OPTIMAL,
|
||||
VK_PIPELINE_STAGE_TRANSFER_BIT,
|
||||
VK_PIPELINE_STAGE_FRAGMENT_SHADER_BIT);
|
||||
|
||||
device->flushCommandBuffer(copyCmd, queue, true);
|
||||
|
||||
stagingBuffer.destroy();
|
||||
|
||||
// Font texture Sampler
|
||||
VkSamplerCreateInfo samplerInfo = vks::initializers::samplerCreateInfo();
|
||||
samplerInfo.magFilter = VK_FILTER_LINEAR;
|
||||
samplerInfo.minFilter = VK_FILTER_LINEAR;
|
||||
samplerInfo.mipmapMode = VK_SAMPLER_MIPMAP_MODE_LINEAR;
|
||||
samplerInfo.addressModeU = VK_SAMPLER_ADDRESS_MODE_CLAMP_TO_EDGE;
|
||||
samplerInfo.addressModeV = VK_SAMPLER_ADDRESS_MODE_CLAMP_TO_EDGE;
|
||||
samplerInfo.addressModeW = VK_SAMPLER_ADDRESS_MODE_CLAMP_TO_EDGE;
|
||||
samplerInfo.borderColor = VK_BORDER_COLOR_FLOAT_OPAQUE_WHITE;
|
||||
VK_CHECK_RESULT(vkCreateSampler(device->logicalDevice, &samplerInfo, nullptr, &sampler));
|
||||
|
||||
// Descriptor pool
|
||||
std::vector<VkDescriptorPoolSize> poolSizes = {
|
||||
vks::initializers::descriptorPoolSize(VK_DESCRIPTOR_TYPE_COMBINED_IMAGE_SAMPLER, 1)
|
||||
};
|
||||
VkDescriptorPoolCreateInfo descriptorPoolInfo = vks::initializers::descriptorPoolCreateInfo(poolSizes, 2);
|
||||
VK_CHECK_RESULT(vkCreateDescriptorPool(device->logicalDevice, &descriptorPoolInfo, nullptr, &descriptorPool));
|
||||
|
||||
// Descriptor set layout
|
||||
std::vector<VkDescriptorSetLayoutBinding> setLayoutBindings = {
|
||||
vks::initializers::descriptorSetLayoutBinding(VK_DESCRIPTOR_TYPE_COMBINED_IMAGE_SAMPLER, VK_SHADER_STAGE_FRAGMENT_BIT, 0),
|
||||
};
|
||||
VkDescriptorSetLayoutCreateInfo descriptorLayout = vks::initializers::descriptorSetLayoutCreateInfo(setLayoutBindings);
|
||||
VK_CHECK_RESULT(vkCreateDescriptorSetLayout(device->logicalDevice, &descriptorLayout, nullptr, &descriptorSetLayout));
|
||||
|
||||
// Descriptor set
|
||||
VkDescriptorSetAllocateInfo allocInfo = vks::initializers::descriptorSetAllocateInfo(descriptorPool, &descriptorSetLayout, 1);
|
||||
VK_CHECK_RESULT(vkAllocateDescriptorSets(device->logicalDevice, &allocInfo, &descriptorSet));
|
||||
VkDescriptorImageInfo fontDescriptor = vks::initializers::descriptorImageInfo(
|
||||
sampler,
|
||||
fontView,
|
||||
VK_IMAGE_LAYOUT_SHADER_READ_ONLY_OPTIMAL
|
||||
);
|
||||
std::vector<VkWriteDescriptorSet> writeDescriptorSets = {
|
||||
vks::initializers::writeDescriptorSet(descriptorSet, VK_DESCRIPTOR_TYPE_COMBINED_IMAGE_SAMPLER, 0, &fontDescriptor)
|
||||
};
|
||||
vkUpdateDescriptorSets(device->logicalDevice, static_cast<uint32_t>(writeDescriptorSets.size()), writeDescriptorSets.data(), 0, nullptr);
|
||||
}
|
||||
|
||||
/** Prepare a separate pipeline for the UI overlay rendering decoupled from the main application */
|
||||
void UIOverlay::preparePipeline(const VkPipelineCache pipelineCache, const VkRenderPass renderPass, const VkFormat colorFormat, const VkFormat depthFormat)
|
||||
{
|
||||
// Pipeline layout
|
||||
// Push constants for UI rendering parameters
|
||||
VkPushConstantRange pushConstantRange = vks::initializers::pushConstantRange(VK_SHADER_STAGE_VERTEX_BIT, sizeof(PushConstBlock), 0);
|
||||
VkPipelineLayoutCreateInfo pipelineLayoutCreateInfo = vks::initializers::pipelineLayoutCreateInfo(&descriptorSetLayout, 1);
|
||||
pipelineLayoutCreateInfo.pushConstantRangeCount = 1;
|
||||
pipelineLayoutCreateInfo.pPushConstantRanges = &pushConstantRange;
|
||||
VK_CHECK_RESULT(vkCreatePipelineLayout(device->logicalDevice, &pipelineLayoutCreateInfo, nullptr, &pipelineLayout));
|
||||
|
||||
// Setup graphics pipeline for UI rendering
|
||||
VkPipelineInputAssemblyStateCreateInfo inputAssemblyState =
|
||||
vks::initializers::pipelineInputAssemblyStateCreateInfo(VK_PRIMITIVE_TOPOLOGY_TRIANGLE_LIST, 0, VK_FALSE);
|
||||
|
||||
VkPipelineRasterizationStateCreateInfo rasterizationState =
|
||||
vks::initializers::pipelineRasterizationStateCreateInfo(VK_POLYGON_MODE_FILL, VK_CULL_MODE_NONE, VK_FRONT_FACE_COUNTER_CLOCKWISE);
|
||||
|
||||
// Enable blending
|
||||
VkPipelineColorBlendAttachmentState blendAttachmentState{};
|
||||
blendAttachmentState.blendEnable = VK_TRUE;
|
||||
blendAttachmentState.colorWriteMask = VK_COLOR_COMPONENT_R_BIT | VK_COLOR_COMPONENT_G_BIT | VK_COLOR_COMPONENT_B_BIT | VK_COLOR_COMPONENT_A_BIT;
|
||||
blendAttachmentState.srcColorBlendFactor = VK_BLEND_FACTOR_SRC_ALPHA;
|
||||
blendAttachmentState.dstColorBlendFactor = VK_BLEND_FACTOR_ONE_MINUS_SRC_ALPHA;
|
||||
blendAttachmentState.colorBlendOp = VK_BLEND_OP_ADD;
|
||||
blendAttachmentState.srcAlphaBlendFactor = VK_BLEND_FACTOR_ONE_MINUS_SRC_ALPHA;
|
||||
blendAttachmentState.dstAlphaBlendFactor = VK_BLEND_FACTOR_ZERO;
|
||||
blendAttachmentState.alphaBlendOp = VK_BLEND_OP_ADD;
|
||||
|
||||
VkPipelineColorBlendStateCreateInfo colorBlendState =
|
||||
vks::initializers::pipelineColorBlendStateCreateInfo(1, &blendAttachmentState);
|
||||
|
||||
VkPipelineDepthStencilStateCreateInfo depthStencilState =
|
||||
vks::initializers::pipelineDepthStencilStateCreateInfo(VK_FALSE, VK_FALSE, VK_COMPARE_OP_ALWAYS);
|
||||
|
||||
VkPipelineViewportStateCreateInfo viewportState =
|
||||
vks::initializers::pipelineViewportStateCreateInfo(1, 1, 0);
|
||||
|
||||
VkPipelineMultisampleStateCreateInfo multisampleState =
|
||||
vks::initializers::pipelineMultisampleStateCreateInfo(rasterizationSamples);
|
||||
|
||||
std::vector<VkDynamicState> dynamicStateEnables = {
|
||||
VK_DYNAMIC_STATE_VIEWPORT,
|
||||
VK_DYNAMIC_STATE_SCISSOR
|
||||
};
|
||||
VkPipelineDynamicStateCreateInfo dynamicState =
|
||||
vks::initializers::pipelineDynamicStateCreateInfo(dynamicStateEnables);
|
||||
|
||||
VkGraphicsPipelineCreateInfo pipelineCreateInfo = vks::initializers::pipelineCreateInfo(pipelineLayout, renderPass);
|
||||
|
||||
pipelineCreateInfo.pInputAssemblyState = &inputAssemblyState;
|
||||
pipelineCreateInfo.pRasterizationState = &rasterizationState;
|
||||
pipelineCreateInfo.pColorBlendState = &colorBlendState;
|
||||
pipelineCreateInfo.pMultisampleState = &multisampleState;
|
||||
pipelineCreateInfo.pViewportState = &viewportState;
|
||||
pipelineCreateInfo.pDepthStencilState = &depthStencilState;
|
||||
pipelineCreateInfo.pDynamicState = &dynamicState;
|
||||
pipelineCreateInfo.stageCount = static_cast<uint32_t>(shaders.size());
|
||||
pipelineCreateInfo.pStages = shaders.data();
|
||||
pipelineCreateInfo.subpass = subpass;
|
||||
|
||||
#if defined(VK_KHR_dynamic_rendering)
|
||||
// SRS - if we are using dynamic rendering (i.e. renderPass null), must define color, depth and stencil attachments at pipeline create time
|
||||
VkPipelineRenderingCreateInfo pipelineRenderingCreateInfo = {};
|
||||
if (renderPass == VK_NULL_HANDLE) {
|
||||
pipelineRenderingCreateInfo.sType = VK_STRUCTURE_TYPE_PIPELINE_RENDERING_CREATE_INFO;
|
||||
pipelineRenderingCreateInfo.colorAttachmentCount = 1;
|
||||
pipelineRenderingCreateInfo.pColorAttachmentFormats = &colorFormat;
|
||||
pipelineRenderingCreateInfo.depthAttachmentFormat = depthFormat;
|
||||
pipelineRenderingCreateInfo.stencilAttachmentFormat = depthFormat;
|
||||
pipelineCreateInfo.pNext = &pipelineRenderingCreateInfo;
|
||||
}
|
||||
#endif
|
||||
|
||||
// Vertex bindings an attributes based on ImGui vertex definition
|
||||
std::vector<VkVertexInputBindingDescription> vertexInputBindings = {
|
||||
vks::initializers::vertexInputBindingDescription(0, sizeof(ImDrawVert), VK_VERTEX_INPUT_RATE_VERTEX),
|
||||
};
|
||||
std::vector<VkVertexInputAttributeDescription> vertexInputAttributes = {
|
||||
vks::initializers::vertexInputAttributeDescription(0, 0, VK_FORMAT_R32G32_SFLOAT, offsetof(ImDrawVert, pos)), // Location 0: Position
|
||||
vks::initializers::vertexInputAttributeDescription(0, 1, VK_FORMAT_R32G32_SFLOAT, offsetof(ImDrawVert, uv)), // Location 1: UV
|
||||
vks::initializers::vertexInputAttributeDescription(0, 2, VK_FORMAT_R8G8B8A8_UNORM, offsetof(ImDrawVert, col)), // Location 0: Color
|
||||
};
|
||||
VkPipelineVertexInputStateCreateInfo vertexInputState = vks::initializers::pipelineVertexInputStateCreateInfo();
|
||||
vertexInputState.vertexBindingDescriptionCount = static_cast<uint32_t>(vertexInputBindings.size());
|
||||
vertexInputState.pVertexBindingDescriptions = vertexInputBindings.data();
|
||||
vertexInputState.vertexAttributeDescriptionCount = static_cast<uint32_t>(vertexInputAttributes.size());
|
||||
vertexInputState.pVertexAttributeDescriptions = vertexInputAttributes.data();
|
||||
|
||||
pipelineCreateInfo.pVertexInputState = &vertexInputState;
|
||||
|
||||
VK_CHECK_RESULT(vkCreateGraphicsPipelines(device->logicalDevice, pipelineCache, 1, &pipelineCreateInfo, nullptr, &pipeline));
|
||||
}
|
||||
|
||||
/** Update vertex and index buffer containing the imGui elements when required */
|
||||
bool UIOverlay::update()
|
||||
{
|
||||
ImDrawData* imDrawData = ImGui::GetDrawData();
|
||||
bool updateCmdBuffers = false;
|
||||
|
||||
if (!imDrawData) { return false; };
|
||||
|
||||
// Note: Alignment is done inside buffer creation
|
||||
VkDeviceSize vertexBufferSize = imDrawData->TotalVtxCount * sizeof(ImDrawVert);
|
||||
VkDeviceSize indexBufferSize = imDrawData->TotalIdxCount * sizeof(ImDrawIdx);
|
||||
|
||||
// Update buffers only if vertex or index count has been changed compared to current buffer size
|
||||
if ((vertexBufferSize == 0) || (indexBufferSize == 0)) {
|
||||
return false;
|
||||
}
|
||||
|
||||
// Vertex buffer
|
||||
if ((vertexBuffer.buffer == VK_NULL_HANDLE) || (vertexCount != imDrawData->TotalVtxCount)) {
|
||||
vertexBuffer.unmap();
|
||||
vertexBuffer.destroy();
|
||||
VK_CHECK_RESULT(device->createBuffer(VK_BUFFER_USAGE_VERTEX_BUFFER_BIT, VK_MEMORY_PROPERTY_HOST_VISIBLE_BIT, &vertexBuffer, vertexBufferSize));
|
||||
vertexCount = imDrawData->TotalVtxCount;
|
||||
vertexBuffer.unmap();
|
||||
vertexBuffer.map();
|
||||
updateCmdBuffers = true;
|
||||
}
|
||||
|
||||
// Index buffer
|
||||
if ((indexBuffer.buffer == VK_NULL_HANDLE) || (indexCount < imDrawData->TotalIdxCount)) {
|
||||
indexBuffer.unmap();
|
||||
indexBuffer.destroy();
|
||||
VK_CHECK_RESULT(device->createBuffer(VK_BUFFER_USAGE_INDEX_BUFFER_BIT, VK_MEMORY_PROPERTY_HOST_VISIBLE_BIT, &indexBuffer, indexBufferSize));
|
||||
indexCount = imDrawData->TotalIdxCount;
|
||||
indexBuffer.map();
|
||||
updateCmdBuffers = true;
|
||||
}
|
||||
|
||||
// Upload data
|
||||
ImDrawVert* vtxDst = (ImDrawVert*)vertexBuffer.mapped;
|
||||
ImDrawIdx* idxDst = (ImDrawIdx*)indexBuffer.mapped;
|
||||
|
||||
for (int n = 0; n < imDrawData->CmdListsCount; n++) {
|
||||
const ImDrawList* cmd_list = imDrawData->CmdLists[n];
|
||||
memcpy(vtxDst, cmd_list->VtxBuffer.Data, cmd_list->VtxBuffer.Size * sizeof(ImDrawVert));
|
||||
memcpy(idxDst, cmd_list->IdxBuffer.Data, cmd_list->IdxBuffer.Size * sizeof(ImDrawIdx));
|
||||
vtxDst += cmd_list->VtxBuffer.Size;
|
||||
idxDst += cmd_list->IdxBuffer.Size;
|
||||
}
|
||||
|
||||
// Flush to make writes visible to GPU
|
||||
vertexBuffer.flush();
|
||||
indexBuffer.flush();
|
||||
|
||||
return updateCmdBuffers;
|
||||
}
|
||||
|
||||
void UIOverlay::draw(const VkCommandBuffer commandBuffer)
|
||||
{
|
||||
ImDrawData* imDrawData = ImGui::GetDrawData();
|
||||
int32_t vertexOffset = 0;
|
||||
int32_t indexOffset = 0;
|
||||
|
||||
if ((!imDrawData) || (imDrawData->CmdListsCount == 0)) {
|
||||
return;
|
||||
}
|
||||
|
||||
ImGuiIO& io = ImGui::GetIO();
|
||||
|
||||
vkCmdBindPipeline(commandBuffer, VK_PIPELINE_BIND_POINT_GRAPHICS, pipeline);
|
||||
vkCmdBindDescriptorSets(commandBuffer, VK_PIPELINE_BIND_POINT_GRAPHICS, pipelineLayout, 0, 1, &descriptorSet, 0, NULL);
|
||||
|
||||
pushConstBlock.scale = glm::vec2(2.0f / io.DisplaySize.x, 2.0f / io.DisplaySize.y);
|
||||
pushConstBlock.translate = glm::vec2(-1.0f);
|
||||
vkCmdPushConstants(commandBuffer, pipelineLayout, VK_SHADER_STAGE_VERTEX_BIT, 0, sizeof(PushConstBlock), &pushConstBlock);
|
||||
|
||||
VkDeviceSize offsets[1] = { 0 };
|
||||
vkCmdBindVertexBuffers(commandBuffer, 0, 1, &vertexBuffer.buffer, offsets);
|
||||
vkCmdBindIndexBuffer(commandBuffer, indexBuffer.buffer, 0, VK_INDEX_TYPE_UINT16);
|
||||
|
||||
for (int32_t i = 0; i < imDrawData->CmdListsCount; i++)
|
||||
{
|
||||
const ImDrawList* cmd_list = imDrawData->CmdLists[i];
|
||||
for (int32_t j = 0; j < cmd_list->CmdBuffer.Size; j++)
|
||||
{
|
||||
const ImDrawCmd* pcmd = &cmd_list->CmdBuffer[j];
|
||||
VkRect2D scissorRect;
|
||||
scissorRect.offset.x = std::max((int32_t)(pcmd->ClipRect.x), 0);
|
||||
scissorRect.offset.y = std::max((int32_t)(pcmd->ClipRect.y), 0);
|
||||
scissorRect.extent.width = (uint32_t)(pcmd->ClipRect.z - pcmd->ClipRect.x);
|
||||
scissorRect.extent.height = (uint32_t)(pcmd->ClipRect.w - pcmd->ClipRect.y);
|
||||
vkCmdSetScissor(commandBuffer, 0, 1, &scissorRect);
|
||||
vkCmdDrawIndexed(commandBuffer, pcmd->ElemCount, 1, indexOffset, vertexOffset, 0);
|
||||
indexOffset += pcmd->ElemCount;
|
||||
}
|
||||
#if (defined(VK_USE_PLATFORM_IOS_MVK) || defined(VK_USE_PLATFORM_METAL_EXT)) && TARGET_OS_SIMULATOR
|
||||
// Apple Device Simulator does not support vkCmdDrawIndexed() with vertexOffset > 0, so rebind vertex buffer instead
|
||||
offsets[0] += cmd_list->VtxBuffer.Size * sizeof(ImDrawVert);
|
||||
vkCmdBindVertexBuffers(commandBuffer, 0, 1, &vertexBuffer.buffer, offsets);
|
||||
#else
|
||||
vertexOffset += cmd_list->VtxBuffer.Size;
|
||||
#endif
|
||||
}
|
||||
}
|
||||
|
||||
void UIOverlay::resize(uint32_t width, uint32_t height)
|
||||
{
|
||||
ImGuiIO& io = ImGui::GetIO();
|
||||
io.DisplaySize = ImVec2((float)(width), (float)(height));
|
||||
}
|
||||
|
||||
void UIOverlay::freeResources()
|
||||
{
|
||||
vertexBuffer.destroy();
|
||||
indexBuffer.destroy();
|
||||
vkDestroyImageView(device->logicalDevice, fontView, nullptr);
|
||||
vkDestroyImage(device->logicalDevice, fontImage, nullptr);
|
||||
vkFreeMemory(device->logicalDevice, fontMemory, nullptr);
|
||||
vkDestroySampler(device->logicalDevice, sampler, nullptr);
|
||||
vkDestroyDescriptorSetLayout(device->logicalDevice, descriptorSetLayout, nullptr);
|
||||
vkDestroyDescriptorPool(device->logicalDevice, descriptorPool, nullptr);
|
||||
vkDestroyPipelineLayout(device->logicalDevice, pipelineLayout, nullptr);
|
||||
vkDestroyPipeline(device->logicalDevice, pipeline, nullptr);
|
||||
}
|
||||
|
||||
bool UIOverlay::header(const char *caption)
|
||||
{
|
||||
return ImGui::CollapsingHeader(caption, ImGuiTreeNodeFlags_DefaultOpen);
|
||||
}
|
||||
|
||||
bool UIOverlay::checkBox(const char *caption, bool *value)
|
||||
{
|
||||
bool res = ImGui::Checkbox(caption, value);
|
||||
if (res) { updated = true; };
|
||||
return res;
|
||||
}
|
||||
|
||||
bool UIOverlay::checkBox(const char *caption, int32_t *value)
|
||||
{
|
||||
bool val = (*value == 1);
|
||||
bool res = ImGui::Checkbox(caption, &val);
|
||||
*value = val;
|
||||
if (res) { updated = true; };
|
||||
return res;
|
||||
}
|
||||
|
||||
bool UIOverlay::radioButton(const char* caption, bool value)
|
||||
{
|
||||
bool res = ImGui::RadioButton(caption, value);
|
||||
if (res) { updated = true; };
|
||||
return res;
|
||||
}
|
||||
|
||||
bool UIOverlay::inputFloat(const char *caption, float *value, float step, uint32_t precision)
|
||||
{
|
||||
bool res = ImGui::InputFloat(caption, value, step, step * 10.0f, precision);
|
||||
if (res) { updated = true; };
|
||||
return res;
|
||||
}
|
||||
|
||||
bool UIOverlay::sliderFloat(const char* caption, float* value, float min, float max)
|
||||
{
|
||||
bool res = ImGui::SliderFloat(caption, value, min, max);
|
||||
if (res) { updated = true; };
|
||||
return res;
|
||||
}
|
||||
|
||||
bool UIOverlay::sliderInt(const char* caption, int32_t* value, int32_t min, int32_t max)
|
||||
{
|
||||
bool res = ImGui::SliderInt(caption, value, min, max);
|
||||
if (res) { updated = true; };
|
||||
return res;
|
||||
}
|
||||
|
||||
bool UIOverlay::comboBox(const char *caption, int32_t *itemindex, std::vector<std::string> items)
|
||||
{
|
||||
if (items.empty()) {
|
||||
return false;
|
||||
}
|
||||
std::vector<const char*> charitems;
|
||||
charitems.reserve(items.size());
|
||||
for (size_t i = 0; i < items.size(); i++) {
|
||||
charitems.push_back(items[i].c_str());
|
||||
}
|
||||
uint32_t itemCount = static_cast<uint32_t>(charitems.size());
|
||||
bool res = ImGui::Combo(caption, itemindex, &charitems[0], itemCount, itemCount);
|
||||
if (res) { updated = true; };
|
||||
return res;
|
||||
}
|
||||
|
||||
bool UIOverlay::button(const char *caption)
|
||||
{
|
||||
bool res = ImGui::Button(caption);
|
||||
if (res) { updated = true; };
|
||||
return res;
|
||||
}
|
||||
|
||||
bool UIOverlay::colorPicker(const char* caption, float* color) {
|
||||
bool res = ImGui::ColorEdit4(caption, color, ImGuiColorEditFlags_NoInputs);
|
||||
if (res) { updated = true; };
|
||||
return res;
|
||||
}
|
||||
|
||||
void UIOverlay::text(const char *formatstr, ...)
|
||||
{
|
||||
va_list args;
|
||||
va_start(args, formatstr);
|
||||
ImGui::TextV(formatstr, args);
|
||||
va_end(args);
|
||||
}
|
||||
}
|
||||
98
base/foundation/VulkanUIOverlay.h
Normal file
98
base/foundation/VulkanUIOverlay.h
Normal file
|
|
@ -0,0 +1,98 @@
|
|||
/*
|
||||
* UI overlay class using ImGui
|
||||
*
|
||||
* Copyright (C) 2017-2024 by Sascha Willems - www.saschawillems.de
|
||||
*
|
||||
* This code is licensed under the MIT license (MIT) (http://opensource.org/licenses/MIT)
|
||||
*/
|
||||
|
||||
#pragma once
|
||||
|
||||
#include <stdio.h>
|
||||
#include <stdlib.h>
|
||||
#include <string.h>
|
||||
#include <assert.h>
|
||||
#include <vector>
|
||||
#include <sstream>
|
||||
#include <iomanip>
|
||||
|
||||
#include <vulkan/vulkan.h>
|
||||
#include "../core/VulkanTools.h"
|
||||
#include "../core/VulkanDebug.h"
|
||||
#include "../memory/VulkanBuffer.h"
|
||||
#include "../device/VulkanDevice.h"
|
||||
|
||||
#include "../external/imgui/imgui.h"
|
||||
|
||||
#if defined(__ANDROID__)
|
||||
#include "VulkanAndroid.h"
|
||||
#endif
|
||||
|
||||
#if defined(__APPLE__)
|
||||
#include <TargetConditionals.h>
|
||||
#endif
|
||||
|
||||
namespace vks
|
||||
{
|
||||
class UIOverlay
|
||||
{
|
||||
public:
|
||||
vks::VulkanDevice* device{ nullptr };
|
||||
VkQueue queue{ VK_NULL_HANDLE };
|
||||
|
||||
VkSampleCountFlagBits rasterizationSamples{ VK_SAMPLE_COUNT_1_BIT };
|
||||
uint32_t subpass{ 0 };
|
||||
|
||||
vks::Buffer vertexBuffer;
|
||||
vks::Buffer indexBuffer;
|
||||
int32_t vertexCount{ 0 };
|
||||
int32_t indexCount{ 0 };
|
||||
|
||||
std::vector<VkPipelineShaderStageCreateInfo> shaders;
|
||||
|
||||
VkDescriptorPool descriptorPool{ VK_NULL_HANDLE };
|
||||
VkDescriptorSetLayout descriptorSetLayout{ VK_NULL_HANDLE };
|
||||
VkDescriptorSet descriptorSet{ VK_NULL_HANDLE };
|
||||
VkPipelineLayout pipelineLayout{ VK_NULL_HANDLE };
|
||||
VkPipeline pipeline{ VK_NULL_HANDLE };
|
||||
|
||||
VkDeviceMemory fontMemory{ VK_NULL_HANDLE };
|
||||
VkImage fontImage{ VK_NULL_HANDLE };
|
||||
VkImageView fontView{ VK_NULL_HANDLE };
|
||||
VkSampler sampler{ VK_NULL_HANDLE };
|
||||
|
||||
struct PushConstBlock {
|
||||
glm::vec2 scale;
|
||||
glm::vec2 translate;
|
||||
} pushConstBlock;
|
||||
|
||||
bool visible{ true };
|
||||
bool updated{ false };
|
||||
float scale{ 1.0f };
|
||||
float updateTimer{ 0.0f };
|
||||
|
||||
UIOverlay();
|
||||
~UIOverlay();
|
||||
|
||||
void preparePipeline(const VkPipelineCache pipelineCache, const VkRenderPass renderPass, const VkFormat colorFormat, const VkFormat depthFormat);
|
||||
void prepareResources();
|
||||
|
||||
bool update();
|
||||
void draw(const VkCommandBuffer commandBuffer);
|
||||
void resize(uint32_t width, uint32_t height);
|
||||
|
||||
void freeResources();
|
||||
|
||||
bool header(const char* caption);
|
||||
bool checkBox(const char* caption, bool* value);
|
||||
bool checkBox(const char* caption, int32_t* value);
|
||||
bool radioButton(const char* caption, bool value);
|
||||
bool inputFloat(const char* caption, float* value, float step, uint32_t precision);
|
||||
bool sliderFloat(const char* caption, float* value, float min, float max);
|
||||
bool sliderInt(const char* caption, int32_t* value, int32_t min, int32_t max);
|
||||
bool comboBox(const char* caption, int32_t* itemindex, std::vector<std::string> items);
|
||||
bool button(const char* caption);
|
||||
bool colorPicker(const char* caption, float* color);
|
||||
void text(const char* formatstr, ...);
|
||||
};
|
||||
}
|
||||
3338
base/foundation/vulkanexamplebase.cpp
Normal file
3338
base/foundation/vulkanexamplebase.cpp
Normal file
File diff suppressed because it is too large
Load diff
423
base/foundation/vulkanexamplebase.h
Normal file
423
base/foundation/vulkanexamplebase.h
Normal file
|
|
@ -0,0 +1,423 @@
|
|||
/*
|
||||
* Vulkan Example base class
|
||||
*
|
||||
* Copyright (C) 2016-2025 by Sascha Willems - www.saschawillems.de
|
||||
*
|
||||
* This code is licensed under the MIT license (MIT) (http://opensource.org/licenses/MIT)
|
||||
*/
|
||||
|
||||
#pragma once
|
||||
|
||||
#ifdef _WIN32
|
||||
// Note: Removed /subsystem:windows pragma to allow console applications
|
||||
// #pragma comment(linker, "/subsystem:windows")
|
||||
#include <windows.h>
|
||||
#include <fcntl.h>
|
||||
#include <io.h>
|
||||
#include <ShellScalingAPI.h>
|
||||
#elif defined(VK_USE_PLATFORM_ANDROID_KHR)
|
||||
#include <android/native_activity.h>
|
||||
#include <android/asset_manager.h>
|
||||
#include <android_native_app_glue.h>
|
||||
#include <sys/system_properties.h>
|
||||
#include "VulkanAndroid.h"
|
||||
#elif defined(VK_USE_PLATFORM_DIRECTFB_EXT)
|
||||
#include <directfb.h>
|
||||
#elif defined(VK_USE_PLATFORM_WAYLAND_KHR)
|
||||
#include <wayland-client.h>
|
||||
#include "xdg-shell-client-protocol.h"
|
||||
#elif defined(_DIRECT2DISPLAY)
|
||||
//
|
||||
#elif defined(VK_USE_PLATFORM_XCB_KHR)
|
||||
#include <xcb/xcb.h>
|
||||
#elif (defined(VK_USE_PLATFORM_IOS_MVK) || defined(VK_USE_PLATFORM_MACOS_MVK) || defined(VK_USE_PLATFORM_METAL_EXT))
|
||||
#include <TargetConditionals.h>
|
||||
#endif
|
||||
|
||||
#include <stdio.h>
|
||||
#include <stdlib.h>
|
||||
#include <string.h>
|
||||
#include <assert.h>
|
||||
#include <vector>
|
||||
#include <array>
|
||||
#include <unordered_map>
|
||||
#include <numeric>
|
||||
#include <ctime>
|
||||
#include <iostream>
|
||||
#include <chrono>
|
||||
#include <random>
|
||||
#include <algorithm>
|
||||
#include <sys/stat.h>
|
||||
|
||||
#define GLM_FORCE_RADIANS
|
||||
#define GLM_FORCE_DEPTH_ZERO_TO_ONE
|
||||
#define GLM_ENABLE_EXPERIMENTAL
|
||||
#include <glm/glm.hpp>
|
||||
#include <glm/gtc/matrix_transform.hpp>
|
||||
#include <glm/gtc/matrix_inverse.hpp>
|
||||
#include <glm/gtc/type_ptr.hpp>
|
||||
#include <string>
|
||||
#include <numeric>
|
||||
#include <array>
|
||||
|
||||
#include "vulkan/vulkan.h"
|
||||
|
||||
#include "CommandLineParser.hpp"
|
||||
#include "keycodes.hpp"
|
||||
#include "../core/VulkanTools.h"
|
||||
#include "../core/VulkanDebug.h"
|
||||
#include "VulkanUIOverlay.h"
|
||||
#include "../device/VulkanSwapChain.h"
|
||||
#include "../memory/VulkanBuffer.h"
|
||||
#include "../device/VulkanDevice.h"
|
||||
#include "../memory/VulkanTexture.h"
|
||||
|
||||
#include "../core/VulkanInitializers.hpp"
|
||||
#include "../camera.hpp"
|
||||
#include "../benchmark.hpp"
|
||||
|
||||
class VulkanExampleBase
|
||||
{
|
||||
private:
|
||||
std::string getWindowTitle() const;
|
||||
uint32_t destWidth{};
|
||||
uint32_t destHeight{};
|
||||
bool resizing = false;
|
||||
void handleMouseMove(int32_t x, int32_t y);
|
||||
void nextFrame();
|
||||
void updateOverlay();
|
||||
void createPipelineCache();
|
||||
void createCommandPool();
|
||||
void createSynchronizationPrimitives();
|
||||
void createSurface();
|
||||
void createSwapChain();
|
||||
void createCommandBuffers();
|
||||
void destroyCommandBuffers();
|
||||
std::string shaderDir = "glsl";
|
||||
protected:
|
||||
// Returns the path to the root of the glsl, hlsl or slang shader directory.
|
||||
std::string getShadersPath() const;
|
||||
|
||||
// Frame counter to display fps
|
||||
uint32_t frameCounter = 0;
|
||||
uint32_t lastFPS = 0;
|
||||
std::chrono::time_point<std::chrono::high_resolution_clock> lastTimestamp, tPrevEnd;
|
||||
// Vulkan instance, stores all per-application states
|
||||
VkInstance instance{ VK_NULL_HANDLE };
|
||||
std::vector<std::string> supportedInstanceExtensions;
|
||||
// Physical device (GPU) that Vulkan will use
|
||||
VkPhysicalDevice physicalDevice{ VK_NULL_HANDLE };
|
||||
// Stores physical device properties (for e.g. checking device limits)
|
||||
VkPhysicalDeviceProperties deviceProperties{};
|
||||
// Stores the features available on the selected physical device (for e.g. checking if a feature is available)
|
||||
VkPhysicalDeviceFeatures deviceFeatures{};
|
||||
// Stores all available memory (type) properties for the physical device
|
||||
VkPhysicalDeviceMemoryProperties deviceMemoryProperties{};
|
||||
/** @brief Set of physical device features to be enabled for this example (must be set in the derived constructor) */
|
||||
VkPhysicalDeviceFeatures enabledFeatures{};
|
||||
/** @brief Set of device extensions to be enabled for this example (must be set in the derived constructor) */
|
||||
std::vector<const char*> enabledDeviceExtensions;
|
||||
/** @brief Set of instance extensions to be enabled for this example (must be set in the derived constructor) */
|
||||
std::vector<const char*> enabledInstanceExtensions;
|
||||
/** @brief Set of layer settings to be enabled for this example (must be set in the derived constructor) */
|
||||
std::vector<VkLayerSettingEXT> enabledLayerSettings;
|
||||
/** @brief Optional pNext structure for passing extension structures to device creation */
|
||||
void* deviceCreatepNextChain = nullptr;
|
||||
/** @brief Logical device, application's view of the physical device (GPU) */
|
||||
VkDevice device{ VK_NULL_HANDLE };
|
||||
// Handle to the device graphics queue that command buffers are submitted to
|
||||
VkQueue queue{ VK_NULL_HANDLE };
|
||||
// Depth buffer format (selected during Vulkan initialization)
|
||||
VkFormat depthFormat{VK_FORMAT_UNDEFINED};
|
||||
// Command buffer pool
|
||||
VkCommandPool cmdPool{ VK_NULL_HANDLE };
|
||||
/** @brief Pipeline stages used to wait at for graphics queue submissions */
|
||||
VkPipelineStageFlags submitPipelineStages = VK_PIPELINE_STAGE_COLOR_ATTACHMENT_OUTPUT_BIT;
|
||||
// Contains command buffers and semaphores to be presented to the queue
|
||||
VkSubmitInfo submitInfo{};
|
||||
// Command buffers used for rendering
|
||||
std::vector<VkCommandBuffer> drawCmdBuffers;
|
||||
// Global render pass for frame buffer writes
|
||||
VkRenderPass renderPass{ VK_NULL_HANDLE };
|
||||
// List of available frame buffers (same as number of swap chain images)
|
||||
std::vector<VkFramebuffer>frameBuffers;
|
||||
// Active frame buffer index
|
||||
uint32_t currentBuffer = 0;
|
||||
// Descriptor set pool
|
||||
VkDescriptorPool descriptorPool{ VK_NULL_HANDLE };
|
||||
// List of shader modules created (stored for cleanup)
|
||||
std::vector<VkShaderModule> shaderModules;
|
||||
// Pipeline cache object
|
||||
VkPipelineCache pipelineCache{ VK_NULL_HANDLE };
|
||||
// Wraps the swap chain to present images (framebuffers) to the windowing system
|
||||
VulkanSwapChain swapChain;
|
||||
// Synchronization semaphores
|
||||
struct {
|
||||
// Swap chain image presentation
|
||||
VkSemaphore presentComplete;
|
||||
// Command buffer submission and execution
|
||||
VkSemaphore renderComplete;
|
||||
} semaphores{};
|
||||
std::vector<VkFence> waitFences;
|
||||
bool requiresStencil{ false };
|
||||
public:
|
||||
bool prepared = false;
|
||||
bool resized = false;
|
||||
bool viewUpdated = false;
|
||||
uint32_t width = 1920;
|
||||
uint32_t height = 1080;
|
||||
|
||||
vks::UIOverlay ui;
|
||||
CommandLineParser commandLineParser;
|
||||
|
||||
/** @brief Last frame time measured using a high performance timer (if available) */
|
||||
float frameTimer = 1.0f;
|
||||
|
||||
vks::Benchmark benchmark;
|
||||
|
||||
/** @brief Encapsulated physical and logical vulkan device */
|
||||
vks::VulkanDevice *vulkanDevice{};
|
||||
|
||||
/** @brief Example settings that can be changed e.g. by command line arguments */
|
||||
struct Settings {
|
||||
/** @brief Activates validation layers (and message output) when set to true */
|
||||
bool validation = false;
|
||||
/** @brief Set to true if fullscreen mode has been requested via command line */
|
||||
bool fullscreen = false;
|
||||
/** @brief Set to true if v-sync will be forced for the swapchain */
|
||||
bool vsync = false;
|
||||
/** @brief Enable UI overlay */
|
||||
bool overlay = true;
|
||||
} settings;
|
||||
|
||||
/** @brief State of gamepad input (only used on Android) */
|
||||
struct {
|
||||
glm::vec2 axisLeft = glm::vec2(0.0f);
|
||||
glm::vec2 axisRight = glm::vec2(0.0f);
|
||||
} gamePadState;
|
||||
|
||||
/** @brief State of mouse/touch input */
|
||||
struct {
|
||||
struct {
|
||||
bool left = false;
|
||||
bool right = false;
|
||||
bool middle = false;
|
||||
} buttons;
|
||||
glm::vec2 position;
|
||||
} mouseState;
|
||||
|
||||
VkClearColorValue defaultClearColor = { { 0.025f, 0.025f, 0.025f, 1.0f } };
|
||||
|
||||
static std::vector<const char*> args;
|
||||
|
||||
// Defines a frame rate independent timer value clamped from -1.0...1.0
|
||||
// For use in animations, rotations, etc.
|
||||
float timer = 0.0f;
|
||||
// Multiplier for speeding up (or slowing down) the global timer
|
||||
float timerSpeed = 0.25f;
|
||||
bool paused = false;
|
||||
|
||||
Camera camera;
|
||||
|
||||
std::string title = "Vulkan Example";
|
||||
std::string name = "vulkanExample";
|
||||
uint32_t apiVersion = VK_API_VERSION_1_0;
|
||||
|
||||
/** @brief Default depth stencil attachment used by the default render pass */
|
||||
struct {
|
||||
VkImage image;
|
||||
VkDeviceMemory memory;
|
||||
VkImageView view;
|
||||
} depthStencil{};
|
||||
|
||||
// OS specific
|
||||
#if defined(_WIN32)
|
||||
HWND window;
|
||||
HINSTANCE windowInstance;
|
||||
#elif defined(VK_USE_PLATFORM_ANDROID_KHR)
|
||||
// true if application has focused, false if moved to background
|
||||
bool focused = false;
|
||||
struct TouchPos {
|
||||
int32_t x;
|
||||
int32_t y;
|
||||
} touchPos{};
|
||||
bool touchDown = false;
|
||||
double touchTimer = 0.0;
|
||||
int64_t lastTapTime = 0;
|
||||
#elif (defined(VK_USE_PLATFORM_IOS_MVK) || defined(VK_USE_PLATFORM_MACOS_MVK) || defined(VK_USE_PLATFORM_METAL_EXT))
|
||||
void* view;
|
||||
#if defined(VK_USE_PLATFORM_METAL_EXT)
|
||||
CAMetalLayer* metalLayer;
|
||||
#endif
|
||||
#if defined(VK_EXAMPLE_XCODE_GENERATED)
|
||||
bool quit = false;
|
||||
#endif
|
||||
#elif defined(VK_USE_PLATFORM_DIRECTFB_EXT)
|
||||
bool quit = false;
|
||||
IDirectFB *dfb = nullptr;
|
||||
IDirectFBDisplayLayer *layer = nullptr;
|
||||
IDirectFBWindow *window = nullptr;
|
||||
IDirectFBSurface *surface = nullptr;
|
||||
IDirectFBEventBuffer *event_buffer = nullptr;
|
||||
#elif defined(VK_USE_PLATFORM_WAYLAND_KHR)
|
||||
wl_display *display = nullptr;
|
||||
wl_registry *registry = nullptr;
|
||||
wl_compositor *compositor = nullptr;
|
||||
struct xdg_wm_base *shell = nullptr;
|
||||
wl_seat *seat = nullptr;
|
||||
wl_pointer *pointer = nullptr;
|
||||
wl_keyboard *keyboard = nullptr;
|
||||
wl_surface *surface = nullptr;
|
||||
struct xdg_surface *xdg_surface;
|
||||
struct xdg_toplevel *xdg_toplevel;
|
||||
bool quit = false;
|
||||
bool configured = false;
|
||||
|
||||
#elif defined(_DIRECT2DISPLAY)
|
||||
bool quit = false;
|
||||
#elif defined(VK_USE_PLATFORM_XCB_KHR)
|
||||
bool quit = false;
|
||||
xcb_connection_t *connection;
|
||||
xcb_screen_t *screen;
|
||||
xcb_window_t window;
|
||||
xcb_intern_atom_reply_t *atom_wm_delete_window;
|
||||
#elif defined(VK_USE_PLATFORM_HEADLESS_EXT)
|
||||
bool quit = false;
|
||||
#elif defined(VK_USE_PLATFORM_SCREEN_QNX)
|
||||
screen_context_t screen_context = nullptr;
|
||||
screen_window_t screen_window = nullptr;
|
||||
screen_event_t screen_event = nullptr;
|
||||
bool quit = false;
|
||||
#endif
|
||||
|
||||
/** @brief Default base class constructor */
|
||||
VulkanExampleBase();
|
||||
virtual ~VulkanExampleBase();
|
||||
/** @brief Setup the vulkan instance, enable required extensions and connect to the physical device (GPU) */
|
||||
bool initVulkan();
|
||||
|
||||
#if defined(_WIN32)
|
||||
void setupConsole(std::string title);
|
||||
void setupDPIAwareness();
|
||||
HWND setupWindow(HINSTANCE hinstance, WNDPROC wndproc);
|
||||
void handleMessages(HWND hWnd, UINT uMsg, WPARAM wParam, LPARAM lParam);
|
||||
#elif defined(VK_USE_PLATFORM_ANDROID_KHR)
|
||||
static int32_t handleAppInput(struct android_app* app, AInputEvent* event);
|
||||
static void handleAppCommand(android_app* app, int32_t cmd);
|
||||
#elif (defined(VK_USE_PLATFORM_IOS_MVK) || defined(VK_USE_PLATFORM_MACOS_MVK) || defined(VK_USE_PLATFORM_METAL_EXT))
|
||||
void* setupWindow(void* view);
|
||||
void displayLinkOutputCb();
|
||||
void mouseDragged(float x, float y);
|
||||
void windowWillResize(float x, float y);
|
||||
void windowDidResize();
|
||||
#elif defined(VK_USE_PLATFORM_DIRECTFB_EXT)
|
||||
IDirectFBSurface *setupWindow();
|
||||
void handleEvent(const DFBWindowEvent *event);
|
||||
#elif defined(VK_USE_PLATFORM_WAYLAND_KHR)
|
||||
struct xdg_surface *setupWindow();
|
||||
void initWaylandConnection();
|
||||
void setSize(int width, int height);
|
||||
static void registryGlobalCb(void *data, struct wl_registry *registry,
|
||||
uint32_t name, const char *interface, uint32_t version);
|
||||
void registryGlobal(struct wl_registry *registry, uint32_t name,
|
||||
const char *interface, uint32_t version);
|
||||
static void registryGlobalRemoveCb(void *data, struct wl_registry *registry,
|
||||
uint32_t name);
|
||||
static void seatCapabilitiesCb(void *data, wl_seat *seat, uint32_t caps);
|
||||
void seatCapabilities(wl_seat *seat, uint32_t caps);
|
||||
static void pointerEnterCb(void *data, struct wl_pointer *pointer,
|
||||
uint32_t serial, struct wl_surface *surface, wl_fixed_t sx,
|
||||
wl_fixed_t sy);
|
||||
static void pointerLeaveCb(void *data, struct wl_pointer *pointer,
|
||||
uint32_t serial, struct wl_surface *surface);
|
||||
static void pointerMotionCb(void *data, struct wl_pointer *pointer,
|
||||
uint32_t time, wl_fixed_t sx, wl_fixed_t sy);
|
||||
void pointerMotion(struct wl_pointer *pointer,
|
||||
uint32_t time, wl_fixed_t sx, wl_fixed_t sy);
|
||||
static void pointerButtonCb(void *data, struct wl_pointer *wl_pointer,
|
||||
uint32_t serial, uint32_t time, uint32_t button, uint32_t state);
|
||||
void pointerButton(struct wl_pointer *wl_pointer,
|
||||
uint32_t serial, uint32_t time, uint32_t button, uint32_t state);
|
||||
static void pointerAxisCb(void *data, struct wl_pointer *wl_pointer,
|
||||
uint32_t time, uint32_t axis, wl_fixed_t value);
|
||||
void pointerAxis(struct wl_pointer *wl_pointer,
|
||||
uint32_t time, uint32_t axis, wl_fixed_t value);
|
||||
static void keyboardKeymapCb(void *data, struct wl_keyboard *keyboard,
|
||||
uint32_t format, int fd, uint32_t size);
|
||||
static void keyboardEnterCb(void *data, struct wl_keyboard *keyboard,
|
||||
uint32_t serial, struct wl_surface *surface, struct wl_array *keys);
|
||||
static void keyboardLeaveCb(void *data, struct wl_keyboard *keyboard,
|
||||
uint32_t serial, struct wl_surface *surface);
|
||||
static void keyboardKeyCb(void *data, struct wl_keyboard *keyboard,
|
||||
uint32_t serial, uint32_t time, uint32_t key, uint32_t state);
|
||||
void keyboardKey(struct wl_keyboard *keyboard,
|
||||
uint32_t serial, uint32_t time, uint32_t key, uint32_t state);
|
||||
static void keyboardModifiersCb(void *data, struct wl_keyboard *keyboard,
|
||||
uint32_t serial, uint32_t mods_depressed, uint32_t mods_latched,
|
||||
uint32_t mods_locked, uint32_t group);
|
||||
|
||||
#elif defined(_DIRECT2DISPLAY)
|
||||
//
|
||||
#elif defined(VK_USE_PLATFORM_XCB_KHR)
|
||||
xcb_window_t setupWindow();
|
||||
void initxcbConnection();
|
||||
void handleEvent(const xcb_generic_event_t *event);
|
||||
#elif defined(VK_USE_PLATFORM_SCREEN_QNX)
|
||||
void setupWindow();
|
||||
void handleEvent();
|
||||
#else
|
||||
void setupWindow();
|
||||
#endif
|
||||
/** @brief (Virtual) Creates the application wide Vulkan instance */
|
||||
virtual VkResult createInstance();
|
||||
/** @brief (Pure virtual) Render function to be implemented by the sample application */
|
||||
virtual void render() = 0;
|
||||
/** @brief (Virtual) Called after a key was pressed, can be used to do custom key handling */
|
||||
virtual void keyPressed(uint32_t);
|
||||
/** @brief (Virtual) Called after the mouse cursor moved and before internal events (like camera rotation) is handled */
|
||||
virtual void mouseMoved(double x, double y, bool &handled);
|
||||
/** @brief (Virtual) Called when the window has been resized, can be used by the sample application to recreate resources */
|
||||
virtual void windowResized();
|
||||
/** @brief (Virtual) Called when resources have been recreated that require a rebuild of the command buffers (e.g. frame buffer), to be implemented by the sample application */
|
||||
virtual void buildCommandBuffers();
|
||||
/** @brief (Virtual) Setup default depth and stencil views */
|
||||
virtual void setupDepthStencil();
|
||||
/** @brief (Virtual) Setup default framebuffers for all requested swapchain images */
|
||||
virtual void setupFrameBuffer();
|
||||
/** @brief (Virtual) Setup a default renderpass */
|
||||
virtual void setupRenderPass();
|
||||
/** @brief (Virtual) Called after the physical device features have been read, can be used to set features to enable on the device */
|
||||
virtual void getEnabledFeatures();
|
||||
/** @brief (Virtual) Called after the physical device extensions have been read, can be used to enable extensions based on the supported extension listing*/
|
||||
virtual void getEnabledExtensions();
|
||||
|
||||
/** @brief Prepares all Vulkan resources and functions required to run the sample */
|
||||
virtual void prepare();
|
||||
|
||||
/** @brief Loads a SPIR-V shader file for the given shader stage */
|
||||
VkPipelineShaderStageCreateInfo loadShader(std::string fileName, VkShaderStageFlagBits stage);
|
||||
|
||||
void windowResize();
|
||||
|
||||
/** @brief Entry point for the main render loop */
|
||||
void renderLoop();
|
||||
|
||||
/** @brief Adds the drawing commands for the ImGui overlay to the given command buffer */
|
||||
void drawUI(const VkCommandBuffer commandBuffer);
|
||||
|
||||
/** Prepare the next frame for workload submission by acquiring the next swap chain image */
|
||||
void prepareFrame();
|
||||
/** @brief Presents the current image to the swap chain */
|
||||
void submitFrame();
|
||||
/** @brief (Virtual) Default image acquire + submission and command buffer submission function */
|
||||
virtual void renderFrame();
|
||||
|
||||
/** @brief (Virtual) Called when the UI overlay is updating, can be used to add custom elements to the overlay */
|
||||
virtual void OnUpdateUIOverlay(vks::UIOverlay *overlay);
|
||||
|
||||
#if defined(_WIN32)
|
||||
virtual void OnHandleMessage(HWND hWnd, UINT uMsg, WPARAM wParam, LPARAM lParam);
|
||||
#endif
|
||||
};
|
||||
|
||||
#include "Entrypoints.h"
|
||||
Loading…
Add table
Add a link
Reference in a new issue