#include #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; uint8_t num_inputs; bool used_textures[2]; VkDescriptorSetLayout set_layout = VK_NULL_HANDLE; std::unordered_map cached_sets; }; std::unordered_map cached_shaders; static ShaderProgramVulkan* last_program = nullptr; struct TextureVulkan { VkImage image = VK_NULL_HANDLE; VkDeviceMemory memory = VK_NULL_HANDLE; VkImageView view = VK_NULL_HANDLE; VkSampler sampler = VK_NULL_HANDLE; }; static std::vector cached_textures; static int last_tile = 0; static std::array bound_textures; static VkDescriptorPool descriptor_pool; static uint32_t window_width, window_height; static VkViewport last_viewport; static VkRect2D last_scissor; static int get_texture_hash() { int hash = 0; for(int i = 0; i < bound_textures.size(); i++) { hash += bound_textures[i]; } return hash; } 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; int next_output = 0; if(uses_textures) { src += "layout(location = " + std::to_string(next_input++) + ") in vec2 uv;\n"; src += "layout(location = " + std::to_string(next_output++) + ") out vec2 out_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 += "layout(location = " + std::to_string(next_output++) + ") out vec" + std::to_string(size) + " output" + std::to_string(i + 1) + ";\n"; } src += "void main() {\n"; src += "gl_Position = position;\n"; if(uses_textures) { src += "out_uv = uv;\n"; } for(int i = 0; i < features.num_inputs; i++) { src += "output" + std::to_string(i + 1) + " = input" + std::to_string(i + 1) + ";\n"; } src += "}"; return src; } static const char *shader_item_to_str(uint32_t item, bool with_alpha, bool only_alpha, bool inputs_have_alpha, bool hint_single_element) { if (!only_alpha) { switch (item) { case SHADER_0: return with_alpha ? "vec4(0.0, 0.0, 0.0, 0.0)" : "vec3(0.0, 0.0, 0.0)"; case SHADER_INPUT_1: return with_alpha || !inputs_have_alpha ? "input1" : "input1.rgb"; case SHADER_INPUT_2: return with_alpha || !inputs_have_alpha ? "input2" : "input2.rgb"; case SHADER_INPUT_3: return with_alpha || !inputs_have_alpha ? "input3" : "input3.rgb"; case SHADER_INPUT_4: return with_alpha || !inputs_have_alpha ? "input4" : "input4.rgb"; case SHADER_TEXEL0: return with_alpha ? "tex_val_0" : "tex_val_0.rgb"; case SHADER_TEXEL0A: return hint_single_element ? "tex_val_0.a" : (with_alpha ? "vec4(tex_val_0.a, tex_val_0.a, tex_val_0.a, tex_val_0.a)" : "vec3(tex_val_0.a, tex_val_0.a, tex_val_0.a)"); case SHADER_TEXEL1: return with_alpha ? "tex_val_1" : "tex_val_1.rgb"; } } else { switch (item) { case SHADER_0: return "0.0"; case SHADER_INPUT_1: return "input1.a"; case SHADER_INPUT_2: return "input2.a"; case SHADER_INPUT_3: return "input3.a"; case SHADER_INPUT_4: return "input4.a"; case SHADER_TEXEL0: return "tex_val_0.a"; case SHADER_TEXEL0A: return "tex_val_0.a"; case SHADER_TEXEL1: return "tex_val_1.a"; } } } static void append_formula(std::string& buf, uint8_t c[2][4], bool do_single, bool do_multiply, bool do_mix, bool with_alpha, bool only_alpha, bool opt_alpha) { if (do_single) { buf += shader_item_to_str(c[only_alpha][3], with_alpha, only_alpha, opt_alpha, false); } else if (do_multiply) { buf += shader_item_to_str(c[only_alpha][0], with_alpha, only_alpha, opt_alpha, false); buf += " * "; buf += shader_item_to_str(c[only_alpha][2], with_alpha, only_alpha, opt_alpha, true); } else if (do_mix) { buf += "mix("; buf += shader_item_to_str(c[only_alpha][1], with_alpha, only_alpha, opt_alpha, false); buf += ", "; buf += shader_item_to_str(c[only_alpha][0], with_alpha, only_alpha, opt_alpha, false); buf += ", "; buf += shader_item_to_str(c[only_alpha][2], with_alpha, only_alpha, opt_alpha, true); buf += ")"; } else { buf += "("; buf += shader_item_to_str(c[only_alpha][0], with_alpha, only_alpha, opt_alpha, false); buf += " - "; buf += shader_item_to_str(c[only_alpha][1], with_alpha, only_alpha, opt_alpha, false); buf += ") * "; buf += shader_item_to_str(c[only_alpha][2], with_alpha, only_alpha, opt_alpha, true); buf += " + "; buf += shader_item_to_str(c[only_alpha][3], with_alpha, only_alpha, opt_alpha, false); } } static std::string gfx_vulkan_create_fragment_shader(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"; } if(features.used_textures[0]) { src += "layout(binding = 0) uniform sampler2D texture0;\n"; } if(features.used_textures[1]) { src += "layout(binding = 1) uniform sampler2D texture1;\n"; } src += "void main() {\n"; if(features.used_textures[0]) { src += "vec4 tex_val_0 = texture(texture0, uv);\n"; } if(features.used_textures[1]) { src += "vec4 tex_val_1 = texture(texture1, uv);\n"; } if(uses_alpha) { src += "vec4 texel = "; } else { src += "vec3 texel = "; } if(!features.color_alpha_same && features.opt_alpha) { src += "vec4("; append_formula(src, features.c, features.do_single[0], features.do_multiply[0], features.do_mix[0], false, false, true); src += ", "; append_formula(src, features.c, features.do_single[1], features.do_multiply[1], features.do_mix[1], true, true, true); src += ")"; } else { append_formula(src,features.c, features.do_single[0], features.do_multiply[0], features.do_mix[0], features.opt_alpha, false, features.opt_alpha); } src += ";\n"; if(uses_alpha) { src += "out_color = texel;\n"; } else { src += "out_color = vec4(texel, 1.0);\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::tuple 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(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.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; binding.inputRate = VK_VERTEX_INPUT_RATE_VERTEX; 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; VkPipelineViewportStateCreateInfo viewport_state = {}; viewport_state.sType = VK_STRUCTURE_TYPE_PIPELINE_VIEWPORT_STATE_CREATE_INFO; viewport_state.viewportCount = 1; viewport_state.scissorCount = 1; 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; const std::array dynamic_states = { VK_DYNAMIC_STATE_VIEWPORT, VK_DYNAMIC_STATE_SCISSOR }; VkPipelineDynamicStateCreateInfo dynamic_state = {}; dynamic_state.sType = VK_STRUCTURE_TYPE_PIPELINE_DYNAMIC_STATE_CREATE_INFO; dynamic_state.dynamicStateCount = dynamic_states.size(); dynamic_state.pDynamicStates = dynamic_states.data(); std::vector bindings; if(features.used_textures[0]) { VkDescriptorSetLayoutBinding textureBinding = {}; textureBinding.descriptorType = VK_DESCRIPTOR_TYPE_COMBINED_IMAGE_SAMPLER; textureBinding.descriptorCount = 1; textureBinding.stageFlags = VK_SHADER_STAGE_FRAGMENT_BIT; textureBinding.binding = 0; bindings.push_back(textureBinding); } if(features.used_textures[0]) { VkDescriptorSetLayoutBinding textureBinding = {}; textureBinding.descriptorType = VK_DESCRIPTOR_TYPE_COMBINED_IMAGE_SAMPLER; textureBinding.descriptorCount = 1; textureBinding.stageFlags = VK_SHADER_STAGE_FRAGMENT_BIT; textureBinding.binding = 1; bindings.push_back(textureBinding); } VkDescriptorSetLayoutCreateInfo layoutInfo = {}; layoutInfo.sType = VK_STRUCTURE_TYPE_DESCRIPTOR_SET_LAYOUT_CREATE_INFO; layoutInfo.bindingCount = bindings.size(); layoutInfo.pBindings = bindings.data(); VkDescriptorSetLayout set_layout; vkCreateDescriptorSetLayout(device, &layoutInfo, nullptr, &set_layout); VkPipelineLayoutCreateInfo pipeline_layout_create_info = {}; pipeline_layout_create_info.sType = VK_STRUCTURE_TYPE_PIPELINE_LAYOUT_CREATE_INFO; pipeline_layout_create_info.pSetLayouts = &set_layout; pipeline_layout_create_info.setLayoutCount = 1; 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, set_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, set_layout] = gfx_vulkan_create_pipeline(cc_features); shader->pipeline = pipeline; shader->pipeline_layout = layout; shader->set_layout = set_layout; cached_shaders[shader_id] = shader; gfx_vulkan_renderer_load_shader(reinterpret_cast(shader)); 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(shader); } static struct ShaderProgram *gfx_vulkan_renderer_lookup_shader(uint32_t shader_id) { return reinterpret_cast(cached_shaders[shader_id]); } static void gfx_vulkan_renderer_shader_get_info(struct ShaderProgram *prg, uint8_t *num_inputs, bool used_textures[2]) { ShaderProgramVulkan* vulkan = reinterpret_cast(prg); *num_inputs = vulkan->num_inputs; used_textures[0] = vulkan->used_textures[0]; used_textures[1] = vulkan->used_textures[1]; } static uint32_t gfx_vulkan_renderer_new_texture(void) { cached_textures.resize(cached_textures.size() + 1); return cached_textures.size() - 1; } 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; } gfx_get_current_windowing_api()->get_dimensions(&window_width, &window_height); // 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 = window_width; // FIXME: hardcoded swapchain_create_info.imageExtent.height = window_height; 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 = window_width; image_create_info.extent.height = window_height; 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 = window_width; create_info.height = window_height; create_info.layers = 1; vkCreateFramebuffer(device, &create_info, nullptr, &swapchain_framebuffers[i]); } } static void gfx_vulkan_create_descriptor_pools() { VkDescriptorPoolSize pool_size = {}; pool_size.type = VK_DESCRIPTOR_TYPE_COMBINED_IMAGE_SAMPLER; pool_size.descriptorCount = 500; const std::array pool_sizes = { pool_size }; VkDescriptorPoolCreateInfo pool_create_info = {}; pool_create_info.sType = VK_STRUCTURE_TYPE_DESCRIPTOR_POOL_CREATE_INFO; pool_create_info.poolSizeCount = pool_sizes.size(); pool_create_info.pPoolSizes = pool_sizes.data(); pool_create_info.maxSets = 500; vkCreateDescriptorPool(device, &pool_create_info, nullptr, &descriptor_pool); } static void gfx_vulkan_cache_descriptor() { ShaderProgramVulkan* vulkan = last_program; VkDescriptorSet set; VkDescriptorSetAllocateInfo allocateInfo = {}; allocateInfo.sType = VK_STRUCTURE_TYPE_DESCRIPTOR_SET_ALLOCATE_INFO; allocateInfo.descriptorPool = descriptor_pool; allocateInfo.descriptorSetCount = 1; allocateInfo.pSetLayouts = &vulkan->set_layout; vkAllocateDescriptorSets(device, &allocateInfo, &set); std::vector writes; VkDescriptorImageInfo imageInfo1 = {}; imageInfo1.imageLayout = VK_IMAGE_LAYOUT_SHADER_READ_ONLY_OPTIMAL; if(vulkan->used_textures[0]) { imageInfo1.imageView = cached_textures[bound_textures[0]].view; imageInfo1.sampler = cached_textures[bound_textures[0]].sampler; VkWriteDescriptorSet descriptorWrite2 = {}; descriptorWrite2.sType = VK_STRUCTURE_TYPE_WRITE_DESCRIPTOR_SET; descriptorWrite2.dstSet = set; descriptorWrite2.descriptorType = VK_DESCRIPTOR_TYPE_COMBINED_IMAGE_SAMPLER; descriptorWrite2.descriptorCount = 1; descriptorWrite2.pImageInfo = &imageInfo1; descriptorWrite2.dstBinding = 0; writes.push_back(descriptorWrite2); } VkDescriptorImageInfo imageInfo2 = {}; imageInfo2.imageLayout = VK_IMAGE_LAYOUT_SHADER_READ_ONLY_OPTIMAL; if(vulkan->used_textures[1]) { imageInfo2.imageView = cached_textures[bound_textures[1]].view; imageInfo2.sampler = cached_textures[bound_textures[1]].sampler; VkWriteDescriptorSet descriptorWrite2 = {}; descriptorWrite2.sType = VK_STRUCTURE_TYPE_WRITE_DESCRIPTOR_SET; descriptorWrite2.dstSet = set; descriptorWrite2.descriptorType = VK_DESCRIPTOR_TYPE_COMBINED_IMAGE_SAMPLER; descriptorWrite2.descriptorCount = 1; descriptorWrite2.pImageInfo = &imageInfo2; descriptorWrite2.dstBinding = 1; writes.push_back(descriptorWrite2); } vkUpdateDescriptorSets(device, writes.size(), writes.data(), 0, nullptr); vulkan->cached_sets[get_texture_hash()] = set; printf("Cached hash of %d\n", get_texture_hash()); } static void* mapped_data = nullptr; 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 = 1048576; 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); vkMapMemory(device, vertex_memory, 0, VK_WHOLE_SIZE, 0, &mapped_data); } 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(); gfx_vulkan_create_descriptor_pools(); } static void gfx_vulkan_renderer_on_resize(void) { gfx_vulkan_create_swapchain(); gfx_vulkan_create_depth(); gfx_vulkan_create_framebuffers(); } static int buf_vbo_offset = 0; 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 = window_width; render_pass_begin.renderArea.extent.height = window_height; vkCmdBeginRenderPass(current_cmd, &render_pass_begin, VK_SUBPASS_CONTENTS_INLINE); buf_vbo_offset = 0; } /* draw commands */ static void gfx_vulkan_renderer_select_texture(int tile, uint32_t texture_id) { last_tile = tile; bound_textures[last_tile] = texture_id; printf("selecting texture %d in slot %d\n", texture_id, tile); } static VkCommandBuffer gfx_vulkan_begin_singletime_commands() { VkCommandBufferAllocateInfo allocate_info = {}; allocate_info.sType = VK_STRUCTURE_TYPE_COMMAND_BUFFER_ALLOCATE_INFO; allocate_info.level = VK_COMMAND_BUFFER_LEVEL_PRIMARY; allocate_info.commandPool = command_pool; allocate_info.commandBufferCount = 1; VkCommandBuffer commandbuffer; vkAllocateCommandBuffers(device, &allocate_info, &commandbuffer); VkCommandBufferBeginInfo begin_info = {}; begin_info.sType = VK_STRUCTURE_TYPE_COMMAND_BUFFER_BEGIN_INFO; begin_info.flags = VK_COMMAND_BUFFER_USAGE_ONE_TIME_SUBMIT_BIT; vkBeginCommandBuffer(commandbuffer, &begin_info); return commandbuffer; } static void gfx_vulkan_end_singletime_commands(VkCommandBuffer commandbuffer) { vkEndCommandBuffer(commandbuffer); VkSubmitInfo submitInfo = {}; submitInfo.sType = VK_STRUCTURE_TYPE_SUBMIT_INFO; submitInfo.commandBufferCount = 1; submitInfo.pCommandBuffers = &commandbuffer; vkQueueSubmit(graphics_queue, 1, &submitInfo, VK_NULL_HANDLE); vkQueueWaitIdle(graphics_queue); vkFreeCommandBuffers(device, command_pool, 1, &commandbuffer); } void gfx_vulkan_transition_layout(VkCommandBuffer commandBuffer, VkImage image, VkFormat format, VkImageAspectFlags aspect, VkImageSubresourceRange range, VkImageLayout oldLayout, VkImageLayout newLayout, VkPipelineStageFlags src_stage_mask = VK_PIPELINE_STAGE_ALL_COMMANDS_BIT, VkPipelineStageFlags dst_stage_mask = VK_PIPELINE_STAGE_ALL_COMMANDS_BIT) { VkImageMemoryBarrier barrier = {}; barrier.sType = VK_STRUCTURE_TYPE_IMAGE_MEMORY_BARRIER; barrier.oldLayout = oldLayout; barrier.newLayout = newLayout; barrier.srcQueueFamilyIndex = VK_QUEUE_FAMILY_IGNORED; barrier.dstQueueFamilyIndex = VK_QUEUE_FAMILY_IGNORED; barrier.image = image; barrier.subresourceRange = range; barrier.subresourceRange.aspectMask = aspect; switch (oldLayout) { case VK_IMAGE_LAYOUT_UNDEFINED: barrier.srcAccessMask = 0; break; case VK_IMAGE_LAYOUT_TRANSFER_DST_OPTIMAL: barrier.srcAccessMask = VK_ACCESS_TRANSFER_WRITE_BIT; break; case VK_IMAGE_LAYOUT_SHADER_READ_ONLY_OPTIMAL: barrier.srcAccessMask = VK_ACCESS_SHADER_READ_BIT; break; case VK_IMAGE_LAYOUT_GENERAL: barrier.srcAccessMask = VK_ACCESS_SHADER_WRITE_BIT; break; default: break; } switch (newLayout) { case VK_IMAGE_LAYOUT_TRANSFER_DST_OPTIMAL: barrier.dstAccessMask = VK_ACCESS_TRANSFER_WRITE_BIT; break; case VK_IMAGE_LAYOUT_SHADER_READ_ONLY_OPTIMAL: barrier.dstAccessMask = VK_ACCESS_SHADER_READ_BIT; break; case VK_IMAGE_LAYOUT_GENERAL: barrier.dstAccessMask = VK_ACCESS_SHADER_READ_BIT; break; default: break; } vkCmdPipelineBarrier(commandBuffer, src_stage_mask, dst_stage_mask, 0, 0, nullptr, 0, nullptr, 1, &barrier); } static void gfx_vulkan_renderer_upload_texture(const uint8_t *rgba32_buf, int width, int height) { TextureVulkan& vulkan = cached_textures[bound_textures[last_tile]]; printf("Uploaded texture for %d\n", bound_textures[last_tile]); 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 = width; image_create_info.extent.height = height; image_create_info.extent.depth = 1; image_create_info.mipLevels = 1; image_create_info.arrayLayers = 1; image_create_info.format = VK_FORMAT_R8G8B8A8_UNORM; image_create_info.tiling = VK_IMAGE_TILING_OPTIMAL; image_create_info.initialLayout = VK_IMAGE_LAYOUT_UNDEFINED; image_create_info.usage = VK_IMAGE_USAGE_TRANSFER_DST_BIT | VK_IMAGE_USAGE_SAMPLED_BIT; image_create_info.sharingMode = VK_SHARING_MODE_EXCLUSIVE; image_create_info.samples = VK_SAMPLE_COUNT_1_BIT; vkCreateImage(device, &image_create_info, nullptr, &vulkan.image); VkMemoryRequirements memory_requirements; vkGetImageMemoryRequirements(device, vulkan.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, &vulkan.memory); vkBindImageMemory(device, vulkan.image, vulkan.memory, 0); const int data_size = 4 * width * height; // copy image data VkBuffer staging_buffer; VkDeviceMemory staging_memory; VkBufferCreateInfo buffer_create_info = {}; buffer_create_info.sType = VK_STRUCTURE_TYPE_BUFFER_CREATE_INFO; buffer_create_info.size = data_size; buffer_create_info.usage = VK_BUFFER_USAGE_TRANSFER_SRC_BIT; buffer_create_info.sharingMode = VK_SHARING_MODE_EXCLUSIVE; vkCreateBuffer(device, &buffer_create_info, nullptr, &staging_buffer); // allocate staging memory vkGetBufferMemoryRequirements(device, staging_buffer, &memory_requirements); 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, &staging_memory); vkBindBufferMemory(device, staging_buffer, staging_memory, 0); // copy to staging buffer void* data = nullptr; vkMapMemory(device, staging_memory, 0, data_size, 0, &data); memcpy(data, rgba32_buf, data_size); vkUnmapMemory(device, staging_memory); // copy staging buffer to image VkCommandBuffer commandbuffer = gfx_vulkan_begin_singletime_commands(); VkImageSubresourceRange range = {}; range.baseMipLevel = 0; range.levelCount = 1; range.baseArrayLayer = 0; range.layerCount = 1; gfx_vulkan_transition_layout(commandbuffer, vulkan.image, image_create_info.format, VK_IMAGE_ASPECT_COLOR_BIT, range, VK_IMAGE_LAYOUT_UNDEFINED, VK_IMAGE_LAYOUT_TRANSFER_DST_OPTIMAL); VkBufferImageCopy region = {}; region.imageSubresource.aspectMask = VK_IMAGE_ASPECT_COLOR_BIT; region.imageSubresource.mipLevel = 0; region.imageSubresource.baseArrayLayer = 0; region.imageSubresource.layerCount = 1; region.imageExtent = {(uint32_t)width, (uint32_t)height, 1}; vkCmdCopyBufferToImage(commandbuffer, staging_buffer, vulkan.image, VK_IMAGE_LAYOUT_TRANSFER_DST_OPTIMAL, 1, ®ion); gfx_vulkan_transition_layout(commandbuffer, vulkan.image, image_create_info.format, VK_IMAGE_ASPECT_COLOR_BIT, range, VK_IMAGE_LAYOUT_TRANSFER_DST_OPTIMAL, VK_IMAGE_LAYOUT_SHADER_READ_ONLY_OPTIMAL); gfx_vulkan_end_singletime_commands(commandbuffer); range = {}; range.levelCount = 1; range.layerCount = 1; range.aspectMask = VK_IMAGE_ASPECT_COLOR_BIT; VkImageViewCreateInfo viewInfo = {}; viewInfo.sType = VK_STRUCTURE_TYPE_IMAGE_VIEW_CREATE_INFO; viewInfo.image = vulkan.image; viewInfo.viewType = VK_IMAGE_VIEW_TYPE_2D; viewInfo.format = image_create_info.format; viewInfo.subresourceRange = range; vkCreateImageView(device, &viewInfo, nullptr, &vulkan.view); VkSamplerCreateInfo samplerInfo = {}; samplerInfo.sType = VK_STRUCTURE_TYPE_SAMPLER_CREATE_INFO; samplerInfo.magFilter = VK_FILTER_LINEAR; samplerInfo.minFilter = VK_FILTER_LINEAR; samplerInfo.addressModeU = VK_SAMPLER_ADDRESS_MODE_REPEAT; samplerInfo.addressModeV = VK_SAMPLER_ADDRESS_MODE_REPEAT; samplerInfo.addressModeW = VK_SAMPLER_ADDRESS_MODE_REPEAT; samplerInfo.mipmapMode = VK_SAMPLER_MIPMAP_MODE_LINEAR; samplerInfo.maxLod = 1.0f; vkCreateSampler(device, &samplerInfo, nullptr, &vulkan.sampler); } 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) { VkViewport viewport = {}; viewport.x = x; viewport.y = y; viewport.maxDepth = 1.0f; viewport.width = width; viewport.height = height; last_viewport = viewport; } static void gfx_vulkan_renderer_set_scissor(int x, int y, int width, int height) { VkRect2D scissor = {}; scissor.extent.width = width; scissor.extent.height = height; scissor.offset.x = x; scissor.offset.y = y; last_scissor = scissor; } 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; last_program = vulkan; } static void gfx_vulkan_renderer_draw_triangles(float buf_vbo[], size_t buf_vbo_len, size_t buf_vbo_num_tris) { vkCmdBindPipeline(current_cmd, VK_PIPELINE_BIND_POINT_GRAPHICS, last_program->pipeline); vkCmdSetViewport(current_cmd, 0, 1, &last_viewport); vkCmdSetScissor(current_cmd, 0, 1, &last_scissor); if(!last_program->cached_sets.count(get_texture_hash())) { gfx_vulkan_cache_descriptor(); } vkCmdBindDescriptorSets(current_cmd, VK_PIPELINE_BIND_POINT_GRAPHICS, last_program->pipeline_layout, 0, 1, &last_program->cached_sets[get_texture_hash()], 0, nullptr); memcpy((uint8_t *)mapped_data + buf_vbo_offset, buf_vbo, buf_vbo_len * sizeof(float)); VkDeviceSize offsets[] = {static_cast(buf_vbo_offset)}; vkCmdBindVertexBuffers(current_cmd, 0, 1, &vertex_buffer, offsets); vkCmdDraw(current_cmd, buf_vbo_num_tris * 3, 1, 0, 0); buf_vbo_offset += buf_vbo_len * sizeof(float); } /* 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, };