ppsspp/Common/Vulkan/VulkanContext.h
Henrik Rydgård 405ac4f2cc Merge pull request #9041 from unknownbrackets/vulkan
WIP: Progress toward device restore on Vulkan
2016-12-01 15:58:32 +01:00

399 lines
12 KiB
C++

#ifndef UTIL_INIT
#define UTIL_INIT
#ifdef __ANDROID__
#undef NDEBUG // asserts
#endif
#include <cassert>
#include <string>
#include <vector>
#include <utility>
#include "base/logging.h"
#ifdef _WIN32
#define WIN32_LEAN_AND_MEAN
#define VK_USE_PLATFORM_WIN32_KHR
#define NOMINMAX /* Don't let Windows define min() or max() */
#define APP_NAME_STR_LEN 80
#include <Windows.h>
#elif defined(__ANDROID__) // _WIN32
#include <android/native_window_jni.h>
#define VK_USE_PLATFORM_ANDROID_KHR
#else
#define VK_USE_PLATFORM_XCB_KHR
#include <unistd.h>
#endif // _WIN32
#include "Common/Vulkan/VulkanLoader.h"
// Amount of time, in nanoseconds, to wait for a command buffer to complete
#define FENCE_TIMEOUT 10000000000
#if defined(NDEBUG) && defined(__GNUC__)
#define U_ASSERT_ONLY __attribute__((unused))
#else
#define U_ASSERT_ONLY
#endif
enum {
VULKAN_FLAG_VALIDATE = 1,
VULKAN_FLAG_PRESENT_MAILBOX = 2,
VULKAN_FLAG_PRESENT_IMMEDIATE = 4,
VULKAN_FLAG_PRESENT_FIFO_RELAXED = 8,
};
// A layer can expose extensions, keep track of those extensions here.
struct layer_properties {
VkLayerProperties properties;
std::vector<VkExtensionProperties> extensions;
};
struct VulkanPhysicalDeviceInfo {
VkFormat preferredDepthStencilFormat;
};
// This is a bit repetitive...
class VulkanDeleteList {
struct Callback {
explicit Callback(void(*f)(void *userdata), void *u)
: func(f), userdata(u) {
}
void(*func)(void *userdata);
void *userdata;
};
public:
void QueueDeleteDescriptorPool(VkDescriptorPool &pool) { descPools_.push_back(pool); pool = VK_NULL_HANDLE; }
void QueueDeleteShaderModule(VkShaderModule &module) { modules_.push_back(module); module = VK_NULL_HANDLE; }
void QueueDeleteBuffer(VkBuffer &buffer) { buffers_.push_back(buffer); buffer = VK_NULL_HANDLE; }
void QueueDeleteBufferView(VkBufferView &bufferView) { bufferViews_.push_back(bufferView); bufferView = VK_NULL_HANDLE; }
void QueueDeleteImage(VkImage &image) { images_.push_back(image); image = VK_NULL_HANDLE; }
void QueueDeleteImageView(VkImageView &imageView) { imageViews_.push_back(imageView); imageView = VK_NULL_HANDLE; }
void QueueDeleteDeviceMemory(VkDeviceMemory &deviceMemory) { deviceMemory_.push_back(deviceMemory); deviceMemory = VK_NULL_HANDLE; }
void QueueDeleteSampler(VkSampler &sampler) { samplers_.push_back(sampler); sampler = VK_NULL_HANDLE; }
void QueueDeletePipeline(VkPipeline &pipeline) { pipelines_.push_back(pipeline); pipeline = VK_NULL_HANDLE; }
void QueueDeletePipelineCache(VkPipelineCache &pipelineCache) { pipelineCaches_.push_back(pipelineCache); pipelineCache = VK_NULL_HANDLE; }
void QueueDeleteRenderPass(VkRenderPass &renderPass) { renderPasses_.push_back(renderPass); renderPass = VK_NULL_HANDLE; }
void QueueDeleteFramebuffer(VkFramebuffer &framebuffer) { framebuffers_.push_back(framebuffer); framebuffer = VK_NULL_HANDLE; }
void QueueCallback(void(*func)(void *userdata), void *userdata) { callbacks_.push_back(Callback(func, userdata)); }
void Take(VulkanDeleteList &del) {
assert(descPools_.size() == 0);
assert(modules_.size() == 0);
assert(buffers_.size() == 0);
assert(bufferViews_.size() == 0);
assert(images_.size() == 0);
assert(imageViews_.size() == 0);
assert(deviceMemory_.size() == 0);
assert(samplers_.size() == 0);
assert(pipelines_.size() == 0);
assert(pipelineCaches_.size() == 0);
assert(renderPasses_.size() == 0);
assert(framebuffers_.size() == 0);
assert(callbacks_.size() == 0);
descPools_ = std::move(del.descPools_);
modules_ = std::move(del.modules_);
buffers_ = std::move(del.buffers_);
bufferViews_ = std::move(del.bufferViews_);
images_ = std::move(del.images_);
imageViews_ = std::move(del.imageViews_);
deviceMemory_ = std::move(del.deviceMemory_);
samplers_ = std::move(del.samplers_);
pipelines_ = std::move(del.pipelines_);
pipelineCaches_ = std::move(del.pipelineCaches_);
renderPasses_ = std::move(del.renderPasses_);
framebuffers_ = std::move(del.framebuffers_);
callbacks_ = std::move(del.callbacks_);
}
void PerformDeletes(VkDevice device) {
for (auto &descPool : descPools_) {
vkDestroyDescriptorPool(device, descPool, nullptr);
}
descPools_.clear();
for (auto &module : modules_) {
vkDestroyShaderModule(device, module, nullptr);
}
modules_.clear();
for (auto &buf : buffers_) {
vkDestroyBuffer(device, buf, nullptr);
}
buffers_.clear();
for (auto &bufView : bufferViews_) {
vkDestroyBufferView(device, bufView, nullptr);
}
bufferViews_.clear();
for (auto &image : images_) {
vkDestroyImage(device, image, nullptr);
}
images_.clear();
for (auto &imageView : imageViews_) {
vkDestroyImageView(device, imageView, nullptr);
}
imageViews_.clear();
for (auto &mem : deviceMemory_) {
vkFreeMemory(device, mem, nullptr);
}
deviceMemory_.clear();
for (auto &sampler : samplers_) {
vkDestroySampler(device, sampler, nullptr);
}
samplers_.clear();
for (auto &pipeline : pipelines_) {
vkDestroyPipeline(device, pipeline, nullptr);
}
pipelines_.clear();
for (auto &pcache : pipelineCaches_) {
vkDestroyPipelineCache(device, pcache, nullptr);
}
pipelineCaches_.clear();
for (auto &renderPass : renderPasses_) {
vkDestroyRenderPass(device, renderPass, nullptr);
}
renderPasses_.clear();
for (auto &framebuffer : framebuffers_) {
vkDestroyFramebuffer(device, framebuffer, nullptr);
}
framebuffers_.clear();
for (auto &callback : callbacks_) {
callback.func(callback.userdata);
}
callbacks_.clear();
}
private:
std::vector<VkDescriptorPool> descPools_;
std::vector<VkShaderModule> modules_;
std::vector<VkBuffer> buffers_;
std::vector<VkBufferView> bufferViews_;
std::vector<VkImage> images_;
std::vector<VkImageView> imageViews_;
std::vector<VkDeviceMemory> deviceMemory_;
std::vector<VkSampler> samplers_;
std::vector<VkPipeline> pipelines_;
std::vector<VkPipelineCache> pipelineCaches_;
std::vector<VkRenderPass> renderPasses_;
std::vector<VkFramebuffer> framebuffers_;
std::vector<Callback> callbacks_;
};
// VulkanContext sets up the basics necessary for rendering to a window, including framebuffers etc.
// Optionally, it can create a depth buffer for you as well.
class VulkanContext {
public:
VulkanContext(const char *app_name, int app_ver, uint32_t flags);
~VulkanContext();
VkResult CreateDevice(int physical_device);
const std::string &InitError() { return init_error_; }
VkDevice GetDevice() { return device_; }
VkInstance GetInstance() { return instance_; }
VulkanDeleteList &Delete() { return globalDeleteList_; }
VkPipelineCache CreatePipelineCache();
#ifdef _WIN32
void InitSurfaceWin32(HINSTANCE conn, HWND wnd);
void ReinitSurfaceWin32();
#elif __ANDROID__
void InitSurfaceAndroid(ANativeWindow *native_window, int width, int height);
void ReinitSurfaceAndroid(int width, int height);
#endif
void InitQueue();
void InitObjects(bool depthPresent);
void InitSwapchain(VkCommandBuffer cmd);
void InitSurfaceRenderPass(bool include_depth, bool clear);
void InitFramebuffers(bool include_depth);
void InitDepthStencilBuffer(VkCommandBuffer cmd);
void InitCommandPool();
// Also destroys the surface.
void DestroyObjects();
void DestroySurfaceRenderPass();
void DestroyFramebuffers();
void DestroySwapChain();
void DestroyDepthStencilBuffer();
void DestroyCommandPool();
void DestroyDevice();
void WaitUntilQueueIdle();
// Utility functions for shorter code
VkFence CreateFence(bool presignalled);
bool CreateShaderModule(const std::vector<uint32_t> &spirv, VkShaderModule *shaderModule);
void WaitAndResetFence(VkFence fence);
int GetWidth() { return width_; }
int GetHeight() { return height_; }
VkCommandBuffer GetInitCommandBuffer();
// This must only be accessed between BeginSurfaceRenderPass and EndSurfaceRenderPass.
VkCommandBuffer GetSurfaceCommandBuffer() {
return frame_[curFrame_ & 1].cmdBuf;
}
// The surface render pass is special because it has to acquire the backbuffer, and may thus "block".
// Use the returned command buffer to enqueue commands that render to the backbuffer.
// To render to other buffers first, you can submit additional commandbuffers using QueueBeforeSurfaceRender(cmd).
VkCommandBuffer BeginSurfaceRenderPass(VkClearValue clear_values[2]);
// May eventually need the ability to break and resume the backbuffer render pass in a few rare cases.
void EndSurfaceRenderPass();
void QueueBeforeSurfaceRender(VkCommandBuffer cmd);
bool MemoryTypeFromProperties(uint32_t typeBits, VkFlags requirements_mask, uint32_t *typeIndex);
VkResult InitDebugMsgCallback(PFN_vkDebugReportCallbackEXT dbgFunc, int bits, void *userdata = nullptr);
void DestroyDebugMsgCallback();
VkRenderPass GetSurfaceRenderPass() const {
return surface_render_pass_;
}
VkPhysicalDevice GetPhysicalDevice(int n = 0) const {
return physical_devices_[n];
}
VkQueue GetGraphicsQueue() const {
return gfx_queue_;
}
int GetGraphicsQueueFamilyIndex() const {
return graphics_queue_family_index_;
}
const VkPhysicalDeviceProperties &GetPhysicalDeviceProperties() {
return gpu_props;
}
VkResult InitGlobalExtensionProperties();
VkResult InitLayerExtensionProperties(layer_properties &layer_props);
VkResult InitGlobalLayerProperties();
VkResult InitDeviceExtensionProperties(layer_properties &layer_props);
VkResult InitDeviceLayerProperties();
const VkPhysicalDeviceFeatures &GetFeaturesAvailable() const { return featuresAvailable_; }
const VkPhysicalDeviceFeatures &GetFeaturesEnabled() const { return featuresEnabled_; }
const VulkanPhysicalDeviceInfo &GetDeviceInfo() const { return deviceInfo_; }
private:
VkSemaphore acquireSemaphore;
#ifdef _WIN32
HINSTANCE connection; // hInstance - Windows Instance
HWND window; // hWnd - window handle
#elif __ANDROID__ // _WIN32
ANativeWindow *native_window;
#endif // _WIN32
// TODO: Move to frame data
VkCommandPool cmd_pool_;
VkInstance instance_;
VkDevice device_;
VkQueue gfx_queue_;
VkSurfaceKHR surface_;
std::string init_error_;
std::vector<const char *> instance_layer_names;
std::vector<const char *> instance_extension_names;
std::vector<layer_properties> instance_layer_properties;
std::vector<VkExtensionProperties> instance_extension_properties;
std::vector<const char *> device_layer_names;
std::vector<const char *> device_extension_names;
std::vector<layer_properties> device_layer_properties;
std::vector<VkExtensionProperties> device_extension_properties;
std::vector<VkPhysicalDevice> physical_devices_;
uint32_t graphics_queue_family_index_;
VkPhysicalDeviceProperties gpu_props;
std::vector<VkQueueFamilyProperties> queue_props;
VkPhysicalDeviceMemoryProperties memory_properties;
// Custom collection of things that are good to know
VulkanPhysicalDeviceInfo deviceInfo_;
struct swap_chain_buffer {
VkImage image;
VkImageView view;
};
// Swap chain
int width_, height_;
int flags_;
VkFormat swapchain_format;
std::vector<VkFramebuffer> framebuffers_;
uint32_t swapchainImageCount;
VkSwapchainKHR swap_chain_;
std::vector<swap_chain_buffer> swapChainBuffers;
// Manages flipping command buffers for the backbuffer render pass.
// It is recommended to do the same for other rendering passes.
struct FrameData {
FrameData() : hasInitCommands(false), cmdInit(nullptr), cmdBuf(nullptr) {}
VkFence fence;
bool hasInitCommands;
VkCommandBuffer cmdInit;
VkCommandBuffer cmdBuf;
VulkanDeleteList deleteList;
};
FrameData frame_[2];
int curFrame_;
// At the end of the frame, this is copied into the frame's delete list, so it can be processed
// the next time the frame comes around again.
VulkanDeleteList globalDeleteList_;
std::vector<VkDebugReportCallbackEXT> msg_callbacks;
struct {
VkFormat format;
VkImage image;
VkDeviceMemory mem;
VkImageView view;
} depth;
VkRenderPass surface_render_pass_;
uint32_t current_buffer;
uint32_t queue_count;
VkPhysicalDeviceFeatures featuresAvailable_;
VkPhysicalDeviceFeatures featuresEnabled_;
std::vector<VkCommandBuffer> cmdQueue_;
};
// Stand-alone utility functions
void VulkanBeginCommandBuffer(VkCommandBuffer cmd);
void TransitionImageLayout(
VkCommandBuffer cmd,
VkImage image,
VkImageAspectFlags aspectMask,
VkImageLayout old_image_layout,
VkImageLayout new_image_layout);
// GLSL compiler
void init_glslang();
void finalize_glslang();
bool GLSLtoSPV(const VkShaderStageFlagBits shader_type, const char *pshader, std::vector<uint32_t> &spirv, std::string *errorMessage = nullptr);
#endif // UTIL_INIT