Files
kamb205--KB-RPCS3/app/base/vulkanexamplebase.h
T

461 lines
19 KiB
C++

/*
* Vulkan Example base class
*
* Copyright (C) 2016-2026 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>
#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_OHOS)
#include <ace/xcomponent/native_interface_xcomponent.h>
#include <rawfile/raw_file_manager.h>
#include "uv.h"
#include "VulkanOHOS.h"
extern NativeWindow* nativeWindow;
extern uv_loop_t* loop;
extern uv_async_t msgSignal;
#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 "vulkan/vulkan.h"
#include "CommandLineParser.hpp"
#include "keycodes.hpp"
#include "VulkanTools.h"
#include "VulkanDebug.h"
#include "VulkanUIOverlay.h"
#include "VulkanSwapChain.h"
#include "VulkanBuffer.h"
#include "VulkanDevice.h"
#include "VulkanTexture.h"
#include "VulkanInitializers.hpp"
#include "camera.hpp"
#include "benchmark.hpp"
constexpr uint32_t maxConcurrentFrames{ 2 };
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";
#if defined(VK_EXAMPLE_PS5)
VkSemaphore ps5CaptureComplete{ VK_NULL_HANDLE };
VkSemaphore ps5CaptureSwapchainImage(VkSemaphore renderComplete);
/** @brief The host reference build is recording every frame (PS5_RECORD, ps5/tools/record-reel.sh) */
static bool ps5Recording();
#endif
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 };
// Command buffers used for rendering
std::array<VkCommandBuffer, maxConcurrentFrames> 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;
// 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 related objects and variables
// These are used to have multiple frame buffers "in flight" to get some CPU/GPU parallelism
uint32_t currentImageIndex{ 0 };
uint32_t currentBuffer{ 0 };
std::array<VkSemaphore, maxConcurrentFrames> presentCompleteSemaphores{};
std::vector<VkSemaphore> renderCompleteSemaphores{};
std::array<VkFence, maxConcurrentFrames> waitFences;
bool requiresStencil{ false };
public:
bool prepared = false;
bool resized = false;
uint32_t width = 1280;
uint32_t height = 720;
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{};
/** @brief Can be used by samples that use the core version of dynamic rendering */
bool useDynamicRendering{ false };
VkPhysicalDeviceDynamicRenderingFeatures baseDynamicRenderingFeatures{ .sType = VK_STRUCTURE_TYPE_PHYSICAL_DEVICE_DYNAMIC_RENDERING_FEATURES, .dynamicRendering = VK_TRUE };
// 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 (__OHOS__)
NativeResourceManager* s_nativeResourceManager;
bool focused = false;
struct TouchPos {
int32_t x;
int32_t y;
} touchPos;
bool touchDown = false;
double touchTimer = 0.0;
int64_t lastTapTime = 0;
int64_t touchDownTime = 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
#if defined(VK_EXAMPLE_PS5)
/** @brief PS5: how the launcher runs this sample (ps5/src/main.cpp); also in the host reference build (ps5/README.md) */
struct {
/** @brief Frames to draw before the sample ends itself; 0: until OPTIONS is pressed */
uint32_t frameBudget = 0;
/** @brief Where the last frame of the budget is saved (a PPM at half the display's size); empty: none */
std::string screenshotPath;
/** @brief Frames drawn so far */
uint32_t framesDrawn = 0;
/** @brief Save the next frame to screenshotPath at full size, whatever the budget (RPCS3 PS5: L3 + R3 on the
* home screen; the console's own screenshot sees only black, as the title presents through its own driver) */
bool captureNow = false;
/** @brief When half the budget had been drawn, and when the last frame began (steady_clock seconds):
* the frame rate after start-up, without the last frame's screenshot */
double halfwayTime = 0.0;
double lastFrameTime = 0.0;
/** @brief The launcher's own menu: ImGui windows of its own instead of the sample's overlay window */
bool ownOverlay = false;
/** @brief OPTIONS ends the sample, back to the menu; in a title with one program, it shows and hides the overlay */
bool optionsEnds = true;
/** @brief A test run's variant of the sample ("uikit.aurora" runs uikit with "aurora"); empty otherwise */
std::string variant;
/** @brief A recording's camera orbit in a test run, degrees a second (test-run.txt's "orbit") */
float orbit = 0.0f;
} ps5;
/** @brief Reads the pad: the sticks move the camera, the buttons drive the UI overlay, OPTIONS ends the sample */
void ps5HandleInput();
#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_OHOS)
static void handleAppInput(OH_NativeXComponent *component, void *window, void* pThis);
// Static callback for libuv async handle
static void onMessageCallbackStatic(uv_async_t* handle);
// Instance method called from static callback
void onMessageCallback();
#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) 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 Begin dynamic rendering with the default setup for the base class color and depth attachment */
void beginDynamicRendering(VkCommandBuffer cmdBuffer);
/** @brief End dynamic rendering with the default setup for the base class color and depth attachment */
void endDynamicRendering(VkCommandBuffer cmdBuffer);
/** @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 and waiting for the previous command buffer to finish */
void prepareFrame(bool waitForFence = true);
/** @brief Presents the current image to the swap chain */
void submitFrame(bool skipQueueSubmit = false);
/** @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"