procedural-3d-engine/base/vulkanexamplebase.h

417 lines
14 KiB
C
Raw Normal View History

2016-02-16 15:07:25 +01:00
/*
* Vulkan Example base class
*
* Copyright (C) 2016 by Sascha Willems - www.saschawillems.de
*
* This code is licensed under the MIT license (MIT) (http://opensource.org/licenses/MIT)
*/
#pragma once
#ifdef _WIN32
#pragma comment(linker, "/subsystem:windows")
#include <windows.h>
#include <fcntl.h>
#include <io.h>
#elif defined(__ANDROID__)
#include <android/native_activity.h>
#include <android/asset_manager.h>
#include <android_native_app_glue.h>
#include "vulkanandroid.h"
#elif defined(__linux__)
2016-02-16 15:07:25 +01:00
#include <xcb/xcb.h>
#endif
#include <iostream>
#include <chrono>
#define GLM_FORCE_RADIANS
#define GLM_FORCE_DEPTH_ZERO_TO_ONE
2016-02-16 15:07:25 +01:00
#include <glm/glm.hpp>
#include <string>
#include <array>
#include "vulkan/vulkan.h"
#include "keycodes.hpp"
2016-02-16 15:07:25 +01:00
#include "vulkantools.h"
#include "vulkandebug.h"
#include "vulkandevice.hpp"
2016-02-16 15:07:25 +01:00
#include "vulkanswapchain.hpp"
#include "vulkanTextureLoader.hpp"
#include "vulkanMeshLoader.hpp"
#include "vulkantextoverlay.hpp"
#include "camera.hpp"
2016-02-16 15:07:25 +01:00
// Function pointer for getting physical device fetures to be enabled
typedef VkPhysicalDeviceFeatures (*PFN_GetEnabledFeatures)();
2016-02-16 15:07:25 +01:00
class VulkanExampleBase
{
private:
// Set to true when example is created with enabled validation layers
bool enableValidation = false;
// Set to true if v-sync will be forced for the swapchain
bool enableVSync = false;
// Device features enabled by the example
// If not set, no additional features are enabled (may result in validation layer errors)
VkPhysicalDeviceFeatures enabledFeatures = {};
// fps timer (one second interval)
float fpsTimer = 0.0f;
2016-02-16 15:07:25 +01:00
// Create application wide Vulkan instance
VkResult createInstance(bool enableValidation);
// Get window title with example name, device, et.
std::string getWindowTitle();
/** brief Indicates that the view (position, rotation) has changed and */
bool viewUpdated = false;
2016-04-10 11:12:04 +02:00
// Destination dimensions for resizing the window
uint32_t destWidth;
uint32_t destHeight;
bool resizing = false;
2016-04-10 11:12:04 +02:00
// Called if the window is resized and some resources have to be recreatesd
void windowResize();
2016-02-16 15:07:25 +01:00
protected:
// Last frame time, measured using a high performance timer (if available)
float frameTimer = 1.0f;
// Frame counter to display fps
uint32_t frameCounter = 0;
uint32_t lastFPS = 0;
2016-02-16 15:07:25 +01:00
// Vulkan instance, stores all per-application states
VkInstance instance;
// Physical device (GPU) that Vulkan will ise
VkPhysicalDevice physicalDevice;
// Stores physical device properties (for e.g. checking device limits)
VkPhysicalDeviceProperties deviceProperties;
// Stores phyiscal device features (for e.g. checking if a feature is available)
VkPhysicalDeviceFeatures deviceFeatures;
2016-02-16 15:07:25 +01:00
// Stores all available memory (type) properties for the physical device
VkPhysicalDeviceMemoryProperties deviceMemoryProperties;
/** @brief Logical device, application's view of the physical device (GPU) */
// todo: getter? should always point to VulkanDevice->device
2016-02-16 15:07:25 +01:00
VkDevice device;
/** @brief Encapsulated physical and logical vulkan device */
2016-07-22 20:45:48 +02:00
vk::VulkanDevice *vulkanDevice;
2016-02-16 15:07:25 +01:00
// Handle to the device graphics queue that command buffers are submitted to
VkQueue queue;
// Color buffer format
VkFormat colorformat = VK_FORMAT_B8G8R8A8_UNORM;
// Depth buffer format
// Depth format is selected during Vulkan initialization
VkFormat depthFormat;
// Command buffer pool
VkCommandPool cmdPool;
// Command buffer used for setup
VkCommandBuffer setupCmdBuffer = 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;
2016-02-16 15:07:25 +01:00
// Command buffers used for rendering
std::vector<VkCommandBuffer> drawCmdBuffers;
// Global render pass for frame buffer writes
VkRenderPass renderPass;
// 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;
2016-02-16 15:07:25 +01:00
// List of shader modules created (stored for cleanup)
std::vector<VkShaderModule> shaderModules;
// Pipeline cache object
VkPipelineCache pipelineCache;
// 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;
// Text overlay submission and execution
VkSemaphore textOverlayComplete;
} semaphores;
2016-02-16 15:07:25 +01:00
// Simple texture loader
vkTools::VulkanTextureLoader *textureLoader = nullptr;
// Returns the base asset path (for shaders, models, textures) depending on the os
const std::string getAssetPath();
2016-02-16 15:07:25 +01:00
public:
bool prepared = false;
uint32_t width = 1280;
uint32_t height = 720;
VkClearColorValue defaultClearColor = { { 0.025f, 0.025f, 0.025f, 1.0f } };
float zoom = 0;
// 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;
bool enableTextOverlay = false;
VulkanTextOverlay *textOverlay;
2016-02-16 15:07:25 +01:00
// Use to adjust mouse rotation speed
float rotationSpeed = 1.0f;
// Use to adjust mouse zoom speed
float zoomSpeed = 1.0f;
Camera camera;
2016-02-16 15:07:25 +01:00
glm::vec3 rotation = glm::vec3();
glm::vec3 cameraPos = glm::vec3();
2016-02-16 15:07:25 +01:00
glm::vec2 mousePos;
std::string title = "Vulkan Example";
std::string name = "vulkanExample";
struct
{
VkImage image;
VkDeviceMemory mem;
VkImageView view;
} depthStencil;
// Gamepad state (only one pad supported)
struct
{
glm::vec2 axisLeft = glm::vec2(0.0f);
glm::vec2 axisRight = glm::vec2(0.0f);
} gamePadState;
// OS specific
#if defined(_WIN32)
HWND window;
HINSTANCE windowInstance;
#elif defined(__ANDROID__)
android_app* androidApp;
// true if application has focused, false if moved to background
bool focused = false;
#elif defined(__linux__)
2016-02-16 15:07:25 +01:00
struct {
bool left = false;
bool right = false;
bool middle = false;
2016-02-16 15:07:25 +01:00
} mouseButtons;
bool quit = false;
2016-02-16 15:07:25 +01:00
xcb_connection_t *connection;
xcb_screen_t *screen;
xcb_window_t window;
xcb_intern_atom_reply_t *atom_wm_delete_window;
#endif
2016-02-16 15:07:25 +01:00
// Default ctor
VulkanExampleBase(bool enableValidation, PFN_GetEnabledFeatures enabledFeaturesFn = nullptr);
// dtor
2016-02-16 15:07:25 +01:00
~VulkanExampleBase();
// Setup the vulkan instance, enable required extensions and connect to the physical device (GPU)
void initVulkan(bool enableValidation);
#if defined(_WIN32)
2016-02-16 15:07:25 +01:00
void setupConsole(std::string title);
HWND setupWindow(HINSTANCE hinstance, WNDPROC wndproc);
void handleMessages(HWND hWnd, UINT uMsg, WPARAM wParam, LPARAM lParam);
#elif defined(__ANDROID__)
static int32_t handleAppInput(struct android_app* app, AInputEvent* event);
2016-03-20 17:35:54 +01:00
static void handleAppCommand(android_app* app, int32_t cmd);
#elif defined(__linux__)
2016-02-16 15:07:25 +01:00
xcb_window_t setupWindow();
void initxcbConnection();
void handleEvent(const xcb_generic_event_t *event);
#endif
// Pure virtual render function (override in derived class)
virtual void render() = 0;
// Called when view change occurs
// Can be overriden in derived class to e.g. update uniform buffers
// Containing view dependant matrices
virtual void viewChanged();
// Called if a key is pressed
// Can be overriden in derived class to do custom key handling
virtual void keyPressed(uint32_t keyCode);
2016-04-10 11:12:04 +02:00
// Called when the window has been resized
// Can be overriden in derived class to recreate or rebuild resources attached to the frame buffer / swapchain
virtual void windowResized();
// Pure virtual function to be overriden by the dervice class
// Called in case of an event where e.g. the framebuffer has to be rebuild and thus
// all command buffers that may reference this
virtual void buildCommandBuffers();
2016-02-16 15:07:25 +01:00
// Creates a new (graphics) command pool object storing command buffers
void createCommandPool();
// Setup default depth and stencil views
virtual void setupDepthStencil();
2016-02-16 15:07:25 +01:00
// Create framebuffers for all requested swap chain images
// Can be overriden in derived class to setup a custom framebuffer (e.g. for MSAA)
virtual void setupFrameBuffer();
2016-02-16 15:07:25 +01:00
// Setup a default render pass
// Can be overriden in derived class to setup a custom render pass (e.g. for MSAA)
virtual void setupRenderPass();
2016-02-16 15:07:25 +01:00
// Connect and prepare the swap chain
void initSwapchain();
// Create swap chain images
void setupSwapChain();
// Check if command buffers are valid (!= VK_NULL_HANDLE)
bool checkCommandBuffers();
// Create command buffers for drawing commands
void createCommandBuffers();
// Destroy all command buffers and set their handles to VK_NULL_HANDLE
// May be necessary during runtime if options are toggled
void destroyCommandBuffers();
// Create command buffer for setup commands
void createSetupCommandBuffer();
// Finalize setup command bufferm submit it to the queue and remove it
void flushSetupCommandBuffer();
// Command buffer creation
// Creates and returns a new command buffer
VkCommandBuffer createCommandBuffer(VkCommandBufferLevel level, bool begin);
// End the command buffer, submit it to the queue and free (if requested)
// Note : Waits for the queue to become idle
void flushCommandBuffer(VkCommandBuffer commandBuffer, VkQueue queue, bool free);
2016-02-16 15:07:25 +01:00
// Create a cache pool for rendering pipelines
void createPipelineCache();
// Prepare commonly used Vulkan functions
2016-03-20 17:35:54 +01:00
virtual void prepare();
2016-02-16 15:07:25 +01:00
// Load a SPIR-V shader
VkPipelineShaderStageCreateInfo loadShader(std::string fileName, VkShaderStageFlagBits stage);
2016-03-06 20:15:56 +01:00
// Create a buffer, fill it with data (if != NULL) and bind buffer memory
VkBool32 createBuffer(
VkBufferUsageFlags usageFlags,
VkMemoryPropertyFlags memoryPropertyFlags,
VkDeviceSize size,
void *data,
VkBuffer *buffer,
VkDeviceMemory *memory);
// This version always uses HOST_VISIBLE memory
2016-02-16 15:07:25 +01:00
VkBool32 createBuffer(
VkBufferUsageFlags usage,
VkDeviceSize size,
void *data,
VkBuffer *buffer,
VkDeviceMemory *memory);
// Overload that assigns buffer info to descriptor
VkBool32 createBuffer(
VkBufferUsageFlags usage,
VkDeviceSize size,
void *data,
VkBuffer *buffer,
VkDeviceMemory *memory,
VkDescriptorBufferInfo *descriptor);
// Overload to pass memory property flags
VkBool32 createBuffer(
VkBufferUsageFlags usage,
VkMemoryPropertyFlags memoryPropertyFlags,
VkDeviceSize size,
void *data,
VkBuffer *buffer,
VkDeviceMemory *memory,
VkDescriptorBufferInfo *descriptor);
2016-02-16 15:07:25 +01:00
// Load a mesh (using ASSIMP) and create vulkan vertex and index buffers with given vertex layout
void loadMesh(
std::string fiename,
vkMeshLoader::MeshBuffer *meshBuffer,
std::vector<vkMeshLoader::VertexLayout> vertexLayout,
2016-02-16 15:07:25 +01:00
float scale);
void loadMesh(
std::string filename,
vkMeshLoader::MeshBuffer *meshBuffer,
std::vector<vkMeshLoader::VertexLayout>
vertexLayout,
vkMeshLoader::MeshCreateInfo *meshCreateInfo);
2016-02-16 15:07:25 +01:00
// Start the main render loop
void renderLoop();
void updateTextOverlay();
// Called when the text overlay is updating
// Can be overriden in derived class to add custom text to the overlay
virtual void getOverlayText(VulkanTextOverlay * textOverlay);
// Prepare the frame for workload submission
// - Acquires the next image from the swap chain
// - Sets the default wait and signal semaphores
void prepareFrame();
// Submit the frames' workload
// - Submits the text overlay (if enabled)
void submitFrame();
2016-02-16 15:07:25 +01:00
};
// OS specific macros for the example main entry points
#if defined(_WIN32)
// Windows entry point
#define VULKAN_EXAMPLE_MAIN() \
VulkanExample *vulkanExample; \
LRESULT CALLBACK WndProc(HWND hWnd, UINT uMsg, WPARAM wParam, LPARAM lParam) \
{ \
if (vulkanExample != NULL) \
{ \
vulkanExample->handleMessages(hWnd, uMsg, wParam, lParam); \
} \
return (DefWindowProc(hWnd, uMsg, wParam, lParam)); \
} \
int APIENTRY WinMain(HINSTANCE hInstance, HINSTANCE hPrevInstance, LPSTR pCmdLine, int nCmdShow) \
{ \
vulkanExample = new VulkanExample(); \
vulkanExample->setupWindow(hInstance, WndProc); \
vulkanExample->initSwapchain(); \
vulkanExample->prepare(); \
vulkanExample->renderLoop(); \
delete(vulkanExample); \
return 0; \
}
#elif defined(__ANDROID__)
// Android entry point
// A note on app_dummy(): This is required as the compiler may otherwise remove the main entry point of the application
#define VULKAN_EXAMPLE_MAIN() \
VulkanExample *vulkanExample; \
void android_main(android_app* state) \
{ \
app_dummy(); \
vulkanExample = new VulkanExample(); \
state->userData = vulkanExample; \
state->onAppCmd = VulkanExample::handleAppCommand; \
state->onInputEvent = VulkanExample::handleAppInput; \
vulkanExample->androidApp = state; \
vulkanExample->renderLoop(); \
delete(vulkanExample); \
}
#elif defined(__linux__)
// Linux entry point
// todo: extract command line arguments
#define VULKAN_EXAMPLE_MAIN() \
VulkanExample *vulkanExample; \
static void handleEvent(const xcb_generic_event_t *event) \
{ \
if (vulkanExample != NULL) \
{ \
vulkanExample->handleEvent(event); \
} \
} \
int main(const int argc, const char *argv[]) \
{ \
vulkanExample = new VulkanExample(); \
vulkanExample->setupWindow(); \
vulkanExample->initSwapchain(); \
vulkanExample->prepare(); \
vulkanExample->renderLoop(); \
delete(vulkanExample); \
return 0; \
}
#endif