#include #include #include #include #include #include #include #include #include #include #include #include "gfx_rendering_api.h" #include "gfx_pc.h" #include "gfx_window_manager_api.h" #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 swapchain_images; static std::vector 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 swapchain_framebuffers; // TODO: removed hardcoded 3 frame queue, should be variable static std::array command_buffers; static std::array image_available; static std::array render_finished; static std::array in_flight; static uint32_t current_frame = 0; static uint32_t image_index = 0; static VkCommandBuffer current_cmd = VK_NULL_HANDLE; 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; }; std::unordered_map cached_shaders; static ShaderProgramVulkan* last_program = nullptr; 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; } static bool gfx_vulkan_renderer_z_is_from_0_to_1(void) { return false; } 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(code); VkShaderModule shader_module; vkCreateShaderModule(device, &create_info, nullptr, &shader_module); return shader_module; } // 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 buffer(fileSize); file.seekg(0); file.read(buffer.data(), fileSize); return gfx_vulkan_create_shader_module(reinterpret_cast(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"; } 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 += "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 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 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); 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); 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); fragment_stage.pName = "main"; std::array 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 attributes; VkVertexInputAttributeDescription position_attribute = {}; 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(needs_screenpos) { VkVertexInputAttributeDescription screenpos_attribute = {}; screenpos_attribute.format = VK_FORMAT_R32G32B32A32_SFLOAT; screenpos_attribute.location = current_location++; screenpos_attribute.offset = num_floats * sizeof(float); num_floats += 4; attributes.push_back(screenpos_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); num_floats += 4; attributes.push_back(fog_attribute); } for(int i = 0; i < features.num_inputs; i++) { VkVertexInputAttributeDescription extra_attribute = {}; extra_attribute.format = VK_FORMAT_R32G32B32A32_SFLOAT; extra_attribute.location = current_location++; extra_attribute.offset = num_floats * sizeof(float); num_floats += 4; 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; 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; rasterizer.cullMode = VK_CULL_MODE_NONE; rasterizer.frontFace = VK_FRONT_FACE_COUNTER_CLOCKWISE; 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); 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; pipeline_create_info.renderPass = render_pass; VkPipeline pipeline = VK_NULL_HANDLE; vkCreateGraphicsPipelines(device, VK_NULL_HANDLE, 1, &pipeline_create_info, nullptr, &pipeline); return {pipeline, layout}; } // FIXME: reorganize static void gfx_vulkan_renderer_load_shader(struct ShaderProgram *new_prg); 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(shader)); return reinterpret_cast(shader); } static struct ShaderProgram *gfx_vulkan_renderer_lookup_shader(uint32_t shader_id) { return NULL; } static void gfx_vulkan_renderer_shader_get_info(struct ShaderProgram *prg, uint8_t *num_inputs, bool used_textures[2]) { *num_inputs = 0; used_textures[0] = false; used_textures[1] = false; } 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; } static void gfx_vulkan_create_instance() { std::vector instance_extensions = {"VK_EXT_debug_utils"}; uint32_t extension_count = 0; vkEnumerateInstanceExtensionProperties(nullptr, &extension_count, nullptr); std::vector 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 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 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 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 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 device_extensions = {"VK_KHR_swapchain"}; 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(queue_create_infos.size()); device_create_info.ppEnabledExtensionNames = device_extensions.data(); device_create_info.enabledExtensionCount = device_extensions.size(); 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(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 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 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 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); } 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 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]); } } static void gfx_vulkan_create_buffers() { vkDeviceWaitIdle(device); // create buffer VkBufferCreateInfo create_info = {}; create_info.sType = VK_STRUCTURE_TYPE_BUFFER_CREATE_INFO; create_info.size = 60000; 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); } static void gfx_vulkan_renderer_init(void) { gfx_vulkan_create_instance(); gfx_vulkan_create_device(); gfx_vulkan_create_swapchain(); gfx_vulkan_create_render_pass(); gfx_vulkan_create_sync(); gfx_vulkan_create_depth(); gfx_vulkan_create_framebuffers(); gfx_vulkan_create_buffers(); } static void gfx_vulkan_renderer_on_resize(void) { // doesn't seem to be called? } static void gfx_vulkan_renderer_start_frame(void) { vkWaitForFences( device, 1, &in_flight[current_frame], VK_TRUE, std::numeric_limits::max()); image_index = 0; VkResult result = vkAcquireNextImageKHR( device, swapchain, std::numeric_limits::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 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; vkCmdBeginRenderPass(current_cmd, &render_pass_begin, VK_SUBPASS_CONTENTS_INLINE); } /* 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) { } static void gfx_vulkan_renderer_load_shader(struct ShaderProgram *new_prg) { ShaderProgramVulkan* vulkan = (ShaderProgramVulkan*)new_prg; if(current_cmd != VK_NULL_HANDLE) { vkCmdBindPipeline(current_cmd, VK_PIPELINE_BIND_POINT_GRAPHICS, vulkan->pipeline); } last_program = vulkan; } static void gfx_vulkan_renderer_draw_triangles(float buf_vbo[], size_t buf_vbo_len, size_t buf_vbo_num_tris) { if(last_program != nullptr) { gfx_vulkan_renderer_load_shader(reinterpret_cast(last_program)); } void* mapped_data = nullptr; vkMapMemory(device, vertex_memory, 0, buf_vbo_len, 0, &mapped_data); memcpy(mapped_data, buf_vbo, buf_vbo_len * sizeof(float)); VkMappedMemoryRange range = {}; range.sType = VK_STRUCTURE_TYPE_MAPPED_MEMORY_RANGE; range.memory = vertex_memory; range.size = buf_vbo_len; vkFlushMappedMemoryRanges(device, 1, &range); vkUnmapMemory(device, vertex_memory); VkDeviceSize offsets[] = {0}; vkCmdBindVertexBuffers(current_cmd, 0, 1, &vertex_buffer, offsets); vkCmdDraw(current_cmd, buf_vbo_num_tris * 3, 1, 0, 0); } /* end draw commands */ static void gfx_vulkan_renderer_end_frame(void) { vkCmdEndRenderPass(current_cmd); vkEndCommandBuffer(current_cmd); } static void gfx_vulkan_renderer_finish_render(void) { 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 = ¤t_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; } 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, };