1
Fork 0
sm64/src/pc/gfx/gfx_vulkan.cpp

1077 lines
40 KiB
C++
Raw Normal View History

2022-10-03 22:49:19 -04:00
#include <time.h>
#include <errno.h>
2022-10-04 09:34:57 -04:00
#include <vulkan/vulkan.h>
#include <vector>
#include <cstring>
#include <cstdio>
#include <array>
2022-10-04 10:01:45 -04:00
#include <limits>
2022-10-04 10:20:38 -04:00
#include <fstream>
#include <glslang/Public/ShaderLang.h>
#include <glslang/SPIRV/GlslangToSpv.h>
2022-10-03 22:49:19 -04:00
#include "gfx_rendering_api.h"
#include "gfx_pc.h"
#include "gfx_window_manager_api.h"
2022-10-04 10:34:29 -04:00
#include "gfx_cc.h"
#include "defaultresources.hpp"
static VkInstance instance = VK_NULL_HANDLE;
static VkPhysicalDevice physical_device = VK_NULL_HANDLE;
static VkDevice device = VK_NULL_HANDLE;
static VkQueue graphics_queue = VK_NULL_HANDLE;
static VkQueue present_queue = VK_NULL_HANDLE;
static VkCommandPool command_pool = VK_NULL_HANDLE;
static VkSwapchainKHR swapchain = VK_NULL_HANDLE;
static VkFormat surface_format;
static std::vector<VkImage> swapchain_images;
static std::vector<VkImageView> swapchain_views;
static VkRenderPass render_pass = VK_NULL_HANDLE;
static VkImage depth_image = VK_NULL_HANDLE;
static VkDeviceMemory depth_memory = VK_NULL_HANDLE;
static VkImageView depth_view = VK_NULL_HANDLE;
static std::vector<VkFramebuffer> swapchain_framebuffers;
2022-10-04 09:34:57 -04:00
2022-10-04 10:01:45 -04:00
// TODO: removed hardcoded 3 frame queue, should be variable
static std::array<VkCommandBuffer, 3> command_buffers;
static std::array<VkSemaphore, 3> image_available;
static std::array<VkSemaphore, 3> render_finished;
static std::array<VkFence, 3> in_flight;
static uint32_t current_frame = 0;
static uint32_t image_index = 0;
static VkCommandBuffer current_cmd = VK_NULL_HANDLE;
2022-10-04 10:20:38 -04:00
2022-10-04 10:25:48 -04:00
static VkBuffer vertex_buffer = VK_NULL_HANDLE;
static VkDeviceMemory vertex_memory = VK_NULL_HANDLE;
struct ShaderProgramVulkan {
VkPipeline pipeline = VK_NULL_HANDLE;
VkPipelineLayout pipeline_layout = VK_NULL_HANDLE;
2022-10-05 20:34:03 -04:00
uint8_t num_inputs;
bool used_textures[2];
};
std::unordered_map<uint32_t, ShaderProgramVulkan*> cached_shaders;
static ShaderProgramVulkan* last_program = nullptr;
2022-10-04 09:34:57 -04:00
VKAPI_ATTR VkBool32 VKAPI_CALL
gfx_vulkan_debug_callback(VkDebugUtilsMessageSeverityFlagBitsEXT messageSeverity,
VkDebugUtilsMessageTypeFlagsEXT messageType,
const VkDebugUtilsMessengerCallbackDataEXT* pCallbackData,
void* pUserData) {
printf("%s\n", pCallbackData->pMessage);
return VK_FALSE;
}
2022-10-03 22:49:19 -04:00
static bool gfx_vulkan_renderer_z_is_from_0_to_1(void) {
return false;
}
2022-10-04 10:20:38 -04:00
static VkShaderModule gfx_vulkan_create_shader_module(const uint32_t* code, const int length) {
VkShaderModuleCreateInfo create_info = {};
create_info.sType = VK_STRUCTURE_TYPE_SHADER_MODULE_CREATE_INFO;
create_info.codeSize = length;
create_info.pCode = reinterpret_cast<const uint32_t*>(code);
VkShaderModule shader_module;
vkCreateShaderModule(device, &create_info, nullptr, &shader_module);
return shader_module;
2022-10-03 22:49:19 -04:00
}
2022-10-04 10:20:38 -04:00
// TODO: will be removed, just used for testing
static VkShaderModule gfx_vulkan_load_shader(const char* path) {
std::ifstream file(path, std::ios::ate | std::ios::binary);
size_t fileSize = (size_t) file.tellg();
std::vector<char> buffer(fileSize);
file.seekg(0);
file.read(buffer.data(), fileSize);
return gfx_vulkan_create_shader_module(reinterpret_cast<const uint32_t *>(buffer.data()), fileSize);
}
static std::string gfx_vulkan_create_vertex_shader(const CCFeatures features) {
std::string src = "#version 460 core\n";
const bool uses_textures = features.used_textures[0] || features.used_textures[1];
const bool uses_alpha = features.opt_alpha;
const bool uses_fog = features.opt_fog;
const bool needs_screenpos = features.opt_alpha && features.opt_noise;
src += "layout(location = 0) in vec4 position;\n";
int next_input = 1;
if(uses_textures) {
src += "layout(location = " + std::to_string(next_input++) + ") in vec2 uv;\n";
}
2022-10-05 20:34:03 -04:00
/*if(needs_screenpos) {
src += "layout(location = " + std::to_string(next_input++) + ") in vec4 screen_pos;\n";
}
2022-10-05 20:34:03 -04:00
*/
if(uses_fog) {
src += "layout(location = " + std::to_string(next_input++) + ") in vec4 fog;\n";
}
// extra inputs?
for(int i = 0; i < features.num_inputs; i++) {
int size = uses_alpha ? 4 : 3;
src += "layout(location = " + std::to_string(next_input++) + ") in vec" + std::to_string(size) + " input" + std::to_string(i + 1) + ";\n";
}
src += "void main() {\n";
src += "gl_Position = position;\n";
src += "}";
return src;
}
static std::string gfx_vulkan_create_fragment_shader(const CCFeatures features) {
std::string src = "#version 460 core\n";
const bool uses_textures = features.used_textures[0] || features.used_textures[1];
const bool uses_alpha = features.opt_alpha;
const bool uses_fog = features.opt_fog;
const bool needs_screenpos = features.opt_alpha && features.opt_noise;
src += "layout(location = 0) out vec4 out_color;\n";
/*int next_input = 0;
if(uses_textures) {
src += "layout(location = " + std::to_string(next_input++) + ") in vec2 uv;\n";
}
if(needs_screenpos) {
src += "layout(location = " + std::to_string(next_input++) + ") in vec4 screen_pos;\n";
}
if(uses_fog) {
src += "layout(location = " + std::to_string(next_input++) + ") in vec4 fog;\n";
}
// extra inputs?
for(int i = 0; i < features.num_inputs; i++) {
int size = uses_alpha ? 4 : 3;
src += "layout(location = " + std::to_string(next_input++) + ") in vec" + std::to_string(size) + " input" + std::to_string(i + 1) + ";\n";
}*/
src += "void main() {\n";
src += "out_color = vec4(1, 1, 0, 1);\n";
src += "}";
return src;
}
static VkShaderModule gfx_vulkan_compile_glsl(const std::string& shader_src, const EShLanguage language) {
glslang::InitializeProcess(); // TODO: lol
const char* InputCString = shader_src.c_str();
glslang::TShader shader(language);
shader.setStrings(&InputCString, 1);
int ClientInputSemanticsVersion = 100; // maps to, say, #define VULKAN 100
shader.setEnvInput(glslang::EShSourceGlsl, language, glslang::EShClientVulkan, ClientInputSemanticsVersion);
// we are targeting vulkan 1.1, so that uses SPIR-V 1.3
shader.setEnvClient(glslang::EShClientVulkan, glslang::EShTargetVulkan_1_0);
shader.setEnvTarget(glslang::EShTargetSpv, glslang::EShTargetSpv_1_0);
if (!shader.parse(&DefaultTBuiltInResource, 100, false, EShMsgDefault)) {
printf("%s\n", shader.getInfoLog());
return {};
}
glslang::TProgram Program;
Program.addShader(&shader);
if (!Program.link(EShMsgDefault)) {
printf("failed to link %s %s %s\n", shader_src.data(), shader.getInfoLog(), shader.getInfoDebugLog());
return VK_NULL_HANDLE;
}
std::vector<unsigned int> SpirV;
spv::SpvBuildLogger logger;
glslang::SpvOptions spvOptions;
spvOptions.generateDebugInfo = true;
glslang::GlslangToSpv(*Program.getIntermediate(language), SpirV, &logger, &spvOptions);
return gfx_vulkan_create_shader_module(SpirV.data(), SpirV.size() * sizeof(uint32_t));
}
static std::pair<VkPipeline, VkPipelineLayout> gfx_vulkan_create_pipeline(const CCFeatures features) {
const std::string vertex_src = gfx_vulkan_create_vertex_shader(features);
const std::string fragment_src = gfx_vulkan_create_fragment_shader(features);
2022-10-04 10:20:38 -04:00
VkPipelineShaderStageCreateInfo vertex_stage = {};
vertex_stage.sType = VK_STRUCTURE_TYPE_PIPELINE_SHADER_STAGE_CREATE_INFO;
vertex_stage.stage = VK_SHADER_STAGE_VERTEX_BIT;
vertex_stage.module = gfx_vulkan_compile_glsl(vertex_src, EShLanguage::EShLangVertex);
2022-10-04 10:20:38 -04:00
vertex_stage.pName = "main";
VkPipelineShaderStageCreateInfo fragment_stage = {};
fragment_stage.sType = VK_STRUCTURE_TYPE_PIPELINE_SHADER_STAGE_CREATE_INFO;
fragment_stage.stage = VK_SHADER_STAGE_FRAGMENT_BIT;
fragment_stage.module = gfx_vulkan_compile_glsl(fragment_src, EShLanguage::EShLangFragment);
2022-10-04 10:20:38 -04:00
fragment_stage.pName = "main";
std::array<VkPipelineShaderStageCreateInfo, 2> shader_stages = { vertex_stage, fragment_stage };
const bool uses_textures = features.used_textures[0] || features.used_textures[1];
const bool uses_alpha = features.opt_alpha;
const bool uses_fog = features.opt_fog;
const bool needs_screenpos = features.opt_alpha && features.opt_noise;
int num_floats = 0;
int current_location = 1;
std::vector<VkVertexInputAttributeDescription> attributes;
2022-10-04 10:20:38 -04:00
VkVertexInputAttributeDescription position_attribute = {};
2022-10-04 10:34:29 -04:00
position_attribute.format = VK_FORMAT_R32G32B32A32_SFLOAT;
num_floats += 4;
attributes.push_back(position_attribute);
if(uses_textures) {
VkVertexInputAttributeDescription uv_attribute = {};
uv_attribute.format = VK_FORMAT_R32G32_SFLOAT;
uv_attribute.location = current_location++;
uv_attribute.offset = num_floats * sizeof(float);
num_floats += 2;
attributes.push_back(uv_attribute);
}
if(uses_fog) {
VkVertexInputAttributeDescription fog_attribute = {};
fog_attribute.format = VK_FORMAT_R32G32B32A32_SFLOAT;
fog_attribute.location = current_location++;
fog_attribute.offset = num_floats * sizeof(float);
2022-10-04 10:20:38 -04:00
num_floats += 4;
attributes.push_back(fog_attribute);
}
for(int i = 0; i < features.num_inputs; i++) {
VkVertexInputAttributeDescription extra_attribute = {};
extra_attribute.location = current_location++;
extra_attribute.offset = num_floats * sizeof(float);
if(uses_alpha) {
extra_attribute.format = VK_FORMAT_R32G32B32A32_SFLOAT;
num_floats += 4;
} else {
extra_attribute.format = VK_FORMAT_R32G32B32_SFLOAT;
num_floats += 3;
}
attributes.push_back(extra_attribute);
}
printf("uses textures: %d\n", uses_textures);
printf("needs screenpos: %d\n", needs_screenpos);
printf("uses fog: %d\n", uses_fog);
printf("extra inputs: %d\n", features.num_inputs);
VkVertexInputBindingDescription binding = {};
binding.stride = sizeof(float) * num_floats;
2022-10-05 20:34:03 -04:00
binding.inputRate = VK_VERTEX_INPUT_RATE_VERTEX;
2022-10-04 10:20:38 -04:00
VkPipelineVertexInputStateCreateInfo vertex_input_state = {};
vertex_input_state.sType = VK_STRUCTURE_TYPE_PIPELINE_VERTEX_INPUT_STATE_CREATE_INFO;
vertex_input_state.vertexBindingDescriptionCount = 1;
vertex_input_state.pVertexBindingDescriptions = &binding;
vertex_input_state.vertexAttributeDescriptionCount = attributes.size();
vertex_input_state.pVertexAttributeDescriptions = attributes.data();
VkPipelineInputAssemblyStateCreateInfo input_assembly = {};
input_assembly.sType = VK_STRUCTURE_TYPE_PIPELINE_INPUT_ASSEMBLY_STATE_CREATE_INFO;
input_assembly.topology = VK_PRIMITIVE_TOPOLOGY_TRIANGLE_LIST;
VkViewport viewport = {};
viewport.width = 640;
viewport.height = 480;
viewport.maxDepth = 1.0f;
VkRect2D scissor = {};
scissor.extent.width = 640;
scissor.extent.height = 480;
VkPipelineViewportStateCreateInfo viewport_state = {};
viewport_state.sType = VK_STRUCTURE_TYPE_PIPELINE_VIEWPORT_STATE_CREATE_INFO;
viewport_state.viewportCount = 1;
viewport_state.pViewports = &viewport;
viewport_state.scissorCount = 1;
viewport_state.pScissors = &scissor;
VkPipelineRasterizationStateCreateInfo rasterizer = {};
rasterizer.sType = VK_STRUCTURE_TYPE_PIPELINE_RASTERIZATION_STATE_CREATE_INFO;
rasterizer.lineWidth = 1.0f;
2022-10-04 10:34:29 -04:00
rasterizer.cullMode = VK_CULL_MODE_NONE;
rasterizer.frontFace = VK_FRONT_FACE_COUNTER_CLOCKWISE;
2022-10-04 10:20:38 -04:00
VkPipelineMultisampleStateCreateInfo multisampling = {};
multisampling.sType = VK_STRUCTURE_TYPE_PIPELINE_MULTISAMPLE_STATE_CREATE_INFO;
multisampling.rasterizationSamples = VK_SAMPLE_COUNT_1_BIT;
VkPipelineColorBlendAttachmentState color_blend_attachment = {};
color_blend_attachment.colorWriteMask = VK_COLOR_COMPONENT_R_BIT | VK_COLOR_COMPONENT_G_BIT | VK_COLOR_COMPONENT_B_BIT | VK_COLOR_COMPONENT_A_BIT;
VkPipelineColorBlendStateCreateInfo color_blending = {};
color_blending.sType = VK_STRUCTURE_TYPE_PIPELINE_COLOR_BLEND_STATE_CREATE_INFO;
color_blending.attachmentCount = 1;
color_blending.pAttachments = &color_blend_attachment;
VkPipelineDynamicStateCreateInfo dynamic_state = {};
dynamic_state.sType = VK_STRUCTURE_TYPE_PIPELINE_DYNAMIC_STATE_CREATE_INFO;
VkPipelineLayoutCreateInfo pipeline_layout_create_info = {};
pipeline_layout_create_info.sType = VK_STRUCTURE_TYPE_PIPELINE_LAYOUT_CREATE_INFO;
VkPipelineLayout layout = VK_NULL_HANDLE;
vkCreatePipelineLayout(device, &pipeline_layout_create_info, nullptr, &layout);
2022-10-04 10:20:38 -04:00
VkPipelineDepthStencilStateCreateInfo depth_stencil = {};
depth_stencil.sType = VK_STRUCTURE_TYPE_PIPELINE_DEPTH_STENCIL_STATE_CREATE_INFO;
depth_stencil.depthTestEnable = VK_TRUE;
depth_stencil.depthWriteEnable = VK_TRUE;
depth_stencil.depthCompareOp = VK_COMPARE_OP_LESS;
depth_stencil.maxDepthBounds = 1.0f;
VkGraphicsPipelineCreateInfo pipeline_create_info = {};
pipeline_create_info.sType = VK_STRUCTURE_TYPE_GRAPHICS_PIPELINE_CREATE_INFO;
pipeline_create_info.stageCount = shader_stages.size();
pipeline_create_info.pStages = shader_stages.data();
pipeline_create_info.pVertexInputState = &vertex_input_state;
pipeline_create_info.pInputAssemblyState = &input_assembly;
pipeline_create_info.pViewportState = &viewport_state;
pipeline_create_info.pRasterizationState = &rasterizer;
pipeline_create_info.pMultisampleState = &multisampling;
pipeline_create_info.pColorBlendState = &color_blending;
pipeline_create_info.pDynamicState = &dynamic_state;
pipeline_create_info.pDepthStencilState = &depth_stencil;
pipeline_create_info.layout = layout;
2022-10-04 10:20:38 -04:00
pipeline_create_info.renderPass = render_pass;
VkPipeline pipeline = VK_NULL_HANDLE;
2022-10-04 10:20:38 -04:00
vkCreateGraphicsPipelines(device,
VK_NULL_HANDLE,
1,
&pipeline_create_info,
nullptr,
&pipeline);
return {pipeline, layout};
2022-10-03 22:49:19 -04:00
}
// FIXME: reorganize
static void gfx_vulkan_renderer_load_shader(struct ShaderProgram *new_prg);
2022-10-03 22:49:19 -04:00
static struct ShaderProgram *gfx_vulkan_renderer_create_and_load_new_shader(uint32_t shader_id) {
ShaderProgramVulkan* shader = new ShaderProgramVulkan();
CCFeatures cc_features;
gfx_cc_get_features(shader_id, &cc_features);
auto [pipeline, layout] = gfx_vulkan_create_pipeline(cc_features);
shader->pipeline = pipeline;
shader->pipeline_layout = layout;
cached_shaders[shader_id] = shader;
gfx_vulkan_renderer_load_shader(reinterpret_cast<ShaderProgram *>(shader));
2022-10-05 20:34:03 -04:00
shader->used_textures[0] = cc_features.used_textures[0];
shader->used_textures[1] = cc_features.used_textures[1];
shader->num_inputs = cc_features.num_inputs;
return reinterpret_cast<ShaderProgram *>(shader);
2022-10-03 22:49:19 -04:00
}
static struct ShaderProgram *gfx_vulkan_renderer_lookup_shader(uint32_t shader_id) {
2022-10-05 20:34:03 -04:00
return reinterpret_cast<ShaderProgram *>(cached_shaders[shader_id]);
2022-10-03 22:49:19 -04:00
}
static void gfx_vulkan_renderer_shader_get_info(struct ShaderProgram *prg, uint8_t *num_inputs, bool used_textures[2]) {
2022-10-05 20:34:03 -04:00
ShaderProgramVulkan* vulkan = reinterpret_cast<ShaderProgramVulkan *>(prg);
*num_inputs = vulkan->num_inputs;
used_textures[0] = vulkan->used_textures[0];
used_textures[1] = vulkan->used_textures[1];
2022-10-03 22:49:19 -04:00
}
static uint32_t gfx_vulkan_renderer_new_texture(void) {
return 0;
}
static uint32_t gfx_vulkan_find_memory_type(uint32_t type_filter, VkMemoryPropertyFlags properties) {
VkPhysicalDeviceMemoryProperties memory_properties;
vkGetPhysicalDeviceMemoryProperties(physical_device, &memory_properties);
for (uint32_t i = 0; i < memory_properties.memoryTypeCount; i++) {
if ((type_filter & (1 << i)) &&
(memory_properties.memoryTypes[i].propertyFlags & properties) ==
properties) {
return i;
}
}
return -1;
}
2022-10-04 09:34:57 -04:00
static void gfx_vulkan_create_instance() {
std::vector<const char*> instance_extensions = {"VK_EXT_debug_utils"};
uint32_t extension_count = 0;
vkEnumerateInstanceExtensionProperties(nullptr, &extension_count, nullptr);
std::vector<VkExtensionProperties> extensions(extension_count);
vkEnumerateInstanceExtensionProperties(nullptr, &extension_count, extensions.data());
for(auto& extension : extensions) {
if (strstr(extension.extensionName, "surface") != nullptr) {
instance_extensions.push_back(extension.extensionName);
}
if (strstr(extension.extensionName, "VK_KHR_get_physical_device_properties2") != nullptr) {
instance_extensions.push_back(extension.extensionName);
}
}
VkDebugUtilsMessengerCreateInfoEXT debug_create_info = {};
debug_create_info.sType =
VK_STRUCTURE_TYPE_DEBUG_UTILS_MESSENGER_CREATE_INFO_EXT;
debug_create_info.messageSeverity =
VK_DEBUG_UTILS_MESSAGE_SEVERITY_WARNING_BIT_EXT |
VK_DEBUG_UTILS_MESSAGE_SEVERITY_ERROR_BIT_EXT;
debug_create_info.messageType =
VK_DEBUG_UTILS_MESSAGE_TYPE_VALIDATION_BIT_EXT;
debug_create_info.pfnUserCallback = gfx_vulkan_debug_callback;
VkInstanceCreateInfo create_info = {};
create_info.pNext = &debug_create_info;
create_info.sType = VK_STRUCTURE_TYPE_INSTANCE_CREATE_INFO;
create_info.ppEnabledExtensionNames = instance_extensions.data();
create_info.enabledExtensionCount = instance_extensions.size();
vkCreateInstance(&create_info, nullptr, &instance);
printf("Created vulkan instance!\n");
}
static void gfx_vulkan_create_command_pool(const int graphics_family_index) {
VkCommandPoolCreateInfo pool_create_info = {};
pool_create_info.sType = VK_STRUCTURE_TYPE_COMMAND_POOL_CREATE_INFO;
pool_create_info.queueFamilyIndex = graphics_family_index;
pool_create_info.flags = VK_COMMAND_POOL_CREATE_RESET_COMMAND_BUFFER_BIT;
vkCreateCommandPool(device, &pool_create_info, nullptr, &command_pool);
}
static void gfx_vulkan_create_device() {
// pick physical device
uint32_t device_count = 0;
vkEnumeratePhysicalDevices(instance, &device_count, nullptr);
std::vector<VkPhysicalDevice> devices(device_count);
vkEnumeratePhysicalDevices(instance, &device_count, devices.data());
for (auto device : devices) {
VkPhysicalDeviceProperties device_properties;
vkGetPhysicalDeviceProperties(device, &device_properties);
printf("GPU Found: %s\n", device_properties.deviceName);
}
physical_device = devices[0];
uint32_t extension_count = 0;
vkEnumerateDeviceExtensionProperties(physical_device, nullptr,
&extension_count, nullptr);
std::vector<VkExtensionProperties> extension_properties(extension_count);
vkEnumerateDeviceExtensionProperties(
physical_device, nullptr, &extension_count,
extension_properties.data());
uint32_t graphics_family_index = 0, present_family_index = 0;
// create logical device
uint32_t queue_family_count = 0;
vkGetPhysicalDeviceQueueFamilyProperties(physical_device, &queue_family_count,
nullptr);
std::vector<VkQueueFamilyProperties> queueFamilies(queue_family_count);
vkGetPhysicalDeviceQueueFamilyProperties(physical_device, &queue_family_count,
queueFamilies.data());
int i = 0;
for (const auto&queue_family : queueFamilies) {
if (queue_family.queueCount > 0 && queue_family.queueFlags & VK_QUEUE_GRAPHICS_BIT) {
graphics_family_index = i;
}
i++;
}
std::vector<VkDeviceQueueCreateInfo> queue_create_infos;
if (graphics_family_index == present_family_index) {
VkDeviceQueueCreateInfo queue_create_info = {};
queue_create_info.sType = VK_STRUCTURE_TYPE_DEVICE_QUEUE_CREATE_INFO;
queue_create_info.queueFamilyIndex = graphics_family_index;
queue_create_info.queueCount = 1;
float queuePriority = 1.0f;
queue_create_info.pQueuePriorities = &queuePriority;
queue_create_infos.push_back(queue_create_info);
} else {
// graphics
{
VkDeviceQueueCreateInfo queue_create_info = {};
queue_create_info.sType = VK_STRUCTURE_TYPE_DEVICE_QUEUE_CREATE_INFO;
queue_create_info.queueFamilyIndex = graphics_family_index;
queue_create_info.queueCount = 1;
float queuePriority = 1.0f;
queue_create_info.pQueuePriorities = &queuePriority;
queue_create_infos.push_back(queue_create_info);
}
// present
{
VkDeviceQueueCreateInfo queue_create_info = {};
queue_create_info.sType = VK_STRUCTURE_TYPE_DEVICE_QUEUE_CREATE_INFO;
queue_create_info.queueFamilyIndex = present_family_index;
queue_create_info.queueCount = 1;
float queuePriority = 1.0f;
queue_create_info.pQueuePriorities = &queuePriority;
queue_create_infos.push_back(queue_create_info);
}
}
std::array<const char*, 1> device_extensions = {"VK_KHR_swapchain"};
2022-10-04 09:34:57 -04:00
VkDeviceCreateInfo device_create_info = {};
device_create_info.sType = VK_STRUCTURE_TYPE_DEVICE_CREATE_INFO;
device_create_info.pQueueCreateInfos = queue_create_infos.data();
device_create_info.queueCreateInfoCount =
static_cast<uint32_t>(queue_create_infos.size());
device_create_info.ppEnabledExtensionNames = device_extensions.data();
device_create_info.enabledExtensionCount = device_extensions.size();
2022-10-04 09:34:57 -04:00
vkCreateDevice(physical_device, &device_create_info, nullptr, &device);
printf("Created vulkan device!\n");
vkGetDeviceQueue(device, graphics_family_index, 0, &graphics_queue);
vkGetDeviceQueue(device, present_family_index, 0, &present_queue);
gfx_vulkan_create_command_pool(graphics_family_index);
}
static void gfx_vulkan_create_swapchain() {
vkQueueWaitIdle(present_queue);
// create surface from wapi
VkSurfaceKHR surface =
static_cast<VkSurfaceKHR>(gfx_get_current_windowing_api()->create_surface(instance));
VkBool32 supported;
vkGetPhysicalDeviceSurfaceSupportKHR(physical_device, 0,
surface, &supported);
// query swapchain support
VkSurfaceCapabilitiesKHR capabilities;
vkGetPhysicalDeviceSurfaceCapabilitiesKHR(
physical_device, surface, &capabilities);
std::vector<VkSurfaceFormatKHR> formats;
uint32_t format_count;
vkGetPhysicalDeviceSurfaceFormatsKHR(
physical_device, surface, &format_count, nullptr);
formats.resize(format_count);
vkGetPhysicalDeviceSurfaceFormatsKHR(
physical_device, surface, &format_count, formats.data());
std::vector<VkPresentModeKHR> present_modes;
uint32_t present_mode_count;
vkGetPhysicalDeviceSurfacePresentModesKHR(
physical_device, surface, &present_mode_count, nullptr);
present_modes.resize(present_mode_count);
vkGetPhysicalDeviceSurfacePresentModesKHR(
physical_device, surface, &present_mode_count,
present_modes.data());
// choosing swapchain features
surface_format = formats[0].format;
for (const auto& available_format : formats) {
if (available_format.format == VK_FORMAT_B8G8R8A8_UNORM && available_format.colorSpace == VK_COLOR_SPACE_SRGB_NONLINEAR_KHR) {
surface_format = available_format.format;
}
}
VkPresentModeKHR present_mode = VK_PRESENT_MODE_FIFO_KHR;
for (const auto& available_mode : present_modes) {
if (available_mode == VK_PRESENT_MODE_MAILBOX_KHR) {
present_mode = available_mode;
}
}
uint32_t image_count = capabilities.minImageCount + 1;
if (capabilities.maxImageCount > 0 && image_count > capabilities.maxImageCount) {
image_count = capabilities.maxImageCount;
}
// create swapchain
VkSwapchainCreateInfoKHR swapchain_create_info = {};
swapchain_create_info.sType = VK_STRUCTURE_TYPE_SWAPCHAIN_CREATE_INFO_KHR;
swapchain_create_info.surface = surface;
swapchain_create_info.minImageCount = image_count;
swapchain_create_info.imageFormat = surface_format;
swapchain_create_info.imageColorSpace = VK_COLORSPACE_SRGB_NONLINEAR_KHR; // FIXME: hardcoded
swapchain_create_info.imageExtent.width = 640; // FIXME: hardcoded
swapchain_create_info.imageExtent.height = 480;
swapchain_create_info.imageArrayLayers = 1;
swapchain_create_info.imageUsage = VK_IMAGE_USAGE_COLOR_ATTACHMENT_BIT;
swapchain_create_info.imageSharingMode = VK_SHARING_MODE_EXCLUSIVE;
swapchain_create_info.preTransform = capabilities.currentTransform;
swapchain_create_info.compositeAlpha = VK_COMPOSITE_ALPHA_OPAQUE_BIT_KHR;
swapchain_create_info.presentMode = present_mode;
swapchain_create_info.clipped = VK_TRUE;
VkSwapchainKHR old_swapchain = swapchain;
swapchain_create_info.oldSwapchain = old_swapchain;
vkCreateSwapchainKHR(device, &swapchain_create_info, nullptr, &swapchain);
if(old_swapchain != VK_NULL_HANDLE) {
vkDestroySwapchainKHR(device, old_swapchain, nullptr);
}
vkGetSwapchainImagesKHR(device, swapchain, &image_count,
nullptr);
swapchain_images.resize(image_count);
vkGetSwapchainImagesKHR(device, swapchain, &image_count, swapchain_images.data());
swapchain_views.resize(swapchain_images.size());
for (size_t i = 0; i < swapchain_images.size(); i++) {
VkImageViewCreateInfo view_create_info = {};
view_create_info.sType = VK_STRUCTURE_TYPE_IMAGE_VIEW_CREATE_INFO;
view_create_info.image = swapchain_images[i];
view_create_info.viewType = VK_IMAGE_VIEW_TYPE_2D;
view_create_info.format = surface_format;
view_create_info.components.r = VK_COMPONENT_SWIZZLE_IDENTITY;
view_create_info.components.g = VK_COMPONENT_SWIZZLE_IDENTITY;
view_create_info.components.b = VK_COMPONENT_SWIZZLE_IDENTITY;
view_create_info.components.a = VK_COMPONENT_SWIZZLE_IDENTITY;
view_create_info.subresourceRange.aspectMask = VK_IMAGE_ASPECT_COLOR_BIT;
view_create_info.subresourceRange.baseMipLevel = 0;
view_create_info.subresourceRange.levelCount = 1;
view_create_info.subresourceRange.baseArrayLayer = 0;
view_create_info.subresourceRange.layerCount = 1;
vkCreateImageView(device, &view_create_info, nullptr,&swapchain_views[i]);
}
printf("Initialized swap chain!\n");
}
static void gfx_vulkan_create_render_pass() {
VkAttachmentDescription color_attachment = {};
color_attachment.format = surface_format;
color_attachment.samples = VK_SAMPLE_COUNT_1_BIT;
color_attachment.loadOp = VK_ATTACHMENT_LOAD_OP_CLEAR;
color_attachment.storeOp = VK_ATTACHMENT_STORE_OP_STORE;
color_attachment.stencilLoadOp = VK_ATTACHMENT_LOAD_OP_DONT_CARE;
color_attachment.stencilStoreOp = VK_ATTACHMENT_STORE_OP_DONT_CARE;
color_attachment.initialLayout = VK_IMAGE_LAYOUT_UNDEFINED;
color_attachment.finalLayout = VK_IMAGE_LAYOUT_PRESENT_SRC_KHR;
VkAttachmentReference color_attachment_ref = {};
color_attachment_ref.attachment = 0;
color_attachment_ref.layout = VK_IMAGE_LAYOUT_COLOR_ATTACHMENT_OPTIMAL;
VkAttachmentDescription depth_attachment = {};
depth_attachment.format = VK_FORMAT_D32_SFLOAT;
depth_attachment.samples = VK_SAMPLE_COUNT_1_BIT;
depth_attachment.loadOp = VK_ATTACHMENT_LOAD_OP_CLEAR;
depth_attachment.storeOp = VK_ATTACHMENT_STORE_OP_STORE;
depth_attachment.stencilLoadOp = VK_ATTACHMENT_LOAD_OP_DONT_CARE;
depth_attachment.stencilStoreOp = VK_ATTACHMENT_STORE_OP_DONT_CARE;
depth_attachment.initialLayout = VK_IMAGE_LAYOUT_UNDEFINED;
depth_attachment.finalLayout = VK_IMAGE_LAYOUT_DEPTH_STENCIL_ATTACHMENT_OPTIMAL;
VkAttachmentReference depth_attachment_ref = {};
depth_attachment_ref.attachment = 1;
depth_attachment_ref.layout = VK_IMAGE_LAYOUT_DEPTH_STENCIL_ATTACHMENT_OPTIMAL;
VkSubpassDependency dependency = {};
dependency.srcSubpass = VK_SUBPASS_EXTERNAL;
dependency.dstSubpass = 0;
dependency.srcStageMask = VK_PIPELINE_STAGE_COLOR_ATTACHMENT_OUTPUT_BIT | VK_PIPELINE_STAGE_EARLY_FRAGMENT_TESTS_BIT;
dependency.dstStageMask = VK_PIPELINE_STAGE_COLOR_ATTACHMENT_OUTPUT_BIT | VK_PIPELINE_STAGE_EARLY_FRAGMENT_TESTS_BIT;
dependency.srcAccessMask = 0;
dependency.dstAccessMask = VK_ACCESS_COLOR_ATTACHMENT_WRITE_BIT | VK_ACCESS_DEPTH_STENCIL_ATTACHMENT_WRITE_BIT;
dependency.dependencyFlags = 0;
VkSubpassDescription subpass = {};
subpass.pipelineBindPoint = VK_PIPELINE_BIND_POINT_GRAPHICS;
subpass.colorAttachmentCount = 1;
subpass.pColorAttachments = &color_attachment_ref;
subpass.pDepthStencilAttachment = &depth_attachment_ref;
std::array<VkAttachmentDescription, 2> attachments = { color_attachment, depth_attachment };
VkRenderPassCreateInfo render_pass_create_info = {};
render_pass_create_info.sType = VK_STRUCTURE_TYPE_RENDER_PASS_CREATE_INFO;
render_pass_create_info.attachmentCount = attachments.size();
render_pass_create_info.pAttachments = attachments.data();
render_pass_create_info.subpassCount = 1;
render_pass_create_info.pSubpasses = &subpass;
render_pass_create_info.dependencyCount = 1;
render_pass_create_info.pDependencies = &dependency;
vkCreateRenderPass(device, &render_pass_create_info, nullptr, &render_pass);
}
2022-10-04 10:01:45 -04:00
static void gfx_vulkan_create_sync() {
for(int i = 0; i < 3; i++) {
VkCommandBufferAllocateInfo allocate_info = {};
allocate_info.sType = VK_STRUCTURE_TYPE_COMMAND_BUFFER_ALLOCATE_INFO;
allocate_info.commandPool = command_pool;
allocate_info.level = VK_COMMAND_BUFFER_LEVEL_PRIMARY;
allocate_info.commandBufferCount = 1;
vkAllocateCommandBuffers(device, &allocate_info, &command_buffers[i]);
}
VkSemaphoreCreateInfo semaphore_create_info = {};
semaphore_create_info.sType = VK_STRUCTURE_TYPE_SEMAPHORE_CREATE_INFO;
VkFenceCreateInfo fence_create_info = {};
fence_create_info.sType = VK_STRUCTURE_TYPE_FENCE_CREATE_INFO;
fence_create_info.flags = VK_FENCE_CREATE_SIGNALED_BIT;
for (size_t i = 0; i < 3; i++) {
vkCreateSemaphore(device, &semaphore_create_info, nullptr, &image_available[i]);
vkCreateSemaphore(device, &semaphore_create_info, nullptr, &render_finished[i]);
vkCreateFence(device, &fence_create_info, nullptr, &in_flight[i]);
}
}
static void gfx_vulkan_create_depth() {
VkImageCreateInfo image_create_info = {};
image_create_info.sType = VK_STRUCTURE_TYPE_IMAGE_CREATE_INFO;
image_create_info.imageType = VK_IMAGE_TYPE_2D;
image_create_info.extent.width = 640;
image_create_info.extent.height = 480;
image_create_info.extent.depth = 1;
image_create_info.mipLevels = 1;
image_create_info.arrayLayers = 1;
image_create_info.format = VK_FORMAT_D32_SFLOAT;
image_create_info.tiling = VK_IMAGE_TILING_OPTIMAL;
image_create_info.initialLayout = VK_IMAGE_LAYOUT_UNDEFINED;
image_create_info.usage = VK_IMAGE_USAGE_DEPTH_STENCIL_ATTACHMENT_BIT;
image_create_info.samples = VK_SAMPLE_COUNT_1_BIT;
image_create_info.sharingMode = VK_SHARING_MODE_EXCLUSIVE;
vkCreateImage(device, &image_create_info, nullptr, &depth_image);
VkMemoryRequirements memory_requirements;
vkGetImageMemoryRequirements(device, depth_image, &memory_requirements);
VkMemoryAllocateInfo allocate_info = {};
allocate_info.sType = VK_STRUCTURE_TYPE_MEMORY_ALLOCATE_INFO;
allocate_info.allocationSize = memory_requirements.size;
allocate_info.memoryTypeIndex = gfx_vulkan_find_memory_type(memory_requirements.memoryTypeBits, VK_MEMORY_PROPERTY_DEVICE_LOCAL_BIT);
vkAllocateMemory(device, &allocate_info, nullptr, &depth_memory);
vkBindImageMemory(device, depth_image, depth_memory, 0);
VkImageViewCreateInfo view_create_info = {};
view_create_info.sType = VK_STRUCTURE_TYPE_IMAGE_VIEW_CREATE_INFO;
view_create_info.image = depth_image;
view_create_info.viewType = VK_IMAGE_VIEW_TYPE_2D;
view_create_info.format = VK_FORMAT_D32_SFLOAT;
view_create_info.subresourceRange.aspectMask = VK_IMAGE_ASPECT_DEPTH_BIT;
view_create_info.subresourceRange.levelCount = 1;
view_create_info.subresourceRange.layerCount = 1;
vkCreateImageView(device, &view_create_info, nullptr, &depth_view);
}
static void gfx_vulkan_create_framebuffers() {
swapchain_framebuffers.resize(swapchain_views.size());
for (size_t i = 0; i < swapchain_framebuffers.size(); i++) {
std::array<VkImageView, 2> attachments = {swapchain_views[i], depth_view};
VkFramebufferCreateInfo create_info = {};
create_info.sType = VK_STRUCTURE_TYPE_FRAMEBUFFER_CREATE_INFO;
create_info.renderPass = render_pass;
create_info.attachmentCount = attachments.size();
create_info.pAttachments = attachments.data();
create_info.width = 640;
create_info.height = 480;
create_info.layers = 1;
vkCreateFramebuffer(device, &create_info, nullptr, &swapchain_framebuffers[i]);
}
}
2022-10-05 20:34:03 -04:00
static void* mapped_data = nullptr;
2022-10-04 10:25:48 -04:00
static void gfx_vulkan_create_buffers() {
vkDeviceWaitIdle(device);
// create buffer
VkBufferCreateInfo create_info = {};
create_info.sType = VK_STRUCTURE_TYPE_BUFFER_CREATE_INFO;
2022-10-05 20:34:03 -04:00
create_info.size = 1048576;
2022-10-04 10:25:48 -04:00
create_info.usage = VK_BUFFER_USAGE_VERTEX_BUFFER_BIT | VK_BUFFER_USAGE_TRANSFER_DST_BIT;
create_info.sharingMode = VK_SHARING_MODE_EXCLUSIVE;
vkCreateBuffer(device, &create_info, nullptr, &vertex_buffer);
// allocate memory
VkMemoryRequirements memory_requirements;
vkGetBufferMemoryRequirements(device, vertex_buffer, &memory_requirements);
VkMemoryAllocateInfo allocate_info = {};
allocate_info.sType = VK_STRUCTURE_TYPE_MEMORY_ALLOCATE_INFO;
allocate_info.allocationSize = memory_requirements.size;
allocate_info.memoryTypeIndex =
gfx_vulkan_find_memory_type(memory_requirements.memoryTypeBits,
VK_MEMORY_PROPERTY_HOST_VISIBLE_BIT |
VK_MEMORY_PROPERTY_HOST_COHERENT_BIT);
vkAllocateMemory(device, &allocate_info, nullptr, &vertex_memory);
vkBindBufferMemory(device, vertex_buffer, vertex_memory, 0);
2022-10-05 20:34:03 -04:00
vkMapMemory(device, vertex_memory, 0, VK_WHOLE_SIZE, 0, &mapped_data);
2022-10-04 10:25:48 -04:00
}
2022-10-03 22:49:19 -04:00
static void gfx_vulkan_renderer_init(void) {
2022-10-04 09:34:57 -04:00
gfx_vulkan_create_instance();
gfx_vulkan_create_device();
gfx_vulkan_create_swapchain();
gfx_vulkan_create_render_pass();
2022-10-04 10:01:45 -04:00
gfx_vulkan_create_sync();
gfx_vulkan_create_depth();
gfx_vulkan_create_framebuffers();
2022-10-04 10:25:48 -04:00
gfx_vulkan_create_buffers();
2022-10-03 22:49:19 -04:00
}
static void gfx_vulkan_renderer_on_resize(void) {
2022-10-04 09:34:57 -04:00
// doesn't seem to be called?
2022-10-03 22:49:19 -04:00
}
static int buf_vbo_offset = 0;
2022-10-03 22:49:19 -04:00
static void gfx_vulkan_renderer_start_frame(void) {
2022-10-04 10:01:45 -04:00
vkWaitForFences(
device, 1,
&in_flight[current_frame],
VK_TRUE, std::numeric_limits<uint64_t>::max());
image_index = 0;
VkResult result = vkAcquireNextImageKHR(
device, swapchain,
std::numeric_limits<uint64_t>::max(),
image_available[current_frame],
VK_NULL_HANDLE, &image_index);
if (result == VK_ERROR_OUT_OF_DATE_KHR) {
printf("Swapchain is out of date!\n");
return;
}
current_cmd = command_buffers[current_frame];
VkCommandBufferBeginInfo buffer_begin = {};
buffer_begin.sType = VK_STRUCTURE_TYPE_COMMAND_BUFFER_BEGIN_INFO;
buffer_begin.flags = VK_COMMAND_BUFFER_USAGE_SIMULTANEOUS_USE_BIT;
vkBeginCommandBuffer(current_cmd, &buffer_begin);
VkRenderPassBeginInfo render_pass_begin = {};
render_pass_begin.sType = VK_STRUCTURE_TYPE_RENDER_PASS_BEGIN_INFO;
render_pass_begin.renderPass = render_pass;
render_pass_begin.framebuffer = swapchain_framebuffers[image_index];
std::array<VkClearValue, 2> clear_values = {};
clear_values[0].color.float32[0] = 0.8;
clear_values[0].color.float32[1] = 0.8;
clear_values[0].color.float32[2] = 0.8;
clear_values[0].color.float32[3] = 1.0;
clear_values[1].depthStencil = {1.0f, 0};
render_pass_begin.clearValueCount = clear_values.size();
render_pass_begin.pClearValues = clear_values.data();
render_pass_begin.renderArea.extent.width = 640;
render_pass_begin.renderArea.extent.height = 480;
2022-10-04 10:01:45 -04:00
vkCmdBeginRenderPass(current_cmd, &render_pass_begin, VK_SUBPASS_CONTENTS_INLINE);
buf_vbo_offset = 0;
2022-10-03 22:49:19 -04:00
}
2022-10-04 10:20:38 -04:00
/* draw commands */
static void gfx_vulkan_renderer_select_texture(int tile, uint32_t texture_id) {
}
static void gfx_vulkan_renderer_upload_texture(const uint8_t *rgba32_buf, int width, int height) {
}
static void gfx_vulkan_renderer_set_sampler_parameters(int tile, bool linear_filter, uint32_t cms, uint32_t cmt) {
}
static void gfx_vulkan_renderer_set_depth_test(bool depth_test) {
}
static void gfx_vulkan_renderer_set_depth_mask(bool z_upd) {
}
static void gfx_vulkan_renderer_set_zmode_decal(bool zmode_decal) {
}
static void gfx_vulkan_renderer_set_viewport(int x, int y, int width, int height) {
}
static void gfx_vulkan_renderer_set_scissor(int x, int y, int width, int height) {
}
static void gfx_vulkan_renderer_set_use_alpha(bool use_alpha) {
}
static void gfx_vulkan_renderer_unload_shader(struct ShaderProgram *old_prg) {
2022-10-04 10:20:38 -04:00
}
static void gfx_vulkan_renderer_load_shader(struct ShaderProgram *new_prg) {
ShaderProgramVulkan* vulkan = (ShaderProgramVulkan*)new_prg;
last_program = vulkan;
2022-10-04 10:20:38 -04:00
}
static void gfx_vulkan_renderer_draw_triangles(float buf_vbo[], size_t buf_vbo_len, size_t buf_vbo_num_tris) {
2022-10-05 20:34:03 -04:00
vkCmdBindPipeline(current_cmd, VK_PIPELINE_BIND_POINT_GRAPHICS, last_program->pipeline);
2022-10-04 10:25:48 -04:00
2022-10-05 20:34:03 -04:00
memcpy((uint8_t *)mapped_data + buf_vbo_offset, buf_vbo, buf_vbo_len * sizeof(float));
2022-10-04 10:25:48 -04:00
VkDeviceSize offsets[] = {static_cast<VkDeviceSize>(buf_vbo_offset)};
2022-10-04 10:25:48 -04:00
vkCmdBindVertexBuffers(current_cmd, 0, 1, &vertex_buffer, offsets);
2022-10-04 10:34:29 -04:00
vkCmdDraw(current_cmd, buf_vbo_num_tris * 3, 1, 0, 0);
buf_vbo_offset += buf_vbo_len * sizeof(float);
2022-10-04 10:20:38 -04:00
}
/* end draw commands */
2022-10-03 22:49:19 -04:00
static void gfx_vulkan_renderer_end_frame(void) {
vkCmdEndRenderPass(current_cmd);
2022-10-04 10:01:45 -04:00
vkEndCommandBuffer(current_cmd);
2022-10-03 22:49:19 -04:00
}
static void gfx_vulkan_renderer_finish_render(void) {
2022-10-04 10:01:45 -04:00
VkSubmitInfo submit_info = {};
submit_info.sType = VK_STRUCTURE_TYPE_SUBMIT_INFO;
VkSemaphore wait_semaphores[] = {image_available[current_frame]};
VkPipelineStageFlags wait_stages[] = {
VK_PIPELINE_STAGE_COLOR_ATTACHMENT_OUTPUT_BIT};
submit_info.waitSemaphoreCount = 1;
submit_info.pWaitSemaphores = wait_semaphores;
submit_info.pWaitDstStageMask = wait_stages;
submit_info.commandBufferCount = 1;
submit_info.pCommandBuffers = &current_cmd;
VkSemaphore signal_semaphores[] = {render_finished[current_frame]};
submit_info.signalSemaphoreCount = 1;
submit_info.pSignalSemaphores = signal_semaphores;
vkResetFences(device, 1, &in_flight[current_frame]);
if (vkQueueSubmit(graphics_queue, 1, &submit_info, in_flight[current_frame]) != VK_SUCCESS) {
return;
}
// present
VkPresentInfoKHR present_info = {};
present_info.sType = VK_STRUCTURE_TYPE_PRESENT_INFO_KHR;
present_info.waitSemaphoreCount = 1;
present_info.pWaitSemaphores = signal_semaphores;
VkSwapchainKHR swapChains[] = {swapchain};
present_info.swapchainCount = 1;
present_info.pSwapchains = swapChains;
present_info.pImageIndices = &image_index;
vkQueuePresentKHR(present_queue, &present_info);
current_frame = (current_frame + 1) % 3;
current_cmd = VK_NULL_HANDLE;
2022-10-03 22:49:19 -04:00
}
static const char* gfx_vulkan_get_name() {
return "Vulkan";
}
struct GfxRenderingAPI gfx_vulkan_api = {
gfx_vulkan_renderer_z_is_from_0_to_1,
gfx_vulkan_renderer_unload_shader,
gfx_vulkan_renderer_load_shader,
gfx_vulkan_renderer_create_and_load_new_shader,
gfx_vulkan_renderer_lookup_shader,
gfx_vulkan_renderer_shader_get_info,
gfx_vulkan_renderer_new_texture,
gfx_vulkan_renderer_select_texture,
gfx_vulkan_renderer_upload_texture,
gfx_vulkan_renderer_set_sampler_parameters,
gfx_vulkan_renderer_set_depth_test,
gfx_vulkan_renderer_set_depth_mask,
gfx_vulkan_renderer_set_zmode_decal,
gfx_vulkan_renderer_set_viewport,
gfx_vulkan_renderer_set_scissor,
gfx_vulkan_renderer_set_use_alpha,
gfx_vulkan_renderer_draw_triangles,
gfx_vulkan_renderer_init,
gfx_vulkan_renderer_on_resize,
gfx_vulkan_renderer_start_frame,
gfx_vulkan_renderer_end_frame,
gfx_vulkan_renderer_finish_render,
gfx_vulkan_get_name,
};