diff options
Diffstat (limited to 'drivers/vulkan')
-rw-r--r-- | drivers/vulkan/SCsub | 2 | ||||
-rw-r--r-- | drivers/vulkan/rendering_device_vulkan.cpp | 1094 | ||||
-rw-r--r-- | drivers/vulkan/rendering_device_vulkan.h | 144 | ||||
-rw-r--r-- | drivers/vulkan/vulkan_context.cpp | 703 | ||||
-rw-r--r-- | drivers/vulkan/vulkan_context.h | 76 |
5 files changed, 1638 insertions, 381 deletions
diff --git a/drivers/vulkan/SCsub b/drivers/vulkan/SCsub index 14b9d63204..6a2a57d18c 100644 --- a/drivers/vulkan/SCsub +++ b/drivers/vulkan/SCsub @@ -105,7 +105,7 @@ elif env["builtin_vulkan"]: else: # Always build VMA. thirdparty_dir = "#thirdparty/vulkan" - env.Prepend(CPPPATH=[thirdparty_dir]) + env.Prepend(CPPPATH=[thirdparty_dir, thirdparty_dir + "/include"]) # Build Vulkan loader library env_thirdparty = env.Clone() diff --git a/drivers/vulkan/rendering_device_vulkan.cpp b/drivers/vulkan/rendering_device_vulkan.cpp index 9584dd3f67..5bf3bbf8a4 100644 --- a/drivers/vulkan/rendering_device_vulkan.cpp +++ b/drivers/vulkan/rendering_device_vulkan.cpp @@ -31,7 +31,7 @@ #include "rendering_device_vulkan.h" #include "core/config/project_settings.h" -#include "core/os/file_access.h" +#include "core/io/file_access.h" #include "core/os/os.h" #include "core/templates/hashfuncs.h" #include "drivers/vulkan/vulkan_context.h" @@ -119,7 +119,7 @@ static void update_external_dependency_for_store(VkSubpassDependency &dependency } if (is_depth) { - // Depth resources have addtional stages that may be interested in them + // Depth resources have additional stages that may be interested in them dependency.dstStageMask |= VK_PIPELINE_STAGE_EARLY_FRAGMENT_TESTS_BIT | VK_PIPELINE_STAGE_LATE_FRAGMENT_TESTS_BIT; dependency.dstAccessMask |= VK_ACCESS_DEPTH_STENCIL_ATTACHMENT_READ_BIT | VK_ACCESS_DEPTH_STENCIL_ATTACHMENT_WRITE_BIT; } @@ -1398,12 +1398,15 @@ Error RenderingDeviceVulkan::_buffer_allocate(Buffer *p_buffer, uint32_t p_size, p_buffer->buffer_info.range = p_size; p_buffer->usage = p_usage; + buffer_memory += p_size; + return OK; } Error RenderingDeviceVulkan::_buffer_free(Buffer *p_buffer) { ERR_FAIL_COND_V(p_buffer->size == 0, ERR_INVALID_PARAMETER); + buffer_memory -= p_buffer->size; vmaDestroyBuffer(allocator, p_buffer->buffer, p_buffer->allocation); p_buffer->buffer = VK_NULL_HANDLE; p_buffer->allocation = nullptr; @@ -1486,7 +1489,7 @@ Error RenderingDeviceVulkan::_staging_buffer_allocate(uint32_t p_amount, uint32_ // possible in a single frame if (staging_buffer_blocks[staging_buffer_current].frame_used == frames_drawn) { //guess we did.. ok, let's see if we can insert a new block.. - if (staging_buffer_blocks.size() * staging_buffer_block_size < staging_buffer_max_size) { + if ((uint64_t)staging_buffer_blocks.size() * staging_buffer_block_size < staging_buffer_max_size) { //we can, so we are safe Error err = _insert_staging_block(); if (err) { @@ -1530,7 +1533,7 @@ Error RenderingDeviceVulkan::_staging_buffer_allocate(uint32_t p_amount, uint32_ staging_buffer_blocks.write[staging_buffer_current].fill_amount = 0; } else if (staging_buffer_blocks[staging_buffer_current].frame_used > frames_drawn - frame_count) { //this block may still be in use, let's not touch it unless we have to, so.. can we create a new one? - if (staging_buffer_blocks.size() * staging_buffer_block_size < staging_buffer_max_size) { + if ((uint64_t)staging_buffer_blocks.size() * staging_buffer_block_size < staging_buffer_max_size) { //we are still allowed to create a new block, so let's do that and insert it for current pos Error err = _insert_staging_block(); if (err) { @@ -1600,7 +1603,7 @@ Error RenderingDeviceVulkan::_buffer_update(Buffer *p_buffer, size_t p_offset, c } //copy to staging buffer - copymem(((uint8_t *)data_ptr) + block_write_offset, p_data + submit_from, block_write_amount); + memcpy(((uint8_t *)data_ptr) + block_write_offset, p_data + submit_from, block_write_amount); //unmap vmaUnmapMemory(allocator, staging_buffer_blocks[staging_buffer_current].allocation); @@ -1896,7 +1899,7 @@ RID RenderingDeviceVulkan::texture_create(const TextureFormat &p_format, const T VkResult err = vmaCreateImage(allocator, &image_create_info, &allocInfo, &texture.image, &texture.allocation, &texture.allocation_info); ERR_FAIL_COND_V_MSG(err, RID(), "vmaCreateImage failed with error " + itos(err) + "."); - + image_memory += texture.allocation_info.size; texture.type = p_format.texture_type; texture.format = p_format.format; texture.width = image_create_info.extent.width; @@ -2029,7 +2032,7 @@ RID RenderingDeviceVulkan::texture_create(const TextureFormat &p_format, const T if (p_data.size()) { for (uint32_t i = 0; i < image_create_info.arrayLayers; i++) { - texture_update(id, i, p_data[i]); + _texture_update(id, i, p_data[i], RD::BARRIER_MASK_ALL, true); } } return id; @@ -2276,10 +2279,14 @@ RID RenderingDeviceVulkan::texture_create_shared_from_slice(const TextureView &p } Error RenderingDeviceVulkan::texture_update(RID p_texture, uint32_t p_layer, const Vector<uint8_t> &p_data, uint32_t p_post_barrier) { + return _texture_update(p_texture, p_layer, p_data, p_post_barrier, false); +} + +Error RenderingDeviceVulkan::_texture_update(RID p_texture, uint32_t p_layer, const Vector<uint8_t> &p_data, uint32_t p_post_barrier, bool p_use_setup_queue) { _THREAD_SAFE_METHOD_ - ERR_FAIL_COND_V_MSG(draw_list || compute_list, ERR_INVALID_PARAMETER, - "Updating textures in is forbidden during creation of a draw or compute list"); + ERR_FAIL_COND_V_MSG((draw_list || compute_list) && !p_use_setup_queue, ERR_INVALID_PARAMETER, + "Updating textures is forbidden during creation of a draw or compute list"); Texture *texture = texture_owner.getornull(p_texture); ERR_FAIL_COND_V(!texture, ERR_INVALID_PARAMETER); @@ -2320,7 +2327,7 @@ Error RenderingDeviceVulkan::texture_update(RID p_texture, uint32_t p_layer, con const uint8_t *r = p_data.ptr(); - VkCommandBuffer command_buffer = p_post_barrier ? frames[frame].draw_command_buffer : frames[frame].setup_command_buffer; + VkCommandBuffer command_buffer = p_use_setup_queue ? frames[frame].setup_command_buffer : frames[frame].draw_command_buffer; //barrier to transfer { @@ -2373,7 +2380,7 @@ Error RenderingDeviceVulkan::texture_update(RID p_texture, uint32_t p_layer, con to_allocate >>= get_compressed_image_format_pixel_rshift(texture->format); uint32_t alloc_offset, alloc_size; - Error err = _staging_buffer_allocate(to_allocate, required_align, alloc_offset, alloc_size, false, p_post_barrier); + Error err = _staging_buffer_allocate(to_allocate, required_align, alloc_offset, alloc_size, false, !p_use_setup_queue); ERR_FAIL_COND_V(err, ERR_CANT_CREATE); uint8_t *write_ptr; @@ -2558,7 +2565,7 @@ Vector<uint8_t> RenderingDeviceVulkan::_texture_get_data_from_image(Texture *tex const uint8_t *rptr = slice_read_ptr + y * layout.rowPitch; uint8_t *wptr = write_ptr + y * line_width; - copymem(wptr, rptr, line_width); + memcpy(wptr, rptr, line_width); } } else { @@ -2566,7 +2573,7 @@ Vector<uint8_t> RenderingDeviceVulkan::_texture_get_data_from_image(Texture *tex for (uint32_t y = 0; y < height; y++) { const uint8_t *rptr = slice_read_ptr + y * layout.rowPitch; uint8_t *wptr = write_ptr + y * pixel_size * width; - copymem(wptr, rptr, pixel_size * width); + memcpy(wptr, rptr, (uint64_t)pixel_size * width); } } } @@ -2699,7 +2706,7 @@ Vector<uint8_t> RenderingDeviceVulkan::texture_get_data(RID p_texture, uint32_t { buffer_data.resize(buffer_size); uint8_t *w = buffer_data.ptrw(); - copymem(w, buffer_mem, buffer_size); + memcpy(w, buffer_mem, buffer_size); } vmaUnmapMemory(allocator, tmp_buffer.allocation); @@ -3108,7 +3115,7 @@ Error RenderingDeviceVulkan::texture_clear(RID p_texture, const Color &p_color, VkImageLayout clear_layout = (src_tex->layout == VK_IMAGE_LAYOUT_GENERAL) ? VK_IMAGE_LAYOUT_GENERAL : VK_IMAGE_LAYOUT_TRANSFER_DST_OPTIMAL; - // NOTE: Perhaps the valid stages/accesses for a given onwner should be a property of the owner. (Here and places like _get_buffer_from_owner) + // NOTE: Perhaps the valid stages/accesses for a given owner should be a property of the owner. (Here and places like _get_buffer_from_owner) const VkPipelineStageFlags valid_texture_stages = VK_PIPELINE_STAGE_VERTEX_SHADER_BIT | VK_PIPELINE_STAGE_FRAGMENT_SHADER_BIT | VK_PIPELINE_STAGE_COMPUTE_SHADER_BIT; constexpr VkAccessFlags read_access = VK_ACCESS_SHADER_READ_BIT; constexpr VkAccessFlags read_write_access = VK_ACCESS_SHADER_READ_BIT | VK_ACCESS_SHADER_WRITE_BIT; @@ -3244,13 +3251,8 @@ bool RenderingDeviceVulkan::texture_is_format_supported_for_usage(DataFormat p_f /**** ATTACHMENT ****/ /********************/ -VkRenderPass RenderingDeviceVulkan::_render_pass_create(const Vector<AttachmentFormat> &p_format, InitialAction p_initial_color_action, FinalAction p_final_color_action, InitialAction p_initial_depth_action, FinalAction p_final_depth_action, int *r_color_attachment_count) { - Vector<VkAttachmentDescription> attachments; - Vector<VkAttachmentReference> color_references; - Vector<VkAttachmentReference> depth_stencil_references; - Vector<VkAttachmentReference> resolve_references; - - // Set up a dependencies from/to external equivalent to the default (implicit) one, and then amend them +VkRenderPass RenderingDeviceVulkan::_render_pass_create(const Vector<AttachmentFormat> &p_attachments, const Vector<FramebufferPass> &p_passes, InitialAction p_initial_action, FinalAction p_final_action, InitialAction p_initial_depth_action, FinalAction p_final_depth_action, uint32_t p_view_count, Vector<TextureSamples> *r_samples) { + // Set up dependencies from/to external equivalent to the default (implicit) one, and then amend them const VkPipelineStageFlags default_access_mask = VK_ACCESS_INPUT_ATTACHMENT_READ_BIT | VK_ACCESS_COLOR_ATTACHMENT_READ_BIT | VK_ACCESS_COLOR_ATTACHMENT_WRITE_BIT | @@ -3262,27 +3264,31 @@ VkRenderPass RenderingDeviceVulkan::_render_pass_create(const Vector<AttachmentF { 0, VK_SUBPASS_EXTERNAL, VK_PIPELINE_STAGE_ALL_GRAPHICS_BIT, VK_PIPELINE_STAGE_BOTTOM_OF_PIPE_BIT, default_access_mask, 0, 0 } }; VkSubpassDependency &dependency_from_external = dependencies[0]; VkSubpassDependency &dependency_to_external = dependencies[1]; + LocalVector<int32_t> attachment_last_pass; + attachment_last_pass.resize(p_attachments.size()); - for (int i = 0; i < p_format.size(); i++) { - ERR_FAIL_INDEX_V(p_format[i].format, DATA_FORMAT_MAX, VK_NULL_HANDLE); - ERR_FAIL_INDEX_V(p_format[i].samples, TEXTURE_SAMPLES_MAX, VK_NULL_HANDLE); - ERR_FAIL_COND_V_MSG(!(p_format[i].usage_flags & (TEXTURE_USAGE_COLOR_ATTACHMENT_BIT | TEXTURE_USAGE_DEPTH_STENCIL_ATTACHMENT_BIT | TEXTURE_USAGE_RESOLVE_ATTACHMENT_BIT)), + Vector<VkAttachmentDescription> attachments; + + for (int i = 0; i < p_attachments.size(); i++) { + ERR_FAIL_INDEX_V(p_attachments[i].format, DATA_FORMAT_MAX, VK_NULL_HANDLE); + ERR_FAIL_INDEX_V(p_attachments[i].samples, TEXTURE_SAMPLES_MAX, VK_NULL_HANDLE); + ERR_FAIL_COND_V_MSG(!(p_attachments[i].usage_flags & (TEXTURE_USAGE_COLOR_ATTACHMENT_BIT | TEXTURE_USAGE_DEPTH_STENCIL_ATTACHMENT_BIT | TEXTURE_USAGE_RESOLVE_ATTACHMENT_BIT)), VK_NULL_HANDLE, "Texture format for index (" + itos(i) + ") requires an attachment (depth, stencil or resolve) bit set."); VkAttachmentDescription description = {}; description.flags = 0; - description.format = vulkan_formats[p_format[i].format]; - description.samples = rasterization_sample_count[p_format[i].samples]; + description.format = vulkan_formats[p_attachments[i].format]; + description.samples = rasterization_sample_count[p_attachments[i].samples]; - bool is_depth_stencil = p_format[i].usage_flags & TEXTURE_USAGE_DEPTH_STENCIL_ATTACHMENT_BIT; - bool is_sampled = p_format[i].usage_flags & TEXTURE_USAGE_SAMPLING_BIT; - bool is_storage = p_format[i].usage_flags & TEXTURE_USAGE_STORAGE_BIT; + bool is_sampled = p_attachments[i].usage_flags & TEXTURE_USAGE_SAMPLING_BIT; + bool is_storage = p_attachments[i].usage_flags & TEXTURE_USAGE_STORAGE_BIT; + bool is_depth = p_attachments[i].usage_flags & TEXTURE_USAGE_DEPTH_STENCIL_ATTACHMENT_BIT; // For each UNDEFINED, assume the prior use was a *read*, as we'd be discarding the output of a write - // Also, each UNDEFINED will do an immediate layout transition (write), s.t. we must ensure execution syncronization vs. - // the read. If this is a performance issue, one could track the actual last accessor of each resource, adding only that + // Also, each UNDEFINED will do an immediate layout transition (write), s.t. we must ensure execution synchronization vs. + // the read. If this is a performance issue, one could track the actual last accessor of each resource, adding only that // stage - switch (is_depth_stencil ? p_initial_depth_action : p_initial_color_action) { + switch (is_depth ? p_initial_depth_action : p_initial_action) { case INITIAL_ACTION_CLEAR_REGION: case INITIAL_ACTION_CLEAR: { description.loadOp = VK_ATTACHMENT_LOAD_OP_CLEAR; @@ -3291,11 +3297,11 @@ VkRenderPass RenderingDeviceVulkan::_render_pass_create(const Vector<AttachmentF dependency_from_external.srcStageMask |= reading_stages; } break; case INITIAL_ACTION_KEEP: { - if (p_format[i].usage_flags & TEXTURE_USAGE_COLOR_ATTACHMENT_BIT) { + if (p_attachments[i].usage_flags & TEXTURE_USAGE_COLOR_ATTACHMENT_BIT) { description.loadOp = VK_ATTACHMENT_LOAD_OP_LOAD; description.initialLayout = is_sampled ? VK_IMAGE_LAYOUT_SHADER_READ_ONLY_OPTIMAL : (is_storage ? VK_IMAGE_LAYOUT_GENERAL : VK_IMAGE_LAYOUT_COLOR_ATTACHMENT_OPTIMAL); description.stencilLoadOp = VK_ATTACHMENT_LOAD_OP_DONT_CARE; - } else if (p_format[i].usage_flags & TEXTURE_USAGE_DEPTH_STENCIL_ATTACHMENT_BIT) { + } else if (p_attachments[i].usage_flags & TEXTURE_USAGE_DEPTH_STENCIL_ATTACHMENT_BIT) { description.loadOp = VK_ATTACHMENT_LOAD_OP_LOAD; description.initialLayout = is_sampled ? VK_IMAGE_LAYOUT_SHADER_READ_ONLY_OPTIMAL : (is_storage ? VK_IMAGE_LAYOUT_GENERAL : VK_IMAGE_LAYOUT_DEPTH_STENCIL_ATTACHMENT_OPTIMAL); description.stencilLoadOp = VK_ATTACHMENT_LOAD_OP_LOAD; @@ -3308,11 +3314,11 @@ VkRenderPass RenderingDeviceVulkan::_render_pass_create(const Vector<AttachmentF } } break; case INITIAL_ACTION_DROP: { - if (p_format[i].usage_flags & TEXTURE_USAGE_COLOR_ATTACHMENT_BIT) { + if (p_attachments[i].usage_flags & TEXTURE_USAGE_COLOR_ATTACHMENT_BIT) { description.loadOp = VK_ATTACHMENT_LOAD_OP_DONT_CARE; description.initialLayout = is_sampled ? VK_IMAGE_LAYOUT_SHADER_READ_ONLY_OPTIMAL : (is_storage ? VK_IMAGE_LAYOUT_GENERAL : VK_IMAGE_LAYOUT_COLOR_ATTACHMENT_OPTIMAL); description.stencilLoadOp = VK_ATTACHMENT_LOAD_OP_DONT_CARE; - } else if (p_format[i].usage_flags & TEXTURE_USAGE_DEPTH_STENCIL_ATTACHMENT_BIT) { + } else if (p_attachments[i].usage_flags & TEXTURE_USAGE_DEPTH_STENCIL_ATTACHMENT_BIT) { description.loadOp = VK_ATTACHMENT_LOAD_OP_DONT_CARE; description.initialLayout = VK_IMAGE_LAYOUT_UNDEFINED; //don't care what is there description.stencilLoadOp = VK_ATTACHMENT_LOAD_OP_DONT_CARE; @@ -3326,11 +3332,11 @@ VkRenderPass RenderingDeviceVulkan::_render_pass_create(const Vector<AttachmentF } break; case INITIAL_ACTION_CLEAR_REGION_CONTINUE: case INITIAL_ACTION_CONTINUE: { - if (p_format[i].usage_flags & TEXTURE_USAGE_COLOR_ATTACHMENT_BIT) { + if (p_attachments[i].usage_flags & TEXTURE_USAGE_COLOR_ATTACHMENT_BIT) { description.loadOp = VK_ATTACHMENT_LOAD_OP_LOAD; description.initialLayout = VK_IMAGE_LAYOUT_COLOR_ATTACHMENT_OPTIMAL; description.stencilLoadOp = VK_ATTACHMENT_LOAD_OP_DONT_CARE; - } else if (p_format[i].usage_flags & TEXTURE_USAGE_DEPTH_STENCIL_ATTACHMENT_BIT) { + } else if (p_attachments[i].usage_flags & TEXTURE_USAGE_DEPTH_STENCIL_ATTACHMENT_BIT) { description.loadOp = VK_ATTACHMENT_LOAD_OP_LOAD; description.initialLayout = VK_IMAGE_LAYOUT_DEPTH_STENCIL_ATTACHMENT_OPTIMAL; description.stencilLoadOp = VK_ATTACHMENT_LOAD_OP_LOAD; @@ -3346,14 +3352,14 @@ VkRenderPass RenderingDeviceVulkan::_render_pass_create(const Vector<AttachmentF } } - switch (is_depth_stencil ? p_final_depth_action : p_final_color_action) { + switch (is_depth ? p_final_depth_action : p_final_action) { case FINAL_ACTION_READ: { - if (p_format[i].usage_flags & TEXTURE_USAGE_COLOR_ATTACHMENT_BIT) { + if (p_attachments[i].usage_flags & TEXTURE_USAGE_COLOR_ATTACHMENT_BIT) { description.storeOp = VK_ATTACHMENT_STORE_OP_STORE; description.stencilStoreOp = VK_ATTACHMENT_STORE_OP_DONT_CARE; description.finalLayout = is_sampled ? VK_IMAGE_LAYOUT_SHADER_READ_ONLY_OPTIMAL : (is_storage ? VK_IMAGE_LAYOUT_GENERAL : VK_IMAGE_LAYOUT_COLOR_ATTACHMENT_OPTIMAL); update_external_dependency_for_store(dependency_to_external, is_sampled, is_storage, false); - } else if (p_format[i].usage_flags & TEXTURE_USAGE_DEPTH_STENCIL_ATTACHMENT_BIT) { + } else if (p_attachments[i].usage_flags & TEXTURE_USAGE_DEPTH_STENCIL_ATTACHMENT_BIT) { description.storeOp = VK_ATTACHMENT_STORE_OP_STORE; description.stencilStoreOp = VK_ATTACHMENT_STORE_OP_STORE; description.finalLayout = is_sampled ? VK_IMAGE_LAYOUT_SHADER_READ_ONLY_OPTIMAL : (is_storage ? VK_IMAGE_LAYOUT_GENERAL : VK_IMAGE_LAYOUT_DEPTH_STENCIL_ATTACHMENT_OPTIMAL); @@ -3366,11 +3372,11 @@ VkRenderPass RenderingDeviceVulkan::_render_pass_create(const Vector<AttachmentF } } break; case FINAL_ACTION_DISCARD: { - if (p_format[i].usage_flags & TEXTURE_USAGE_COLOR_ATTACHMENT_BIT) { + if (p_attachments[i].usage_flags & TEXTURE_USAGE_COLOR_ATTACHMENT_BIT) { description.storeOp = VK_ATTACHMENT_STORE_OP_DONT_CARE; description.stencilStoreOp = VK_ATTACHMENT_STORE_OP_DONT_CARE; description.finalLayout = is_sampled ? VK_IMAGE_LAYOUT_SHADER_READ_ONLY_OPTIMAL : (is_storage ? VK_IMAGE_LAYOUT_GENERAL : VK_IMAGE_LAYOUT_COLOR_ATTACHMENT_OPTIMAL); - } else if (p_format[i].usage_flags & TEXTURE_USAGE_DEPTH_STENCIL_ATTACHMENT_BIT) { + } else if (p_attachments[i].usage_flags & TEXTURE_USAGE_DEPTH_STENCIL_ATTACHMENT_BIT) { description.storeOp = VK_ATTACHMENT_STORE_OP_DONT_CARE; description.stencilStoreOp = VK_ATTACHMENT_STORE_OP_DONT_CARE; description.finalLayout = is_sampled ? VK_IMAGE_LAYOUT_SHADER_READ_ONLY_OPTIMAL : (is_storage ? VK_IMAGE_LAYOUT_GENERAL : VK_IMAGE_LAYOUT_DEPTH_STENCIL_ATTACHMENT_OPTIMAL); @@ -3381,11 +3387,11 @@ VkRenderPass RenderingDeviceVulkan::_render_pass_create(const Vector<AttachmentF } } break; case FINAL_ACTION_CONTINUE: { - if (p_format[i].usage_flags & TEXTURE_USAGE_COLOR_ATTACHMENT_BIT) { + if (p_attachments[i].usage_flags & TEXTURE_USAGE_COLOR_ATTACHMENT_BIT) { description.storeOp = VK_ATTACHMENT_STORE_OP_STORE; description.stencilStoreOp = VK_ATTACHMENT_STORE_OP_DONT_CARE; description.finalLayout = VK_IMAGE_LAYOUT_COLOR_ATTACHMENT_OPTIMAL; - } else if (p_format[i].usage_flags & TEXTURE_USAGE_DEPTH_STENCIL_ATTACHMENT_BIT) { + } else if (p_attachments[i].usage_flags & TEXTURE_USAGE_DEPTH_STENCIL_ATTACHMENT_BIT) { description.storeOp = VK_ATTACHMENT_STORE_OP_STORE; description.stencilStoreOp = VK_ATTACHMENT_STORE_OP_STORE; description.finalLayout = VK_IMAGE_LAYOUT_DEPTH_STENCIL_ATTACHMENT_OPTIMAL; @@ -3401,89 +3407,281 @@ VkRenderPass RenderingDeviceVulkan::_render_pass_create(const Vector<AttachmentF } } + attachment_last_pass[i] = -1; + attachments.push_back(description); + } - VkAttachmentReference reference; - reference.attachment = i; + LocalVector<VkSubpassDescription> subpasses; + LocalVector<LocalVector<VkAttachmentReference>> color_reference_array; + LocalVector<LocalVector<VkAttachmentReference>> input_reference_array; + LocalVector<LocalVector<VkAttachmentReference>> resolve_reference_array; + LocalVector<LocalVector<uint32_t>> preserve_reference_array; + LocalVector<VkAttachmentReference> depth_reference_array; - if (p_format[i].usage_flags & TEXTURE_USAGE_COLOR_ATTACHMENT_BIT) { - reference.layout = VK_IMAGE_LAYOUT_COLOR_ATTACHMENT_OPTIMAL; + subpasses.resize(p_passes.size()); + color_reference_array.resize(p_passes.size()); + input_reference_array.resize(p_passes.size()); + resolve_reference_array.resize(p_passes.size()); + preserve_reference_array.resize(p_passes.size()); + depth_reference_array.resize(p_passes.size()); + + LocalVector<VkSubpassDependency> subpass_dependencies; + + for (int i = 0; i < p_passes.size(); i++) { + const FramebufferPass *pass = &p_passes[i]; + + LocalVector<VkAttachmentReference> &color_references = color_reference_array[i]; + + TextureSamples texture_samples = TEXTURE_SAMPLES_1; + bool is_multisample_first = true; + + for (int j = 0; j < pass->color_attachments.size(); j++) { + int32_t attachment = pass->color_attachments[j]; + VkAttachmentReference reference; + if (attachment == FramebufferPass::ATTACHMENT_UNUSED) { + reference.attachment = VK_ATTACHMENT_UNUSED; + reference.layout = VK_IMAGE_LAYOUT_UNDEFINED; + } else { + ERR_FAIL_INDEX_V_MSG(attachment, p_attachments.size(), VK_NULL_HANDLE, "Invalid framebuffer format attachment(" + itos(attachment) + "), in pass (" + itos(i) + "), color attachment (" + itos(j) + ")."); + ERR_FAIL_COND_V_MSG(!(p_attachments[attachment].usage_flags & TEXTURE_USAGE_COLOR_ATTACHMENT_BIT), VK_NULL_HANDLE, "Invalid framebuffer format attachment(" + itos(attachment) + "), in pass (" + itos(i) + "), it's marked as depth, but it's not usable as color attachment."); + ERR_FAIL_COND_V_MSG(attachment_last_pass[attachment] == i, VK_NULL_HANDLE, "Invalid framebuffer format attachment(" + itos(attachment) + "), in pass (" + itos(i) + "), it already was used for something else before in this pass."); + + if (is_multisample_first) { + texture_samples = p_attachments[attachment].samples; + is_multisample_first = false; + } else { + ERR_FAIL_COND_V_MSG(texture_samples != p_attachments[attachment].samples, VK_NULL_HANDLE, "Invalid framebuffer format attachment(" + itos(attachment) + "), in pass (" + itos(i) + "), if an attachment is marked as multisample, all of them should be multisample and use the same number of samples."); + } + reference.attachment = attachment; + reference.layout = VK_IMAGE_LAYOUT_COLOR_ATTACHMENT_OPTIMAL; + attachment_last_pass[attachment] = i; + } color_references.push_back(reference); - } else if (p_format[i].usage_flags & TEXTURE_USAGE_DEPTH_STENCIL_ATTACHMENT_BIT) { - reference.layout = VK_IMAGE_LAYOUT_DEPTH_STENCIL_ATTACHMENT_OPTIMAL; - depth_stencil_references.push_back(reference); - } else if (p_format[i].usage_flags & TEXTURE_USAGE_RESOLVE_ATTACHMENT_BIT) { - reference.layout = VK_IMAGE_LAYOUT_COLOR_ATTACHMENT_OPTIMAL; + } + + LocalVector<VkAttachmentReference> &input_references = input_reference_array[i]; + + for (int j = 0; j < pass->input_attachments.size(); j++) { + int32_t attachment = pass->input_attachments[j]; + VkAttachmentReference reference; + if (attachment == FramebufferPass::ATTACHMENT_UNUSED) { + reference.attachment = VK_ATTACHMENT_UNUSED; + reference.layout = VK_IMAGE_LAYOUT_UNDEFINED; + } else { + ERR_FAIL_INDEX_V_MSG(attachment, p_attachments.size(), VK_NULL_HANDLE, "Invalid framebuffer format attachment(" + itos(attachment) + "), in pass (" + itos(i) + "), input attachment (" + itos(j) + ")."); + ERR_FAIL_COND_V_MSG(!(p_attachments[attachment].usage_flags & TEXTURE_USAGE_COLOR_ATTACHMENT_BIT), VK_NULL_HANDLE, "Invalid framebuffer format attachment(" + itos(attachment) + "), in pass (" + itos(i) + "), it's marked as depth, but it's not usable as input attachment."); + ERR_FAIL_COND_V_MSG(attachment_last_pass[attachment] == i, VK_NULL_HANDLE, "Invalid framebuffer format attachment(" + itos(attachment) + "), in pass (" + itos(i) + "), it already was used for something else before in this pass."); + reference.attachment = attachment; + reference.layout = VK_IMAGE_LAYOUT_SHADER_READ_ONLY_OPTIMAL; + attachment_last_pass[attachment] = i; + } + input_references.push_back(reference); + } + + LocalVector<VkAttachmentReference> &resolve_references = resolve_reference_array[i]; + + if (pass->resolve_attachments.size() > 0) { + ERR_FAIL_COND_V_MSG(pass->resolve_attachments.size() != pass->color_attachments.size(), VK_NULL_HANDLE, "The amount of resolve attachments (" + itos(pass->resolve_attachments.size()) + ") must match the number of color attachments (" + itos(pass->color_attachments.size()) + ")."); + ERR_FAIL_COND_V_MSG(texture_samples == TEXTURE_SAMPLES_1, VK_NULL_HANDLE, "Resolve attachments specified, but color attachments are not multisample."); + } + for (int j = 0; j < pass->resolve_attachments.size(); j++) { + int32_t attachment = pass->resolve_attachments[j]; + VkAttachmentReference reference; + if (attachment == FramebufferPass::ATTACHMENT_UNUSED) { + reference.attachment = VK_ATTACHMENT_UNUSED; + reference.layout = VK_IMAGE_LAYOUT_UNDEFINED; + } else { + ERR_FAIL_INDEX_V_MSG(attachment, p_attachments.size(), VK_NULL_HANDLE, "Invalid framebuffer format attachment(" + itos(attachment) + "), in pass (" + itos(i) + "), resolve attachment (" + itos(j) + ")."); + ERR_FAIL_COND_V_MSG(pass->color_attachments[j] == FramebufferPass::ATTACHMENT_UNUSED, VK_NULL_HANDLE, "Invalid framebuffer format attachment(" + itos(attachment) + "), in pass (" + itos(i) + "), resolve attachment (" + itos(j) + "), the respective color attachment is marked as unused."); + ERR_FAIL_COND_V_MSG(!(p_attachments[attachment].usage_flags & TEXTURE_USAGE_COLOR_ATTACHMENT_BIT), VK_NULL_HANDLE, "Invalid framebuffer format attachment(" + itos(attachment) + "), in pass (" + itos(i) + "), it's marked as depth, but it's not usable as resolve attachment."); + ERR_FAIL_COND_V_MSG(attachment_last_pass[attachment] == i, VK_NULL_HANDLE, "Invalid framebuffer format attachment(" + itos(attachment) + "), in pass (" + itos(i) + "), it already was used for something else before in this pass."); + bool multisample = p_attachments[attachment].samples > TEXTURE_SAMPLES_1; + ERR_FAIL_COND_V_MSG(multisample, VK_NULL_HANDLE, "Invalid framebuffer format attachment(" + itos(attachment) + "), in pass (" + itos(i) + "), resolve attachments can't be multisample."); + reference.attachment = attachment; + reference.layout = VK_IMAGE_LAYOUT_SHADER_READ_ONLY_OPTIMAL; + attachment_last_pass[attachment] = i; + } resolve_references.push_back(reference); - // if resolves are done, we need to ensure the copy is safe - dependency_to_external.dstStageMask |= VK_PIPELINE_STAGE_TRANSFER_BIT; - dependency_to_external.dstAccessMask |= VK_ACCESS_TRANSFER_READ_BIT; + } + + LocalVector<uint32_t> &preserve_references = preserve_reference_array[i]; + + for (int j = 0; j < pass->preserve_attachments.size(); j++) { + int32_t attachment = pass->preserve_attachments[j]; + + ERR_FAIL_COND_V_MSG(attachment == FramebufferPass::ATTACHMENT_UNUSED, VK_NULL_HANDLE, "Invalid framebuffer format attachment(" + itos(attachment) + "), in pass (" + itos(i) + "), preserve attachment (" + itos(j) + "). Preserve attachments can't be unused."); + + ERR_FAIL_INDEX_V_MSG(attachment, p_attachments.size(), VK_NULL_HANDLE, "Invalid framebuffer format attachment(" + itos(attachment) + "), in pass (" + itos(i) + "), preserve attachment (" + itos(j) + ")."); + ERR_FAIL_COND_V_MSG(attachment_last_pass[attachment] == i, VK_NULL_HANDLE, "Invalid framebuffer format attachment(" + itos(attachment) + "), in pass (" + itos(i) + "), it already was used for something else before in this pass."); + + attachment_last_pass[attachment] = i; + + preserve_references.push_back(attachment); + } + + VkAttachmentReference &depth_stencil_reference = depth_reference_array[i]; + + if (pass->depth_attachment != FramebufferPass::ATTACHMENT_UNUSED) { + int32_t attachment = pass->depth_attachment; + ERR_FAIL_INDEX_V_MSG(attachment, p_attachments.size(), VK_NULL_HANDLE, "Invalid framebuffer depth format attachment(" + itos(attachment) + "), in pass (" + itos(i) + "), depth attachment."); + ERR_FAIL_COND_V_MSG(!(p_attachments[attachment].usage_flags & TEXTURE_USAGE_DEPTH_STENCIL_ATTACHMENT_BIT), VK_NULL_HANDLE, "Invalid framebuffer depth format attachment(" + itos(attachment) + "), in pass (" + itos(i) + "), it's marked as depth, but it's not a depth attachment."); + ERR_FAIL_COND_V_MSG(attachment_last_pass[attachment] == i, VK_NULL_HANDLE, "Invalid framebuffer depth format attachment(" + itos(attachment) + "), in pass (" + itos(i) + "), it already was used for something else before in this pass."); + depth_stencil_reference.attachment = attachment; + depth_stencil_reference.layout = VK_IMAGE_LAYOUT_DEPTH_STENCIL_ATTACHMENT_OPTIMAL; + attachment_last_pass[attachment] = i; + + if (is_multisample_first) { + texture_samples = p_attachments[attachment].samples; + is_multisample_first = false; + } else { + ERR_FAIL_COND_V_MSG(texture_samples != p_attachments[attachment].samples, VK_NULL_HANDLE, "Invalid framebuffer depth format attachment(" + itos(attachment) + "), in pass (" + itos(i) + "), if an attachment is marked as multisample, all of them should be multisample and use the same number of samples including the depth."); + } + + } else { + depth_stencil_reference.attachment = VK_ATTACHMENT_UNUSED; + depth_stencil_reference.layout = VK_IMAGE_LAYOUT_UNDEFINED; + } + + VkSubpassDescription &subpass = subpasses[i]; + subpass.flags = 0; + subpass.pipelineBindPoint = VK_PIPELINE_BIND_POINT_GRAPHICS; + subpass.inputAttachmentCount = input_references.size(); + if (input_references.size()) { + subpass.pInputAttachments = input_references.ptr(); + } else { + subpass.pInputAttachments = nullptr; + } + subpass.colorAttachmentCount = color_references.size(); + if (color_references.size()) { + subpass.pColorAttachments = color_references.ptr(); } else { - ERR_FAIL_V_MSG(VK_NULL_HANDLE, "Texture index " + itos(i) + " is neither color, depth stencil or resolve so it can't be used as attachment."); + subpass.pColorAttachments = nullptr; + } + if (depth_stencil_reference.attachment != VK_ATTACHMENT_UNUSED) { + subpass.pDepthStencilAttachment = &depth_stencil_reference; + } else { + subpass.pDepthStencilAttachment = nullptr; + } + + if (resolve_references.size()) { + subpass.pResolveAttachments = resolve_references.ptr(); + } else { + subpass.pResolveAttachments = nullptr; + } + + subpass.preserveAttachmentCount = preserve_references.size(); + if (preserve_references.size()) { + subpass.pPreserveAttachments = preserve_references.ptr(); + } else { + subpass.pPreserveAttachments = nullptr; + } + + if (r_samples) { + r_samples->push_back(texture_samples); } + if (i > 0) { + VkSubpassDependency dependency; + dependency.srcSubpass = i - 1; + dependency.dstSubpass = i; + dependency.srcStageMask = 0; + dependency.srcStageMask = VK_PIPELINE_STAGE_COLOR_ATTACHMENT_OUTPUT_BIT | VK_PIPELINE_STAGE_EARLY_FRAGMENT_TESTS_BIT | VK_PIPELINE_STAGE_LATE_FRAGMENT_TESTS_BIT; + dependency.dstStageMask = VK_PIPELINE_STAGE_COLOR_ATTACHMENT_OUTPUT_BIT | VK_PIPELINE_STAGE_EARLY_FRAGMENT_TESTS_BIT | VK_PIPELINE_STAGE_LATE_FRAGMENT_TESTS_BIT | VK_PIPELINE_STAGE_FRAGMENT_SHADER_BIT; + + dependency.srcAccessMask = VK_ACCESS_COLOR_ATTACHMENT_WRITE_BIT | VK_ACCESS_DEPTH_STENCIL_ATTACHMENT_WRITE_BIT; + dependency.dstAccessMask = VK_ACCESS_COLOR_ATTACHMENT_READ_BIT | VK_ACCESS_COLOR_ATTACHMENT_WRITE_BIT | VK_ACCESS_DEPTH_STENCIL_ATTACHMENT_READ_BIT | VK_ACCESS_INPUT_ATTACHMENT_READ_BIT; + dependency.dependencyFlags = VK_DEPENDENCY_BY_REGION_BIT; + subpass_dependencies.push_back(dependency); + } + /* // NOTE: Big Mallet Approach -- any layout transition causes a full barrier if (reference.layout != description.initialLayout) { - // NOTE: this should be smarter based on the textures knowledge of it's previous role + // NOTE: this should be smarter based on the texture's knowledge of its previous role dependency_from_external.srcStageMask |= VK_PIPELINE_STAGE_ALL_COMMANDS_BIT; dependency_from_external.srcAccessMask |= VK_ACCESS_MEMORY_READ_BIT | VK_ACCESS_MEMORY_WRITE_BIT; } if (reference.layout != description.finalLayout) { - // NOTE: this should be smarter based on the textures knowledge of it's subsequent role + // NOTE: this should be smarter based on the texture's knowledge of its subsequent role dependency_to_external.dstStageMask |= VK_PIPELINE_STAGE_ALL_COMMANDS_BIT; dependency_to_external.dstAccessMask |= VK_ACCESS_MEMORY_READ_BIT | VK_ACCESS_MEMORY_WRITE_BIT; } + */ } - ERR_FAIL_COND_V_MSG(depth_stencil_references.size() > 1, VK_NULL_HANDLE, - "Formats can only have one depth/stencil attachment, supplied (" + itos(depth_stencil_references.size()) + ")."); - - ERR_FAIL_COND_V_MSG(resolve_references.size() > 1, VK_NULL_HANDLE, - "Formats can only have one resolve attachment, supplied (" + itos(resolve_references.size()) + ")."); - - VkSubpassDescription subpass; - subpass.flags = 0; - subpass.pipelineBindPoint = VK_PIPELINE_BIND_POINT_GRAPHICS; - subpass.inputAttachmentCount = 0; //unsupported for now - subpass.pInputAttachments = nullptr; - subpass.colorAttachmentCount = color_references.size(); - subpass.pColorAttachments = color_references.ptr(); - subpass.pDepthStencilAttachment = depth_stencil_references.ptr(); - subpass.pResolveAttachments = resolve_references.ptr(); - subpass.preserveAttachmentCount = 0; - subpass.pPreserveAttachments = nullptr; - VkRenderPassCreateInfo render_pass_create_info; render_pass_create_info.sType = VK_STRUCTURE_TYPE_RENDER_PASS_CREATE_INFO; render_pass_create_info.pNext = nullptr; render_pass_create_info.flags = 0; render_pass_create_info.attachmentCount = attachments.size(); render_pass_create_info.pAttachments = attachments.ptr(); - render_pass_create_info.subpassCount = 1; - render_pass_create_info.pSubpasses = &subpass; + render_pass_create_info.subpassCount = subpasses.size(); + render_pass_create_info.pSubpasses = subpasses.ptr(); // Commenting this because it seems it just avoids raster and compute to work at the same time. // Other barriers seem to be protecting the render pass fine. // render_pass_create_info.dependencyCount = 2; // render_pass_create_info.pDependencies = dependencies; - render_pass_create_info.dependencyCount = 0; - render_pass_create_info.pDependencies = nullptr; + render_pass_create_info.dependencyCount = subpass_dependencies.size(); + if (subpass_dependencies.size()) { + render_pass_create_info.pDependencies = subpass_dependencies.ptr(); + } else { + render_pass_create_info.pDependencies = nullptr; + } + + const uint32_t view_mask = (1 << p_view_count) - 1; + const uint32_t correlation_mask = (1 << p_view_count) - 1; + VkRenderPassMultiviewCreateInfo render_pass_multiview_create_info; + + if (p_view_count > 1) { + const VulkanContext::MultiviewCapabilities capabilities = context->get_multiview_capabilities(); + + // For now this only works with multiview! + ERR_FAIL_COND_V_MSG(!capabilities.is_supported, VK_NULL_HANDLE, "Multiview not supported"); + + // Make sure we limit this to the number of views we support. + ERR_FAIL_COND_V_MSG(p_view_count > capabilities.max_view_count, VK_NULL_HANDLE, "Hardware does not support requested number of views for Multiview render pass"); + + render_pass_multiview_create_info.sType = VK_STRUCTURE_TYPE_RENDER_PASS_MULTIVIEW_CREATE_INFO; + render_pass_multiview_create_info.pNext = nullptr; + render_pass_multiview_create_info.subpassCount = 1; + render_pass_multiview_create_info.pViewMasks = &view_mask; + render_pass_multiview_create_info.dependencyCount = 0; + render_pass_multiview_create_info.pViewOffsets = nullptr; + render_pass_multiview_create_info.correlationMaskCount = 1; + render_pass_multiview_create_info.pCorrelationMasks = &correlation_mask; + + render_pass_create_info.pNext = &render_pass_multiview_create_info; + } VkRenderPass render_pass; VkResult res = vkCreateRenderPass(device, &render_pass_create_info, nullptr, &render_pass); ERR_FAIL_COND_V_MSG(res, VK_NULL_HANDLE, "vkCreateRenderPass failed with error " + itos(res) + "."); - if (r_color_attachment_count) { - *r_color_attachment_count = color_references.size(); - } return render_pass; } -RenderingDevice::FramebufferFormatID RenderingDeviceVulkan::framebuffer_format_create(const Vector<AttachmentFormat> &p_format) { +RenderingDevice::FramebufferFormatID RenderingDeviceVulkan::framebuffer_format_create(const Vector<AttachmentFormat> &p_format, uint32_t p_view_count) { + FramebufferPass pass; + for (int i = 0; i < p_format.size(); i++) { + if (p_format[i].usage_flags & TEXTURE_USAGE_DEPTH_STENCIL_ATTACHMENT_BIT) { + pass.depth_attachment = i; + } else { + pass.color_attachments.push_back(i); + } + } + + Vector<FramebufferPass> passes; + passes.push_back(pass); + return framebuffer_format_create_multipass(p_format, passes, p_view_count); +} +RenderingDevice::FramebufferFormatID RenderingDeviceVulkan::framebuffer_format_create_multipass(const Vector<AttachmentFormat> &p_attachments, Vector<FramebufferPass> &p_passes, uint32_t p_view_count) { _THREAD_SAFE_METHOD_ FramebufferFormatKey key; - key.attachments = p_format; + key.attachments = p_attachments; + key.passes = p_passes; + key.view_count = p_view_count; const Map<FramebufferFormatKey, FramebufferFormatID>::Element *E = framebuffer_format_cache.find(key); if (E) { @@ -3491,8 +3689,8 @@ RenderingDevice::FramebufferFormatID RenderingDeviceVulkan::framebuffer_format_c return E->get(); } - int color_references; - VkRenderPass render_pass = _render_pass_create(p_format, INITIAL_ACTION_CLEAR, FINAL_ACTION_READ, INITIAL_ACTION_CLEAR, FINAL_ACTION_READ, &color_references); //actions don't matter for this use case + Vector<TextureSamples> samples; + VkRenderPass render_pass = _render_pass_create(p_attachments, p_passes, INITIAL_ACTION_CLEAR, FINAL_ACTION_READ, INITIAL_ACTION_CLEAR, FINAL_ACTION_READ, p_view_count, &samples); //actions don't matter for this use case if (render_pass == VK_NULL_HANDLE) { //was likely invalid return INVALID_ID; @@ -3502,15 +3700,16 @@ RenderingDevice::FramebufferFormatID RenderingDeviceVulkan::framebuffer_format_c E = framebuffer_format_cache.insert(key, id); FramebufferFormat fb_format; fb_format.E = E; - fb_format.color_attachments = color_references; fb_format.render_pass = render_pass; - fb_format.samples = p_format[0].samples; + fb_format.pass_samples = samples; + fb_format.view_count = p_view_count; framebuffer_formats[id] = fb_format; return id; } RenderingDevice::FramebufferFormatID RenderingDeviceVulkan::framebuffer_format_create_empty(TextureSamples p_samples) { FramebufferFormatKey key; + key.passes.push_back(FramebufferPass()); const Map<FramebufferFormatKey, FramebufferFormatID>::Element *E = framebuffer_format_cache.find(key); if (E) { @@ -3556,18 +3755,18 @@ RenderingDevice::FramebufferFormatID RenderingDeviceVulkan::framebuffer_format_c FramebufferFormat fb_format; fb_format.E = E; - fb_format.color_attachments = 0; fb_format.render_pass = render_pass; - fb_format.samples = p_samples; + fb_format.pass_samples.push_back(p_samples); framebuffer_formats[id] = fb_format; return id; } -RenderingDevice::TextureSamples RenderingDeviceVulkan::framebuffer_format_get_texture_samples(FramebufferFormatID p_format) { +RenderingDevice::TextureSamples RenderingDeviceVulkan::framebuffer_format_get_texture_samples(FramebufferFormatID p_format, uint32_t p_pass) { Map<FramebufferFormatID, FramebufferFormat>::Element *E = framebuffer_formats.find(p_format); ERR_FAIL_COND_V(!E, TEXTURE_SAMPLES_1); + ERR_FAIL_COND_V(p_pass >= uint32_t(E->get().pass_samples.size()), TEXTURE_SAMPLES_1); - return E->get().samples; + return E->get().pass_samples[p_pass]; } /***********************/ @@ -3580,11 +3779,36 @@ RID RenderingDeviceVulkan::framebuffer_create_empty(const Size2i &p_size, Textur framebuffer.format_id = framebuffer_format_create_empty(p_samples); ERR_FAIL_COND_V(p_format_check != INVALID_FORMAT_ID && framebuffer.format_id != p_format_check, RID()); framebuffer.size = p_size; + framebuffer.view_count = 1; return framebuffer_owner.make_rid(framebuffer); } -RID RenderingDeviceVulkan::framebuffer_create(const Vector<RID> &p_texture_attachments, FramebufferFormatID p_format_check) { +RID RenderingDeviceVulkan::framebuffer_create(const Vector<RID> &p_texture_attachments, FramebufferFormatID p_format_check, uint32_t p_view_count) { + _THREAD_SAFE_METHOD_ + + FramebufferPass pass; + + for (int i = 0; i < p_texture_attachments.size(); i++) { + Texture *texture = texture_owner.getornull(p_texture_attachments[i]); + ERR_FAIL_COND_V_MSG(!texture, RID(), "Texture index supplied for framebuffer (" + itos(i) + ") is not a valid texture."); + + ERR_FAIL_COND_V_MSG(texture->layers != p_view_count, RID(), "Layers of our texture doesn't match view count for this framebuffer"); + + if (texture->usage_flags & TEXTURE_USAGE_DEPTH_STENCIL_ATTACHMENT_BIT) { + pass.depth_attachment = i; + } else { + pass.color_attachments.push_back(i); + } + } + + Vector<FramebufferPass> passes; + passes.push_back(pass); + + return framebuffer_create_multipass(p_texture_attachments, passes, p_format_check, p_view_count); +} + +RID RenderingDeviceVulkan::framebuffer_create_multipass(const Vector<RID> &p_texture_attachments, Vector<FramebufferPass> &p_passes, FramebufferFormatID p_format_check, uint32_t p_view_count) { _THREAD_SAFE_METHOD_ Vector<AttachmentFormat> attachments; @@ -3594,6 +3818,8 @@ RID RenderingDeviceVulkan::framebuffer_create(const Vector<RID> &p_texture_attac Texture *texture = texture_owner.getornull(p_texture_attachments[i]); ERR_FAIL_COND_V_MSG(!texture, RID(), "Texture index supplied for framebuffer (" + itos(i) + ") is not a valid texture."); + ERR_FAIL_COND_V_MSG(texture->layers != p_view_count, RID(), "Layers of our texture doesn't match view count for this framebuffer"); + if (i == 0) { size.width = texture->width; size.height = texture->height; @@ -3609,7 +3835,7 @@ RID RenderingDeviceVulkan::framebuffer_create(const Vector<RID> &p_texture_attac attachments.push_back(af); } - FramebufferFormatID format_id = framebuffer_format_create(attachments); + FramebufferFormatID format_id = framebuffer_format_create_multipass(attachments, p_passes, p_view_count); if (format_id == INVALID_ID) { return RID(); } @@ -3621,6 +3847,7 @@ RID RenderingDeviceVulkan::framebuffer_create(const Vector<RID> &p_texture_attac framebuffer.format_id = format_id; framebuffer.texture_ids = p_texture_attachments; framebuffer.size = size; + framebuffer.view_count = p_view_count; RID id = framebuffer_owner.make_rid(framebuffer); @@ -4147,6 +4374,8 @@ RID RenderingDeviceVulkan::shader_create(const Vector<ShaderStageData> &p_stages uint32_t stages_processed = 0; + Vector<Shader::SpecializationConstant> specialization_constants; + bool is_compute = false; uint32_t compute_local_size[3] = { 0, 0, 0 }; @@ -4333,6 +4562,60 @@ RID RenderingDeviceVulkan::shader_create(const Vector<ShaderStageData> &p_stages } } + { + //specialization constants + + uint32_t sc_count = 0; + result = spvReflectEnumerateSpecializationConstants(&module, &sc_count, nullptr); + ERR_FAIL_COND_V_MSG(result != SPV_REFLECT_RESULT_SUCCESS, RID(), + "Reflection of SPIR-V shader stage '" + String(shader_stage_names[p_stages[i].shader_stage]) + "' failed enumerating specialization constants."); + + if (sc_count) { + Vector<SpvReflectSpecializationConstant *> spec_constants; + spec_constants.resize(sc_count); + + result = spvReflectEnumerateSpecializationConstants(&module, &sc_count, spec_constants.ptrw()); + ERR_FAIL_COND_V_MSG(result != SPV_REFLECT_RESULT_SUCCESS, RID(), + "Reflection of SPIR-V shader stage '" + String(shader_stage_names[p_stages[i].shader_stage]) + "' failed obtaining specialization constants."); + + for (uint32_t j = 0; j < sc_count; j++) { + int32_t existing = -1; + Shader::SpecializationConstant sconst; + sconst.constant.constant_id = spec_constants[j]->constant_id; + switch (spec_constants[j]->constant_type) { + case SPV_REFLECT_SPECIALIZATION_CONSTANT_BOOL: { + sconst.constant.type = PIPELINE_SPECIALIZATION_CONSTANT_TYPE_BOOL; + sconst.constant.bool_value = spec_constants[j]->default_value.int_bool_value != 0; + } break; + case SPV_REFLECT_SPECIALIZATION_CONSTANT_INT: { + sconst.constant.type = PIPELINE_SPECIALIZATION_CONSTANT_TYPE_INT; + sconst.constant.int_value = spec_constants[j]->default_value.int_bool_value; + } break; + case SPV_REFLECT_SPECIALIZATION_CONSTANT_FLOAT: { + sconst.constant.type = PIPELINE_SPECIALIZATION_CONSTANT_TYPE_FLOAT; + sconst.constant.float_value = spec_constants[j]->default_value.float_value; + } break; + } + sconst.stage_flags = 1 << p_stages[i].shader_stage; + + for (int k = 0; k < specialization_constants.size(); k++) { + if (specialization_constants[k].constant.constant_id == sconst.constant.constant_id) { + ERR_FAIL_COND_V_MSG(specialization_constants[k].constant.type != sconst.constant.type, RID(), "More than one specialization constant used for id (" + itos(sconst.constant.constant_id) + "), but their types differ."); + ERR_FAIL_COND_V_MSG(specialization_constants[k].constant.int_value != sconst.constant.int_value, RID(), "More than one specialization constant used for id (" + itos(sconst.constant.constant_id) + "), but their default values differ."); + existing = k; + break; + } + } + + if (existing > 0) { + specialization_constants.write[existing].stage_flags |= sconst.stage_flags; + } else { + specialization_constants.push_back(sconst); + } + } + } + } + if (stage == SHADER_STAGE_VERTEX) { uint32_t iv_count = 0; result = spvReflectEnumerateInputVariables(&module, &iv_count, nullptr); @@ -4370,8 +4653,9 @@ RID RenderingDeviceVulkan::shader_create(const Vector<ShaderStageData> &p_stages "Reflection of SPIR-V shader stage '" + String(shader_stage_names[p_stages[i].shader_stage]) + "' failed obtaining output variables."); for (uint32_t j = 0; j < ov_count; j++) { - if (output_vars[j]) { - fragment_outputs = MAX(fragment_outputs, output_vars[j]->location + 1); + const SpvReflectInterfaceVariable *refvar = output_vars[j]; + if (refvar != nullptr && refvar->built_in != SpvBuiltInFragDepth) { + fragment_outputs |= 1 << refvar->location; } } } @@ -4422,12 +4706,13 @@ RID RenderingDeviceVulkan::shader_create(const Vector<ShaderStageData> &p_stages Shader shader; shader.vertex_input_mask = vertex_input_mask; - shader.fragment_outputs = fragment_outputs; + shader.fragment_output_mask = fragment_outputs; shader.push_constant = push_constant; shader.is_compute = is_compute; shader.compute_local_size[0] = compute_local_size[0]; shader.compute_local_size[1] = compute_local_size[1]; shader.compute_local_size[2] = compute_local_size[2]; + shader.specialization_constants = specialization_constants; String error_text; @@ -5151,7 +5436,7 @@ RID RenderingDeviceVulkan::uniform_set_create(const Vector<Uniform> &p_uniforms, } ERR_FAIL_COND_V_MSG(!buffer, RID(), "Storage buffer supplied (binding: " + itos(uniform.binding) + ") is invalid."); - //if 0, then its sized on link time + //if 0, then it's sized on link time ERR_FAIL_COND_V_MSG(set_uniform.length > 0 && buffer->size != (uint32_t)set_uniform.length, RID(), "Storage buffer supplied (binding: " + itos(uniform.binding) + ") size (" + itos(buffer->size) + " does not match size of shader uniform: (" + itos(set_uniform.length) + ")."); @@ -5163,6 +5448,49 @@ RID RenderingDeviceVulkan::uniform_set_create(const Vector<Uniform> &p_uniforms, write.pTexelBufferView = nullptr; } break; case UNIFORM_TYPE_INPUT_ATTACHMENT: { + ERR_FAIL_COND_V_MSG(shader->is_compute, RID(), "InputAttachment (binding: " + itos(uniform.binding) + ") supplied for compute shader (this is not allowed)."); + + if (uniform.ids.size() != set_uniform.length) { + if (set_uniform.length > 1) { + ERR_FAIL_V_MSG(RID(), "InputAttachment (binding: " + itos(uniform.binding) + ") is an array of (" + itos(set_uniform.length) + ") textures, so it should be provided equal number of texture IDs to satisfy it (IDs provided: " + itos(uniform.ids.size()) + ")."); + } else { + ERR_FAIL_V_MSG(RID(), "InputAttachment (binding: " + itos(uniform.binding) + ") should provide one ID referencing a texture (IDs provided: " + itos(uniform.ids.size()) + ")."); + } + } + + Vector<VkDescriptorImageInfo> image_info; + + for (int j = 0; j < uniform.ids.size(); j++) { + Texture *texture = texture_owner.getornull(uniform.ids[j]); + + ERR_FAIL_COND_V_MSG(!texture, RID(), + "InputAttachment (binding: " + itos(uniform.binding) + ", index " + itos(j) + ") is not a valid texture."); + + ERR_FAIL_COND_V_MSG(!(texture->usage_flags & TEXTURE_USAGE_SAMPLING_BIT), RID(), + "InputAttachment (binding: " + itos(uniform.binding) + ", index " + itos(j) + ") needs the TEXTURE_USAGE_SAMPLING_BIT usage flag set in order to be used as uniform."); + + VkDescriptorImageInfo img_info; + img_info.sampler = VK_NULL_HANDLE; + img_info.imageView = texture->view; + + if (texture->owner.is_valid()) { + texture = texture_owner.getornull(texture->owner); + ERR_FAIL_COND_V(!texture, RID()); //bug, should never happen + } + + img_info.imageLayout = VK_IMAGE_LAYOUT_SHADER_READ_ONLY_OPTIMAL; + + image_info.push_back(img_info); + } + + write.dstArrayElement = 0; + write.descriptorCount = uniform.ids.size(); + write.descriptorType = VK_DESCRIPTOR_TYPE_INPUT_ATTACHMENT; + write.pImageInfo = image_infos.push_back(image_info)->get().ptr(); + write.pBufferInfo = nullptr; + write.pTexelBufferView = nullptr; + + type_size = uniform.ids.size(); } break; default: { } @@ -5234,6 +5562,13 @@ bool RenderingDeviceVulkan::uniform_set_is_valid(RID p_uniform_set) { return uniform_set_owner.owns(p_uniform_set); } +void RenderingDeviceVulkan::uniform_set_set_invalidation_callback(RID p_uniform_set, UniformSetInvalidatedCallback p_callback, void *p_userdata) { + UniformSet *us = uniform_set_owner.getornull(p_uniform_set); + ERR_FAIL_COND(!us); + us->invalidated_callback = p_callback; + us->invalidated_callback_userdata = p_userdata; +} + Error RenderingDeviceVulkan::buffer_update(RID p_buffer, uint32_t p_offset, uint32_t p_size, const void *p_data, uint32_t p_post_barrier) { _THREAD_SAFE_METHOD_ @@ -5336,7 +5671,7 @@ Vector<uint8_t> RenderingDeviceVulkan::buffer_get_data(RID p_buffer) { ERR_FAIL_V_MSG(Vector<uint8_t>(), "Buffer is either invalid or this type of buffer can't be retrieved. Only Index and Vertex buffers allow retrieving."); } - // Make sure no one is using the buffer -- the "false" gets us to the same command buffer as below. + // Make sure no one is using the buffer -- the "false" gets us to the same command buffer as below. _buffer_memory_barrier(buffer->buffer, 0, buffer->size, src_stage_mask, src_access_mask, VK_PIPELINE_STAGE_TRANSFER_BIT, VK_ACCESS_TRANSFER_READ_BIT, false); VkCommandBuffer command_buffer = frames[frame].setup_command_buffer; @@ -5359,7 +5694,7 @@ Vector<uint8_t> RenderingDeviceVulkan::buffer_get_data(RID p_buffer) { { buffer_data.resize(buffer->size); uint8_t *w = buffer_data.ptrw(); - copymem(w, buffer_mem, buffer->size); + memcpy(w, buffer_mem, buffer->size); } vmaUnmapMemory(allocator, tmp_buffer.allocation); @@ -5373,7 +5708,7 @@ Vector<uint8_t> RenderingDeviceVulkan::buffer_get_data(RID p_buffer) { /**** RENDER PIPELINE ****/ /*************************/ -RID RenderingDeviceVulkan::render_pipeline_create(RID p_shader, FramebufferFormatID p_framebuffer_format, VertexFormatID p_vertex_format, RenderPrimitive p_render_primitive, const PipelineRasterizationState &p_rasterization_state, const PipelineMultisampleState &p_multisample_state, const PipelineDepthStencilState &p_depth_stencil_state, const PipelineColorBlendState &p_blend_state, int p_dynamic_state_flags) { +RID RenderingDeviceVulkan::render_pipeline_create(RID p_shader, FramebufferFormatID p_framebuffer_format, VertexFormatID p_vertex_format, RenderPrimitive p_render_primitive, const PipelineRasterizationState &p_rasterization_state, const PipelineMultisampleState &p_multisample_state, const PipelineDepthStencilState &p_depth_stencil_state, const PipelineColorBlendState &p_blend_state, int p_dynamic_state_flags, uint32_t p_for_render_pass, const Vector<PipelineSpecializationConstant> &p_specialization_constants) { _THREAD_SAFE_METHOD_ //needs a shader @@ -5392,8 +5727,16 @@ RID RenderingDeviceVulkan::render_pipeline_create(RID p_shader, FramebufferForma { //validate shader vs framebuffer - ERR_FAIL_COND_V_MSG(shader->fragment_outputs != fb_format.color_attachments, RID(), - "Mismatch fragment output bindings (" + itos(shader->fragment_outputs) + ") and framebuffer color buffers (" + itos(fb_format.color_attachments) + ") when binding both in render pipeline."); + ERR_FAIL_COND_V_MSG(p_for_render_pass >= uint32_t(fb_format.E->key().passes.size()), RID(), "Render pass requested for pipeline creation (" + itos(p_for_render_pass) + ") is out of bounds"); + const FramebufferPass &pass = fb_format.E->key().passes[p_for_render_pass]; + uint32_t output_mask = 0; + for (int i = 0; i < pass.color_attachments.size(); i++) { + if (pass.color_attachments[i] != FramebufferPass::ATTACHMENT_UNUSED) { + output_mask |= 1 << i; + } + } + ERR_FAIL_COND_V_MSG(shader->fragment_output_mask != output_mask, RID(), + "Mismatch fragment shader output mask (" + itos(shader->fragment_output_mask) + ") and framebuffer color output mask (" + itos(output_mask) + ") when binding both in render pipeline."); } //vertex VkPipelineVertexInputStateCreateInfo pipeline_vertex_input_state_create_info; @@ -5465,7 +5808,7 @@ RID RenderingDeviceVulkan::render_pipeline_create(RID p_shader, FramebufferForma tessellation_create_info.sType = VK_STRUCTURE_TYPE_PIPELINE_TESSELLATION_STATE_CREATE_INFO; tessellation_create_info.pNext = nullptr; tessellation_create_info.flags = 0; - ERR_FAIL_COND_V(p_rasterization_state.patch_control_points < 1 || p_rasterization_state.patch_control_points > limits.maxTessellationPatchSize, RID()); + ERR_FAIL_COND_V(limits.maxTessellationPatchSize > 0 && (p_rasterization_state.patch_control_points < 1 || p_rasterization_state.patch_control_points > limits.maxTessellationPatchSize), RID()); tessellation_create_info.patchControlPoints = p_rasterization_state.patch_control_points; VkPipelineViewportStateCreateInfo viewport_state_create_info; @@ -5578,44 +5921,53 @@ RID RenderingDeviceVulkan::render_pipeline_create(RID p_shader, FramebufferForma ERR_FAIL_INDEX_V(p_blend_state.logic_op, LOGIC_OP_MAX, RID()); color_blend_state_create_info.logicOp = logic_operations[p_blend_state.logic_op]; - ERR_FAIL_COND_V(fb_format.color_attachments != p_blend_state.attachments.size(), RID()); - Vector<VkPipelineColorBlendAttachmentState> attachment_states; + { + const FramebufferPass &pass = fb_format.E->key().passes[p_for_render_pass]; + + for (int i = 0; i < pass.color_attachments.size(); i++) { + if (pass.color_attachments[i] != FramebufferPass::ATTACHMENT_UNUSED) { + int idx = attachment_states.size(); + + ERR_FAIL_INDEX_V(idx, p_blend_state.attachments.size(), RID()); + VkPipelineColorBlendAttachmentState state; + state.blendEnable = p_blend_state.attachments[idx].enable_blend; + + ERR_FAIL_INDEX_V(p_blend_state.attachments[idx].src_color_blend_factor, BLEND_FACTOR_MAX, RID()); + state.srcColorBlendFactor = blend_factors[p_blend_state.attachments[idx].src_color_blend_factor]; + ERR_FAIL_INDEX_V(p_blend_state.attachments[idx].dst_color_blend_factor, BLEND_FACTOR_MAX, RID()); + state.dstColorBlendFactor = blend_factors[p_blend_state.attachments[idx].dst_color_blend_factor]; + ERR_FAIL_INDEX_V(p_blend_state.attachments[idx].color_blend_op, BLEND_OP_MAX, RID()); + state.colorBlendOp = blend_operations[p_blend_state.attachments[idx].color_blend_op]; + + ERR_FAIL_INDEX_V(p_blend_state.attachments[idx].src_alpha_blend_factor, BLEND_FACTOR_MAX, RID()); + state.srcAlphaBlendFactor = blend_factors[p_blend_state.attachments[idx].src_alpha_blend_factor]; + ERR_FAIL_INDEX_V(p_blend_state.attachments[idx].dst_alpha_blend_factor, BLEND_FACTOR_MAX, RID()); + state.dstAlphaBlendFactor = blend_factors[p_blend_state.attachments[idx].dst_alpha_blend_factor]; + ERR_FAIL_INDEX_V(p_blend_state.attachments[idx].alpha_blend_op, BLEND_OP_MAX, RID()); + state.alphaBlendOp = blend_operations[p_blend_state.attachments[idx].alpha_blend_op]; + + state.colorWriteMask = 0; + if (p_blend_state.attachments[idx].write_r) { + state.colorWriteMask |= VK_COLOR_COMPONENT_R_BIT; + } + if (p_blend_state.attachments[idx].write_g) { + state.colorWriteMask |= VK_COLOR_COMPONENT_G_BIT; + } + if (p_blend_state.attachments[idx].write_b) { + state.colorWriteMask |= VK_COLOR_COMPONENT_B_BIT; + } + if (p_blend_state.attachments[idx].write_a) { + state.colorWriteMask |= VK_COLOR_COMPONENT_A_BIT; + } - for (int i = 0; i < p_blend_state.attachments.size(); i++) { - VkPipelineColorBlendAttachmentState state; - state.blendEnable = p_blend_state.attachments[i].enable_blend; - - ERR_FAIL_INDEX_V(p_blend_state.attachments[i].src_color_blend_factor, BLEND_FACTOR_MAX, RID()); - state.srcColorBlendFactor = blend_factors[p_blend_state.attachments[i].src_color_blend_factor]; - ERR_FAIL_INDEX_V(p_blend_state.attachments[i].dst_color_blend_factor, BLEND_FACTOR_MAX, RID()); - state.dstColorBlendFactor = blend_factors[p_blend_state.attachments[i].dst_color_blend_factor]; - ERR_FAIL_INDEX_V(p_blend_state.attachments[i].color_blend_op, BLEND_OP_MAX, RID()); - state.colorBlendOp = blend_operations[p_blend_state.attachments[i].color_blend_op]; - - ERR_FAIL_INDEX_V(p_blend_state.attachments[i].src_alpha_blend_factor, BLEND_FACTOR_MAX, RID()); - state.srcAlphaBlendFactor = blend_factors[p_blend_state.attachments[i].src_alpha_blend_factor]; - ERR_FAIL_INDEX_V(p_blend_state.attachments[i].dst_alpha_blend_factor, BLEND_FACTOR_MAX, RID()); - state.dstAlphaBlendFactor = blend_factors[p_blend_state.attachments[i].dst_alpha_blend_factor]; - ERR_FAIL_INDEX_V(p_blend_state.attachments[i].alpha_blend_op, BLEND_OP_MAX, RID()); - state.alphaBlendOp = blend_operations[p_blend_state.attachments[i].alpha_blend_op]; - - state.colorWriteMask = 0; - if (p_blend_state.attachments[i].write_r) { - state.colorWriteMask |= VK_COLOR_COMPONENT_R_BIT; - } - if (p_blend_state.attachments[i].write_g) { - state.colorWriteMask |= VK_COLOR_COMPONENT_G_BIT; - } - if (p_blend_state.attachments[i].write_b) { - state.colorWriteMask |= VK_COLOR_COMPONENT_B_BIT; - } - if (p_blend_state.attachments[i].write_a) { - state.colorWriteMask |= VK_COLOR_COMPONENT_A_BIT; + attachment_states.push_back(state); + idx++; + }; } - attachment_states.push_back(state); - }; + ERR_FAIL_COND_V(attachment_states.size() != p_blend_state.attachments.size(), RID()); + } color_blend_state_create_info.attachmentCount = attachment_states.size(); color_blend_state_create_info.pAttachments = attachment_states.ptr(); @@ -5674,8 +6026,63 @@ RID RenderingDeviceVulkan::render_pipeline_create(RID p_shader, FramebufferForma graphics_pipeline_create_info.pNext = nullptr; graphics_pipeline_create_info.flags = 0; - graphics_pipeline_create_info.stageCount = shader->pipeline_stages.size(); - graphics_pipeline_create_info.pStages = shader->pipeline_stages.ptr(); + Vector<VkPipelineShaderStageCreateInfo> pipeline_stages = shader->pipeline_stages; + Vector<VkSpecializationInfo> specialization_info; + Vector<Vector<VkSpecializationMapEntry>> specialization_map_entries; + Vector<uint32_t> specialization_constant_data; + + if (shader->specialization_constants.size()) { + specialization_constant_data.resize(shader->specialization_constants.size()); + uint32_t *data_ptr = specialization_constant_data.ptrw(); + specialization_info.resize(pipeline_stages.size()); + specialization_map_entries.resize(pipeline_stages.size()); + for (int i = 0; i < shader->specialization_constants.size(); i++) { + //see if overriden + const Shader::SpecializationConstant &sc = shader->specialization_constants[i]; + data_ptr[i] = sc.constant.int_value; //just copy the 32 bits + + for (int j = 0; j < p_specialization_constants.size(); j++) { + const PipelineSpecializationConstant &psc = p_specialization_constants[j]; + if (psc.constant_id == sc.constant.constant_id) { + ERR_FAIL_COND_V_MSG(psc.type != sc.constant.type, RID(), "Specialization constant provided for id (" + itos(sc.constant.constant_id) + ") is of the wrong type."); + data_ptr[i] = psc.int_value; + break; + } + } + + VkSpecializationMapEntry entry; + + entry.constantID = sc.constant.constant_id; + entry.offset = i * sizeof(uint32_t); + entry.size = sizeof(uint32_t); + + for (int j = 0; j < SHADER_STAGE_MAX; j++) { + if (sc.stage_flags & (1 << j)) { + VkShaderStageFlagBits stage = shader_stage_masks[j]; + for (int k = 0; k < pipeline_stages.size(); k++) { + if (pipeline_stages[k].stage == stage) { + specialization_map_entries.write[k].push_back(entry); + } + } + } + } + } + + for (int i = 0; i < pipeline_stages.size(); i++) { + if (specialization_map_entries[i].size()) { + specialization_info.write[i].dataSize = specialization_constant_data.size() * sizeof(uint32_t); + specialization_info.write[i].pData = data_ptr; + specialization_info.write[i].mapEntryCount = specialization_map_entries[i].size(); + specialization_info.write[i].pMapEntries = specialization_map_entries[i].ptr(); + + pipeline_stages.write[i].pSpecializationInfo = specialization_info.ptr() + i; + } + } + } + + graphics_pipeline_create_info.stageCount = pipeline_stages.size(); + graphics_pipeline_create_info.pStages = pipeline_stages.ptr(); + graphics_pipeline_create_info.pVertexInputState = &pipeline_vertex_input_state_create_info; graphics_pipeline_create_info.pInputAssemblyState = &input_assembly_create_info; graphics_pipeline_create_info.pTessellationState = &tessellation_create_info; @@ -5688,7 +6095,7 @@ RID RenderingDeviceVulkan::render_pipeline_create(RID p_shader, FramebufferForma graphics_pipeline_create_info.layout = shader->pipeline_layout; graphics_pipeline_create_info.renderPass = fb_format.render_pass; - graphics_pipeline_create_info.subpass = 0; + graphics_pipeline_create_info.subpass = p_for_render_pass; graphics_pipeline_create_info.basePipelineHandle = VK_NULL_HANDLE; graphics_pipeline_create_info.basePipelineIndex = 0; @@ -5705,6 +6112,7 @@ RID RenderingDeviceVulkan::render_pipeline_create(RID p_shader, FramebufferForma #ifdef DEBUG_ENABLED pipeline.validation.dynamic_state = p_dynamic_state_flags; pipeline.validation.framebuffer_format = p_framebuffer_format; + pipeline.validation.render_pass = p_for_render_pass; pipeline.validation.vertex_format = p_vertex_format; pipeline.validation.uses_restart_indices = input_assembly_create_info.primitiveRestartEnable; @@ -5729,7 +6137,7 @@ RID RenderingDeviceVulkan::render_pipeline_create(RID p_shader, FramebufferForma #endif //create ID to associate with this pipeline RID id = render_pipeline_owner.make_rid(pipeline); - //now add aall the dependencies + //now add all the dependencies _add_dependency(id, p_shader); return id; } @@ -5743,7 +6151,7 @@ bool RenderingDeviceVulkan::render_pipeline_is_valid(RID p_pipeline) { /**** COMPUTE PIPELINE ****/ /**************************/ -RID RenderingDeviceVulkan::compute_pipeline_create(RID p_shader) { +RID RenderingDeviceVulkan::compute_pipeline_create(RID p_shader, const Vector<PipelineSpecializationConstant> &p_specialization_constants) { _THREAD_SAFE_METHOD_ //needs a shader @@ -5765,6 +6173,44 @@ RID RenderingDeviceVulkan::compute_pipeline_create(RID p_shader) { compute_pipeline_create_info.basePipelineHandle = VK_NULL_HANDLE; compute_pipeline_create_info.basePipelineIndex = 0; + VkSpecializationInfo specialization_info; + Vector<VkSpecializationMapEntry> specialization_map_entries; + Vector<uint32_t> specialization_constant_data; + + if (shader->specialization_constants.size()) { + specialization_constant_data.resize(shader->specialization_constants.size()); + uint32_t *data_ptr = specialization_constant_data.ptrw(); + for (int i = 0; i < shader->specialization_constants.size(); i++) { + //see if overriden + const Shader::SpecializationConstant &sc = shader->specialization_constants[i]; + data_ptr[i] = sc.constant.int_value; //just copy the 32 bits + + for (int j = 0; j < p_specialization_constants.size(); j++) { + const PipelineSpecializationConstant &psc = p_specialization_constants[j]; + if (psc.constant_id == sc.constant.constant_id) { + ERR_FAIL_COND_V_MSG(psc.type != sc.constant.type, RID(), "Specialization constant provided for id (" + itos(sc.constant.constant_id) + ") is of the wrong type."); + data_ptr[i] = sc.constant.int_value; + break; + } + } + + VkSpecializationMapEntry entry; + + entry.constantID = sc.constant.constant_id; + entry.offset = i * sizeof(uint32_t); + entry.size = sizeof(uint32_t); + + specialization_map_entries.push_back(entry); + } + + specialization_info.dataSize = specialization_constant_data.size() * sizeof(uint32_t); + specialization_info.pData = data_ptr; + specialization_info.mapEntryCount = specialization_map_entries.size(); + specialization_info.pMapEntries = specialization_map_entries.ptr(); + + compute_pipeline_create_info.stage.pSpecializationInfo = &specialization_info; + } + ComputePipeline pipeline; VkResult err = vkCreateComputePipelines(device, VK_NULL_HANDLE, 1, &compute_pipeline_create_info, nullptr, &pipeline.pipeline); ERR_FAIL_COND_V_MSG(err, RID(), "vkCreateComputePipelines failed with error " + itos(err) + "."); @@ -5780,7 +6226,7 @@ RID RenderingDeviceVulkan::compute_pipeline_create(RID p_shader) { //create ID to associate with this pipeline RID id = compute_pipeline_owner.make_rid(pipeline); - //now add aall the dependencies + //now add all the dependencies _add_dependency(id, p_shader); return id; } @@ -5843,13 +6289,14 @@ RenderingDevice::DrawListID RenderingDeviceVulkan::draw_list_begin_for_screen(Di ERR_FAIL_COND_V_MSG(compute_list != nullptr, INVALID_ID, "Only one draw/compute list can be active at the same time."); VkCommandBuffer command_buffer = frames[frame].draw_command_buffer; - draw_list = memnew(DrawList); - draw_list->command_buffer = command_buffer; + + Size2i size = Size2i(context->window_get_width(p_screen), context->window_get_height(p_screen)); + + _draw_list_allocate(Rect2i(Vector2i(), size), 0, 0); #ifdef DEBUG_ENABLED - draw_list->validation.framebuffer_format = screen_get_framebuffer_format(); + draw_list_framebuffer_format = screen_get_framebuffer_format(); #endif - draw_list_count = 0; - draw_list_split = false; + draw_list_subpass_count = 1; VkRenderPassBeginInfo render_pass_begin; render_pass_begin.sType = VK_STRUCTURE_TYPE_RENDER_PASS_BEGIN_INFO; @@ -5857,8 +6304,8 @@ RenderingDevice::DrawListID RenderingDeviceVulkan::draw_list_begin_for_screen(Di render_pass_begin.renderPass = context->window_get_render_pass(p_screen); render_pass_begin.framebuffer = context->window_get_framebuffer(p_screen); - render_pass_begin.renderArea.extent.width = context->window_get_width(p_screen); - render_pass_begin.renderArea.extent.height = context->window_get_height(p_screen); + render_pass_begin.renderArea.extent.width = size.width; + render_pass_begin.renderArea.extent.height = size.height; render_pass_begin.renderArea.offset.x = 0; render_pass_begin.renderArea.offset.y = 0; @@ -5898,18 +6345,19 @@ RenderingDevice::DrawListID RenderingDeviceVulkan::draw_list_begin_for_screen(Di return int64_t(ID_TYPE_DRAW_LIST) << ID_BASE_SHIFT; } -Error RenderingDeviceVulkan::_draw_list_setup_framebuffer(Framebuffer *p_framebuffer, InitialAction p_initial_color_action, FinalAction p_final_color_action, InitialAction p_initial_depth_action, FinalAction p_final_depth_action, VkFramebuffer *r_framebuffer, VkRenderPass *r_render_pass) { +Error RenderingDeviceVulkan::_draw_list_setup_framebuffer(Framebuffer *p_framebuffer, InitialAction p_initial_color_action, FinalAction p_final_color_action, InitialAction p_initial_depth_action, FinalAction p_final_depth_action, VkFramebuffer *r_framebuffer, VkRenderPass *r_render_pass, uint32_t *r_subpass_count) { Framebuffer::VersionKey vk; vk.initial_color_action = p_initial_color_action; vk.final_color_action = p_final_color_action; vk.initial_depth_action = p_initial_depth_action; vk.final_depth_action = p_final_depth_action; + vk.view_count = p_framebuffer->view_count; if (!p_framebuffer->framebuffers.has(vk)) { //need to create this version Framebuffer::Version version; - version.render_pass = _render_pass_create(framebuffer_formats[p_framebuffer->format_id].E->key().attachments, p_initial_color_action, p_final_color_action, p_initial_depth_action, p_final_depth_action); + version.render_pass = _render_pass_create(framebuffer_formats[p_framebuffer->format_id].E->key().attachments, framebuffer_formats[p_framebuffer->format_id].E->key().passes, p_initial_color_action, p_final_color_action, p_initial_depth_action, p_final_depth_action, p_framebuffer->view_count); VkFramebufferCreateInfo framebuffer_create_info; framebuffer_create_info.sType = VK_STRUCTURE_TYPE_FRAMEBUFFER_CREATE_INFO; @@ -5933,11 +6381,14 @@ Error RenderingDeviceVulkan::_draw_list_setup_framebuffer(Framebuffer *p_framebu VkResult err = vkCreateFramebuffer(device, &framebuffer_create_info, nullptr, &version.framebuffer); ERR_FAIL_COND_V_MSG(err, ERR_CANT_CREATE, "vkCreateFramebuffer failed with error " + itos(err) + "."); + version.subpass_count = framebuffer_formats[p_framebuffer->format_id].E->key().passes.size(); + p_framebuffer->framebuffers.insert(vk, version); } const Framebuffer::Version &version = p_framebuffer->framebuffers[vk]; *r_framebuffer = version.framebuffer; *r_render_pass = version.render_pass; + *r_subpass_count = version.subpass_count; return OK; } @@ -6127,15 +6578,23 @@ RenderingDevice::DrawListID RenderingDeviceVulkan::draw_list_begin(RID p_framebu if (p_initial_color_action == INITIAL_ACTION_CLEAR) { //check clear values - int color_attachments = framebuffer_formats[framebuffer->format_id].color_attachments; - ERR_FAIL_COND_V_MSG(p_clear_color_values.size() != color_attachments, INVALID_ID, - "Clear color values supplied (" + itos(p_clear_color_values.size()) + ") differ from the amount required for framebuffer (" + itos(color_attachments) + ")."); + int color_count = 0; + for (int i = 0; i < framebuffer->texture_ids.size(); i++) { + Texture *texture = texture_owner.getornull(framebuffer->texture_ids[i]); + + if (texture->usage_flags & TEXTURE_USAGE_COLOR_ATTACHMENT_BIT) { + color_count++; + } + } + + ERR_FAIL_COND_V_MSG(p_clear_color_values.size() != color_count, INVALID_ID, + "Clear color values supplied (" + itos(p_clear_color_values.size()) + ") differ from the amount required for framebuffer color attachments (" + itos(color_count) + ")."); } VkFramebuffer vkframebuffer; VkRenderPass render_pass; - Error err = _draw_list_setup_framebuffer(framebuffer, p_initial_color_action, p_final_color_action, p_initial_depth_action, p_final_depth_action, &vkframebuffer, &render_pass); + Error err = _draw_list_setup_framebuffer(framebuffer, p_initial_color_action, p_final_color_action, p_initial_depth_action, p_final_depth_action, &vkframebuffer, &render_pass, &draw_list_subpass_count); ERR_FAIL_COND_V(err != OK, INVALID_ID); VkCommandBuffer command_buffer = frames[frame].draw_command_buffer; @@ -6145,13 +6604,14 @@ RenderingDevice::DrawListID RenderingDeviceVulkan::draw_list_begin(RID p_framebu return INVALID_ID; } - draw_list = memnew(DrawList); - draw_list->command_buffer = command_buffer; + draw_list_render_pass = render_pass; + draw_list_vkframebuffer = vkframebuffer; + + _draw_list_allocate(Rect2i(viewport_offset, viewport_size), 0, 0); #ifdef DEBUG_ENABLED - draw_list->validation.framebuffer_format = framebuffer->format_id; + draw_list_framebuffer_format = framebuffer->format_id; #endif - draw_list_count = 0; - draw_list_split = false; + draw_list_current_subpass = 0; if (needs_clear_color || needs_clear_depth) { _draw_list_insert_clear_region(draw_list, framebuffer, viewport_offset, viewport_size, needs_clear_color, p_clear_color_values, needs_clear_depth, p_clear_depth, p_clear_stencil); @@ -6175,7 +6635,6 @@ RenderingDevice::DrawListID RenderingDeviceVulkan::draw_list_begin(RID p_framebu vkCmdSetScissor(command_buffer, 0, 1, &scissor); - draw_list->viewport = Rect2i(viewport_offset, viewport_size); return int64_t(ID_TYPE_DRAW_LIST) << ID_BASE_SHIFT; } @@ -6217,47 +6676,23 @@ Error RenderingDeviceVulkan::draw_list_begin_split(RID p_framebuffer, uint32_t p if (p_initial_color_action == INITIAL_ACTION_CLEAR) { //check clear values - int color_attachments = framebuffer_formats[framebuffer->format_id].color_attachments; - ERR_FAIL_COND_V_MSG(p_clear_color_values.size() != color_attachments, ERR_INVALID_PARAMETER, - "Clear color values supplied (" + itos(p_clear_color_values.size()) + ") differ from the amount required for framebuffer (" + itos(color_attachments) + ")."); - } - - if (p_splits > (uint32_t)split_draw_list_allocators.size()) { - uint32_t from = split_draw_list_allocators.size(); - split_draw_list_allocators.resize(p_splits); - for (uint32_t i = from; i < p_splits; i++) { - VkCommandPoolCreateInfo cmd_pool_info; - cmd_pool_info.sType = VK_STRUCTURE_TYPE_COMMAND_POOL_CREATE_INFO; - cmd_pool_info.pNext = nullptr; - cmd_pool_info.queueFamilyIndex = context->get_graphics_queue(); - cmd_pool_info.flags = VK_COMMAND_POOL_CREATE_RESET_COMMAND_BUFFER_BIT; - - VkResult res = vkCreateCommandPool(device, &cmd_pool_info, nullptr, &split_draw_list_allocators.write[i].command_pool); - ERR_FAIL_COND_V_MSG(res, ERR_CANT_CREATE, "vkCreateCommandPool failed with error " + itos(res) + "."); - - for (int j = 0; j < frame_count; j++) { - VkCommandBuffer command_buffer; - - VkCommandBufferAllocateInfo cmdbuf; - //no command buffer exists, create it. - cmdbuf.sType = VK_STRUCTURE_TYPE_COMMAND_BUFFER_ALLOCATE_INFO; - cmdbuf.pNext = nullptr; - cmdbuf.commandPool = split_draw_list_allocators[i].command_pool; - cmdbuf.level = VK_COMMAND_BUFFER_LEVEL_SECONDARY; - cmdbuf.commandBufferCount = 1; - - VkResult err = vkAllocateCommandBuffers(device, &cmdbuf, &command_buffer); - ERR_FAIL_COND_V_MSG(err, ERR_CANT_CREATE, "vkAllocateCommandBuffers failed with error " + itos(err) + "."); + int color_count = 0; + for (int i = 0; i < framebuffer->texture_ids.size(); i++) { + Texture *texture = texture_owner.getornull(framebuffer->texture_ids[i]); - split_draw_list_allocators.write[i].command_buffers.push_back(command_buffer); + if (texture->usage_flags & TEXTURE_USAGE_COLOR_ATTACHMENT_BIT) { + color_count++; } } + + ERR_FAIL_COND_V_MSG(p_clear_color_values.size() != color_count, ERR_INVALID_PARAMETER, + "Clear color values supplied (" + itos(p_clear_color_values.size()) + ") differ from the amount required for framebuffer (" + itos(color_count) + ")."); } VkFramebuffer vkframebuffer; VkRenderPass render_pass; - Error err = _draw_list_setup_framebuffer(framebuffer, p_initial_color_action, p_final_color_action, p_initial_depth_action, p_final_depth_action, &vkframebuffer, &render_pass); + Error err = _draw_list_setup_framebuffer(framebuffer, p_initial_color_action, p_final_color_action, p_initial_depth_action, p_final_depth_action, &vkframebuffer, &render_pass, &draw_list_subpass_count); ERR_FAIL_COND_V(err != OK, ERR_CANT_CREATE); VkCommandBuffer frame_command_buffer = frames[frame].draw_command_buffer; @@ -6267,53 +6702,24 @@ Error RenderingDeviceVulkan::draw_list_begin_split(RID p_framebuffer, uint32_t p return ERR_CANT_CREATE; } - draw_list = memnew_arr(DrawList, p_splits); - draw_list_count = p_splits; - draw_list_split = true; - - for (uint32_t i = 0; i < p_splits; i++) { - //take a command buffer and initialize it - VkCommandBuffer command_buffer = split_draw_list_allocators[i].command_buffers[frame]; - - VkCommandBufferInheritanceInfo inheritance_info; - inheritance_info.sType = VK_STRUCTURE_TYPE_COMMAND_BUFFER_INHERITANCE_INFO; - inheritance_info.pNext = nullptr; - inheritance_info.renderPass = render_pass; - inheritance_info.subpass = 0; - inheritance_info.framebuffer = vkframebuffer; - inheritance_info.occlusionQueryEnable = false; - inheritance_info.queryFlags = 0; //? - inheritance_info.pipelineStatistics = 0; - - VkCommandBufferBeginInfo cmdbuf_begin; - cmdbuf_begin.sType = VK_STRUCTURE_TYPE_COMMAND_BUFFER_BEGIN_INFO; - cmdbuf_begin.pNext = nullptr; - cmdbuf_begin.flags = VK_COMMAND_BUFFER_USAGE_SIMULTANEOUS_USE_BIT | VK_COMMAND_BUFFER_USAGE_RENDER_PASS_CONTINUE_BIT; - cmdbuf_begin.pInheritanceInfo = &inheritance_info; + draw_list_current_subpass = 0; - VkResult res = vkResetCommandBuffer(command_buffer, 0); - if (res) { - memdelete_arr(draw_list); - draw_list = nullptr; - ERR_FAIL_V_MSG(ERR_CANT_CREATE, "vkResetCommandBuffer failed with error " + itos(res) + "."); - } - - res = vkBeginCommandBuffer(command_buffer, &cmdbuf_begin); - if (res) { - memdelete_arr(draw_list); - draw_list = nullptr; - ERR_FAIL_V_MSG(ERR_CANT_CREATE, "vkBeginCommandBuffer failed with error " + itos(res) + "."); - } - - draw_list[i].command_buffer = command_buffer; #ifdef DEBUG_ENABLED - draw_list[i].validation.framebuffer_format = framebuffer->format_id; + draw_list_framebuffer_format = framebuffer->format_id; #endif + draw_list_render_pass = render_pass; + draw_list_vkframebuffer = vkframebuffer; - if (i == 0 && (needs_clear_color || needs_clear_depth)) { - _draw_list_insert_clear_region(draw_list, framebuffer, viewport_offset, viewport_size, needs_clear_color, p_clear_color_values, needs_clear_depth, p_clear_depth, p_clear_stencil); - } + err = _draw_list_allocate(Rect2i(viewport_offset, viewport_size), p_splits, 0); + if (err != OK) { + return err; + } + + if (needs_clear_color || needs_clear_depth) { + _draw_list_insert_clear_region(&draw_list[0], framebuffer, viewport_offset, viewport_size, needs_clear_color, p_clear_color_values, needs_clear_depth, p_clear_depth, p_clear_stencil); + } + for (uint32_t i = 0; i < p_splits; i++) { VkViewport viewport; viewport.x = viewport_offset.x; viewport.y = viewport_offset.y; @@ -6322,7 +6728,7 @@ Error RenderingDeviceVulkan::draw_list_begin_split(RID p_framebuffer, uint32_t p viewport.minDepth = 0; viewport.maxDepth = 1.0; - vkCmdSetViewport(command_buffer, 0, 1, &viewport); + vkCmdSetViewport(draw_list[i].command_buffer, 0, 1, &viewport); VkRect2D scissor; scissor.offset.x = viewport_offset.x; @@ -6330,10 +6736,8 @@ Error RenderingDeviceVulkan::draw_list_begin_split(RID p_framebuffer, uint32_t p scissor.extent.width = viewport_size.width; scissor.extent.height = viewport_size.height; - vkCmdSetScissor(command_buffer, 0, 1, &scissor); + vkCmdSetScissor(draw_list[i].command_buffer, 0, 1, &scissor); r_split_ids[i] = (int64_t(ID_TYPE_SPLIT_DRAW_LIST) << ID_BASE_SHIFT) + i; - - draw_list[i].viewport = Rect2i(viewport_offset, viewport_size); } return OK; @@ -6378,7 +6782,7 @@ void RenderingDeviceVulkan::draw_list_bind_render_pipeline(DrawListID p_list, RI const RenderPipeline *pipeline = render_pipeline_owner.getornull(p_render_pipeline); ERR_FAIL_COND(!pipeline); #ifdef DEBUG_ENABLED - ERR_FAIL_COND(pipeline->validation.framebuffer_format != dl->validation.framebuffer_format); + ERR_FAIL_COND(pipeline->validation.framebuffer_format != draw_list_framebuffer_format && pipeline->validation.render_pass != draw_list_current_subpass); #endif if (p_render_pipeline == dl->state.pipeline) { @@ -6717,30 +7121,162 @@ void RenderingDeviceVulkan::draw_list_disable_scissor(DrawListID p_list) { vkCmdSetScissor(dl->command_buffer, 0, 1, &scissor); } -void RenderingDeviceVulkan::draw_list_end(uint32_t p_post_barrier) { - _THREAD_SAFE_METHOD_ +RenderingDevice::DrawListID RenderingDeviceVulkan::draw_list_switch_to_next_pass() { + ERR_FAIL_COND_V(draw_list == nullptr, INVALID_ID); + ERR_FAIL_COND_V(draw_list_current_subpass >= draw_list_subpass_count - 1, INVALID_FORMAT_ID); - ERR_FAIL_COND_MSG(!draw_list, "Immediate draw list is already inactive."); + draw_list_current_subpass++; + + Rect2i viewport; + _draw_list_free(&viewport); + + vkCmdNextSubpass(frames[frame].draw_command_buffer, VK_SUBPASS_CONTENTS_INLINE); + + _draw_list_allocate(viewport, 0, draw_list_current_subpass); + + return int64_t(ID_TYPE_DRAW_LIST) << ID_BASE_SHIFT; +} +Error RenderingDeviceVulkan::draw_list_switch_to_next_pass_split(uint32_t p_splits, DrawListID *r_split_ids) { + ERR_FAIL_COND_V(draw_list == nullptr, ERR_INVALID_PARAMETER); + ERR_FAIL_COND_V(draw_list_current_subpass >= draw_list_subpass_count - 1, ERR_INVALID_PARAMETER); + + draw_list_current_subpass++; + + Rect2i viewport; + _draw_list_free(&viewport); + + vkCmdNextSubpass(frames[frame].draw_command_buffer, VK_SUBPASS_CONTENTS_INLINE); + + _draw_list_allocate(viewport, p_splits, draw_list_current_subpass); + + for (uint32_t i = 0; i < p_splits; i++) { + r_split_ids[i] = (int64_t(ID_TYPE_SPLIT_DRAW_LIST) << ID_BASE_SHIFT) + i; + } + + return OK; +} + +Error RenderingDeviceVulkan::_draw_list_allocate(const Rect2i &p_viewport, uint32_t p_splits, uint32_t p_subpass) { + if (p_splits == 0) { + draw_list = memnew(DrawList); + draw_list->command_buffer = frames[frame].draw_command_buffer; + draw_list->viewport = p_viewport; + draw_list_count = 0; + draw_list_split = false; + } else { + if (p_splits > (uint32_t)split_draw_list_allocators.size()) { + uint32_t from = split_draw_list_allocators.size(); + split_draw_list_allocators.resize(p_splits); + for (uint32_t i = from; i < p_splits; i++) { + VkCommandPoolCreateInfo cmd_pool_info; + cmd_pool_info.sType = VK_STRUCTURE_TYPE_COMMAND_POOL_CREATE_INFO; + cmd_pool_info.pNext = nullptr; + cmd_pool_info.queueFamilyIndex = context->get_graphics_queue(); + cmd_pool_info.flags = VK_COMMAND_POOL_CREATE_RESET_COMMAND_BUFFER_BIT; + + VkResult res = vkCreateCommandPool(device, &cmd_pool_info, nullptr, &split_draw_list_allocators.write[i].command_pool); + ERR_FAIL_COND_V_MSG(res, ERR_CANT_CREATE, "vkCreateCommandPool failed with error " + itos(res) + "."); + + for (int j = 0; j < frame_count; j++) { + VkCommandBuffer command_buffer; + + VkCommandBufferAllocateInfo cmdbuf; + //no command buffer exists, create it. + cmdbuf.sType = VK_STRUCTURE_TYPE_COMMAND_BUFFER_ALLOCATE_INFO; + cmdbuf.pNext = nullptr; + cmdbuf.commandPool = split_draw_list_allocators[i].command_pool; + cmdbuf.level = VK_COMMAND_BUFFER_LEVEL_SECONDARY; + cmdbuf.commandBufferCount = 1; + + VkResult err = vkAllocateCommandBuffers(device, &cmdbuf, &command_buffer); + ERR_FAIL_COND_V_MSG(err, ERR_CANT_CREATE, "vkAllocateCommandBuffers failed with error " + itos(err) + "."); + + split_draw_list_allocators.write[i].command_buffers.push_back(command_buffer); + } + } + } + draw_list = memnew_arr(DrawList, p_splits); + draw_list_count = p_splits; + draw_list_split = true; + + for (uint32_t i = 0; i < p_splits; i++) { + //take a command buffer and initialize it + VkCommandBuffer command_buffer = split_draw_list_allocators[i].command_buffers[frame]; + + VkCommandBufferInheritanceInfo inheritance_info; + inheritance_info.sType = VK_STRUCTURE_TYPE_COMMAND_BUFFER_INHERITANCE_INFO; + inheritance_info.pNext = nullptr; + inheritance_info.renderPass = draw_list_render_pass; + inheritance_info.subpass = p_subpass; + inheritance_info.framebuffer = draw_list_vkframebuffer; + inheritance_info.occlusionQueryEnable = false; + inheritance_info.queryFlags = 0; //? + inheritance_info.pipelineStatistics = 0; + VkCommandBufferBeginInfo cmdbuf_begin; + cmdbuf_begin.sType = VK_STRUCTURE_TYPE_COMMAND_BUFFER_BEGIN_INFO; + cmdbuf_begin.pNext = nullptr; + cmdbuf_begin.flags = VK_COMMAND_BUFFER_USAGE_SIMULTANEOUS_USE_BIT | VK_COMMAND_BUFFER_USAGE_RENDER_PASS_CONTINUE_BIT; + cmdbuf_begin.pInheritanceInfo = &inheritance_info; + + VkResult res = vkResetCommandBuffer(command_buffer, 0); + if (res) { + memdelete_arr(draw_list); + draw_list = nullptr; + ERR_FAIL_V_MSG(ERR_CANT_CREATE, "vkResetCommandBuffer failed with error " + itos(res) + "."); + } + + res = vkBeginCommandBuffer(command_buffer, &cmdbuf_begin); + if (res) { + memdelete_arr(draw_list); + draw_list = nullptr; + ERR_FAIL_V_MSG(ERR_CANT_CREATE, "vkBeginCommandBuffer failed with error " + itos(res) + "."); + } + + draw_list[i].command_buffer = command_buffer; + draw_list[i].viewport = p_viewport; + } + } + + return OK; +} + +void RenderingDeviceVulkan::_draw_list_free(Rect2i *r_last_viewport) { if (draw_list_split) { //send all command buffers VkCommandBuffer *command_buffers = (VkCommandBuffer *)alloca(sizeof(VkCommandBuffer) * draw_list_count); for (uint32_t i = 0; i < draw_list_count; i++) { vkEndCommandBuffer(draw_list[i].command_buffer); command_buffers[i] = draw_list[i].command_buffer; + if (r_last_viewport) { + if (i == 0 || draw_list[i].viewport_set) { + *r_last_viewport = draw_list[i].viewport; + } + } } vkCmdExecuteCommands(frames[frame].draw_command_buffer, draw_list_count, command_buffers); - vkCmdEndRenderPass(frames[frame].draw_command_buffer); memdelete_arr(draw_list); draw_list = nullptr; } else { + if (r_last_viewport) { + *r_last_viewport = draw_list->viewport; + } //just end the list - vkCmdEndRenderPass(draw_list->command_buffer); memdelete(draw_list); draw_list = nullptr; } +} + +void RenderingDeviceVulkan::draw_list_end(uint32_t p_post_barrier) { + _THREAD_SAFE_METHOD_ + + ERR_FAIL_COND_MSG(!draw_list, "Immediate draw list is already inactive."); + + _draw_list_free(); + + vkCmdEndRenderPass(frames[frame].draw_command_buffer); for (int i = 0; i < draw_list_bound_textures.size(); i++) { Texture *texture = texture_owner.getornull(draw_list_bound_textures[i]); @@ -6817,7 +7353,7 @@ void RenderingDeviceVulkan::draw_list_end(uint32_t p_post_barrier) { // To ensure proper synchronization, we must make sure rendering is done before: // * Some buffer is copied - // * Another render pass happens (since we may be done + // * Another render pass happens (since we may be done) #ifdef FORCE_FULL_BARRIER _full_barrier(true); @@ -7465,6 +8001,10 @@ void RenderingDeviceVulkan::_free_internal(RID p_id) { } else if (uniform_set_owner.owns(p_id)) { UniformSet *uniform_set = uniform_set_owner.getornull(p_id); frames[frame].uniform_sets_to_dispose_of.push_back(*uniform_set); + if (uniform_set->invalidated_callback != nullptr) { + uniform_set->invalidated_callback(p_id, uniform_set->invalidated_callback_userdata); + } + uniform_set_owner.free(p_id); } else if (render_pipeline_owner.owns(p_id)) { RenderPipeline *pipeline = render_pipeline_owner.getornull(p_id); @@ -7749,6 +8289,7 @@ void RenderingDeviceVulkan::_free_pending_resources(int p_frame) { vkDestroyImageView(device, texture->view, nullptr); if (texture->owner.is_null()) { //actually owns the image and the allocation too + image_memory -= texture->allocation_info.size; vmaDestroyImage(allocator, texture->image, texture->allocation); } frames[p_frame].textures_to_dispose_of.pop_front(); @@ -7772,15 +8313,21 @@ uint32_t RenderingDeviceVulkan::get_frame_delay() const { return frame_count; } -uint64_t RenderingDeviceVulkan::get_memory_usage() const { - VmaStats stats; - vmaCalculateStats(allocator, &stats); - return stats.total.usedBytes; +uint64_t RenderingDeviceVulkan::get_memory_usage(MemoryType p_type) const { + if (p_type == MEMORY_BUFFERS) { + return buffer_memory; + } else if (p_type == MEMORY_TEXTURES) { + return image_memory; + } else { + VmaStats stats; + vmaCalculateStats(allocator, &stats); + return stats.total.usedBytes; + } } void RenderingDeviceVulkan::_flush(bool p_current_frame) { if (local_device.is_valid() && !p_current_frame) { - return; //flushign previous frames has no effect with local device + return; //flushing previous frames has no effect with local device } //not doing this crashes RADV (undefined behavior) if (p_current_frame) { @@ -7834,6 +8381,22 @@ void RenderingDeviceVulkan::_flush(bool p_current_frame) { } void RenderingDeviceVulkan::initialize(VulkanContext *p_context, bool p_local_device) { + // get our device capabilities + { + device_capabilities.version_major = p_context->get_vulkan_major(); + device_capabilities.version_minor = p_context->get_vulkan_minor(); + + // get info about subgroups + VulkanContext::SubgroupCapabilities subgroup_capabilities = p_context->get_subgroup_capabilities(); + device_capabilities.subgroup_size = subgroup_capabilities.size; + device_capabilities.subgroup_in_shaders = subgroup_capabilities.supported_stages_flags_rd(); + device_capabilities.subgroup_operations = subgroup_capabilities.supported_operations_flags_rd(); + + // get info about further features + VulkanContext::MultiviewCapabilities multiview_capabilies = p_context->get_multiview_capabilities(); + device_capabilities.supports_multiview = multiview_capabilies.is_supported && multiview_capabilies.max_view_count > 1; + } + context = p_context; device = p_context->get_device(); if (p_local_device) { @@ -7974,7 +8537,11 @@ void RenderingDeviceVulkan::_free_rids(T &p_owner, const char *p_type) { List<RID> owned; p_owner.get_owned_list(&owned); if (owned.size()) { - WARN_PRINT(itos(owned.size()) + " RIDs of type '" + p_type + "' were leaked."); + if (owned.size() == 1) { + WARN_PRINT(vformat("1 RID of type \"%s\" was leaked.", p_type)); + } else { + WARN_PRINT(vformat("%d RIDs of type \"%s\" were leaked.", owned.size(), p_type)); + } for (List<RID>::Element *E = owned.front(); E; E = E->next()) { free(E->get()); } @@ -8187,7 +8754,11 @@ void RenderingDeviceVulkan::finalize() { List<RID> owned; texture_owner.get_owned_list(&owned); if (owned.size()) { - WARN_PRINT(itos(owned.size()) + " RIDs of type 'Texture' were leaked."); + if (owned.size() == 1) { + WARN_PRINT("1 RID of type \"Texture\" was leaked."); + } else { + WARN_PRINT(vformat("%d RIDs of type \"Texture\" were leaked.", owned.size())); + } //free shared first for (List<RID>::Element *E = owned.front(); E;) { List<RID>::Element *N = E->next(); @@ -8253,6 +8824,7 @@ RenderingDevice *RenderingDeviceVulkan::create_local_device() { } RenderingDeviceVulkan::RenderingDeviceVulkan() { + device_capabilities.device_family = DEVICE_VULKAN; } RenderingDeviceVulkan::~RenderingDeviceVulkan() { diff --git a/drivers/vulkan/rendering_device_vulkan.h b/drivers/vulkan/rendering_device_vulkan.h index a2527d5c33..8b95ff43b8 100644 --- a/drivers/vulkan/rendering_device_vulkan.h +++ b/drivers/vulkan/rendering_device_vulkan.h @@ -156,6 +156,7 @@ class RenderingDeviceVulkan : public RenderingDevice { uint32_t texture_upload_region_size_px = 0; Vector<uint8_t> _texture_get_data_from_image(Texture *tex, VkImage p_image, VmaAllocation p_allocation, uint32_t p_layer, bool p_2d = false); + Error _texture_update(RID p_texture, uint32_t p_layer, const Vector<uint8_t> &p_data, uint32_t p_post_barrier, bool p_use_setup_queue); /*****************/ /**** SAMPLER ****/ @@ -234,7 +235,87 @@ class RenderingDeviceVulkan : public RenderingDevice { struct FramebufferFormatKey { Vector<AttachmentFormat> attachments; + Vector<FramebufferPass> passes; + uint32_t view_count = 1; bool operator<(const FramebufferFormatKey &p_key) const { + if (view_count != p_key.view_count) { + return view_count < p_key.view_count; + } + + uint32_t pass_size = passes.size(); + uint32_t key_pass_size = p_key.passes.size(); + if (pass_size != key_pass_size) { + return pass_size < key_pass_size; + } + const FramebufferPass *pass_ptr = passes.ptr(); + const FramebufferPass *key_pass_ptr = p_key.passes.ptr(); + + for (uint32_t i = 0; i < pass_size; i++) { + { //compare color attachments + uint32_t attachment_size = pass_ptr[i].color_attachments.size(); + uint32_t key_attachment_size = key_pass_ptr[i].color_attachments.size(); + if (attachment_size != key_attachment_size) { + return attachment_size < key_attachment_size; + } + const int32_t *pass_attachment_ptr = pass_ptr[i].color_attachments.ptr(); + const int32_t *key_pass_attachment_ptr = key_pass_ptr[i].color_attachments.ptr(); + + for (uint32_t j = 0; j < attachment_size; j++) { + if (pass_attachment_ptr[j] != key_pass_attachment_ptr[j]) { + return pass_attachment_ptr[j] < key_pass_attachment_ptr[j]; + } + } + } + { //compare input attachments + uint32_t attachment_size = pass_ptr[i].input_attachments.size(); + uint32_t key_attachment_size = key_pass_ptr[i].input_attachments.size(); + if (attachment_size != key_attachment_size) { + return attachment_size < key_attachment_size; + } + const int32_t *pass_attachment_ptr = pass_ptr[i].input_attachments.ptr(); + const int32_t *key_pass_attachment_ptr = key_pass_ptr[i].input_attachments.ptr(); + + for (uint32_t j = 0; j < attachment_size; j++) { + if (pass_attachment_ptr[j] != key_pass_attachment_ptr[j]) { + return pass_attachment_ptr[j] < key_pass_attachment_ptr[j]; + } + } + } + { //compare resolve attachments + uint32_t attachment_size = pass_ptr[i].resolve_attachments.size(); + uint32_t key_attachment_size = key_pass_ptr[i].resolve_attachments.size(); + if (attachment_size != key_attachment_size) { + return attachment_size < key_attachment_size; + } + const int32_t *pass_attachment_ptr = pass_ptr[i].resolve_attachments.ptr(); + const int32_t *key_pass_attachment_ptr = key_pass_ptr[i].resolve_attachments.ptr(); + + for (uint32_t j = 0; j < attachment_size; j++) { + if (pass_attachment_ptr[j] != key_pass_attachment_ptr[j]) { + return pass_attachment_ptr[j] < key_pass_attachment_ptr[j]; + } + } + } + { //compare preserve attachments + uint32_t attachment_size = pass_ptr[i].preserve_attachments.size(); + uint32_t key_attachment_size = key_pass_ptr[i].preserve_attachments.size(); + if (attachment_size != key_attachment_size) { + return attachment_size < key_attachment_size; + } + const int32_t *pass_attachment_ptr = pass_ptr[i].preserve_attachments.ptr(); + const int32_t *key_pass_attachment_ptr = key_pass_ptr[i].preserve_attachments.ptr(); + + for (uint32_t j = 0; j < attachment_size; j++) { + if (pass_attachment_ptr[j] != key_pass_attachment_ptr[j]) { + return pass_attachment_ptr[j] < key_pass_attachment_ptr[j]; + } + } + } + if (pass_ptr[i].depth_attachment != key_pass_ptr[i].depth_attachment) { + return pass_ptr[i].depth_attachment < key_pass_ptr[i].depth_attachment; + } + } + int as = attachments.size(); int bs = p_key.attachments.size(); if (as != bs) { @@ -261,16 +342,15 @@ class RenderingDeviceVulkan : public RenderingDevice { } }; - VkRenderPass _render_pass_create(const Vector<AttachmentFormat> &p_format, InitialAction p_initial_action, FinalAction p_final_action, InitialAction p_initial_depth_action, FinalAction p_final_depthcolor_action, int *r_color_attachment_count = nullptr); - + VkRenderPass _render_pass_create(const Vector<AttachmentFormat> &p_attachments, const Vector<FramebufferPass> &p_passes, InitialAction p_initial_action, FinalAction p_final_action, InitialAction p_initial_depth_action, FinalAction p_final_depth_action, uint32_t p_view_count = 1, Vector<TextureSamples> *r_samples = nullptr); // This is a cache and it's never freed, it ensures // IDs for a given format are always unique. Map<FramebufferFormatKey, FramebufferFormatID> framebuffer_format_cache; struct FramebufferFormat { const Map<FramebufferFormatKey, FramebufferFormatID>::Element *E; VkRenderPass render_pass = VK_NULL_HANDLE; //here for constructing shaders, never used, see section (7.2. Render Pass Compatibility from Vulkan spec) - int color_attachments = 0; //used for pipeline validation - TextureSamples samples; + Vector<TextureSamples> pass_samples; + uint32_t view_count = 1; // number of views }; Map<FramebufferFormatID, FramebufferFormat> framebuffer_formats; @@ -282,11 +362,17 @@ class RenderingDeviceVulkan : public RenderingDevice { FinalAction final_color_action; InitialAction initial_depth_action; FinalAction final_depth_action; + uint32_t view_count; + bool operator<(const VersionKey &p_key) const { if (initial_color_action == p_key.initial_color_action) { if (final_color_action == p_key.final_color_action) { if (initial_depth_action == p_key.initial_depth_action) { - return final_depth_action < p_key.final_depth_action; + if (final_depth_action == p_key.final_depth_action) { + return view_count < p_key.view_count; + } else { + return final_depth_action < p_key.final_depth_action; + } } else { return initial_depth_action < p_key.initial_depth_action; } @@ -305,10 +391,12 @@ class RenderingDeviceVulkan : public RenderingDevice { struct Version { VkFramebuffer framebuffer = VK_NULL_HANDLE; VkRenderPass render_pass = VK_NULL_HANDLE; //this one is owned + uint32_t subpass_count = 1; }; Map<VersionKey, Version> framebuffers; Size2 size; + uint32_t view_count; }; RID_Owner<Framebuffer, true> framebuffer_owner; @@ -524,7 +612,7 @@ class RenderingDeviceVulkan : public RenderingDevice { }; uint32_t vertex_input_mask = 0; //inputs used, this is mostly for validation - int fragment_outputs = 0; + uint32_t fragment_output_mask = 0; struct PushConstant { uint32_t push_constant_size = 0; @@ -535,11 +623,17 @@ class RenderingDeviceVulkan : public RenderingDevice { uint32_t compute_local_size[3] = { 0, 0, 0 }; + struct SpecializationConstant { + PipelineSpecializationConstant constant; + uint32_t stage_flags = 0; + }; + bool is_compute = false; int max_output = 0; Vector<Set> sets; Vector<uint32_t> set_formats; Vector<VkPipelineShaderStageCreateInfo> pipeline_stages; + Vector<SpecializationConstant> specialization_constants; VkPipelineLayout pipeline_layout = VK_NULL_HANDLE; }; @@ -644,6 +738,8 @@ class RenderingDeviceVulkan : public RenderingDevice { LocalVector<AttachableTexture> attachable_textures; //used for validation Vector<Texture *> mutable_sampled_textures; //used for layout change Vector<Texture *> mutable_storage_textures; //used for layout change + UniformSetInvalidatedCallback invalidated_callback = nullptr; + void *invalidated_callback_userdata = nullptr; }; RID_Owner<UniformSet, true> uniform_set_owner; @@ -668,6 +764,7 @@ class RenderingDeviceVulkan : public RenderingDevice { #ifdef DEBUG_ENABLED struct Validation { FramebufferFormatID framebuffer_format = 0; + uint32_t render_pass = 0; uint32_t dynamic_state = 0; VertexFormatID vertex_format = 0; bool uses_restart_indices = false; @@ -723,6 +820,7 @@ class RenderingDeviceVulkan : public RenderingDevice { struct DrawList { VkCommandBuffer command_buffer = VK_NULL_HANDLE; // If persistent, this is owned, otherwise it's shared with the ringbuffer. Rect2i viewport; + bool viewport_set = false; struct SetState { uint32_t pipeline_expected_format = 0; @@ -746,7 +844,6 @@ class RenderingDeviceVulkan : public RenderingDevice { #ifdef DEBUG_ENABLED struct Validation { bool active = true; // Means command buffer was not closed, so you can keep adding things. - FramebufferFormatID framebuffer_format = INVALID_ID; // Actual render pass values. uint32_t dynamic_state = 0; VertexFormatID vertex_format = INVALID_ID; @@ -782,7 +879,15 @@ class RenderingDeviceVulkan : public RenderingDevice { }; DrawList *draw_list = nullptr; // One for regular draw lists, multiple for split. + uint32_t draw_list_subpass_count = 0; uint32_t draw_list_count = 0; + VkRenderPass draw_list_render_pass; + VkFramebuffer draw_list_vkframebuffer; +#ifdef DEBUG_ENABLED + FramebufferFormatID draw_list_framebuffer_format = INVALID_ID; +#endif + uint32_t draw_list_current_subpass = 0; + bool draw_list_split = false; Vector<RID> draw_list_bound_textures; Vector<RID> draw_list_storage_textures; @@ -790,10 +895,12 @@ class RenderingDeviceVulkan : public RenderingDevice { bool draw_list_unbind_depth_textures = false; void _draw_list_insert_clear_region(DrawList *draw_list, Framebuffer *framebuffer, Point2i viewport_offset, Point2i viewport_size, bool p_clear_color, const Vector<Color> &p_clear_colors, bool p_clear_depth, float p_depth, uint32_t p_stencil); - Error _draw_list_setup_framebuffer(Framebuffer *p_framebuffer, InitialAction p_initial_color_action, FinalAction p_final_color_action, InitialAction p_initial_depth_action, FinalAction p_final_depth_action, VkFramebuffer *r_framebuffer, VkRenderPass *r_render_pass); + Error _draw_list_setup_framebuffer(Framebuffer *p_framebuffer, InitialAction p_initial_color_action, FinalAction p_final_color_action, InitialAction p_initial_depth_action, FinalAction p_final_depth_action, VkFramebuffer *r_framebuffer, VkRenderPass *r_render_pass, uint32_t *r_subpass_count); Error _draw_list_render_pass_begin(Framebuffer *framebuffer, InitialAction p_initial_color_action, FinalAction p_final_color_action, InitialAction p_initial_depth_action, FinalAction p_final_depth_action, const Vector<Color> &p_clear_colors, float p_clear_depth, uint32_t p_clear_stencil, Point2i viewport_offset, Point2i viewport_size, VkFramebuffer vkframebuffer, VkRenderPass render_pass, VkCommandBuffer command_buffer, VkSubpassContents subpass_contents, const Vector<RID> &p_storage_textures); _FORCE_INLINE_ DrawList *_get_draw_list_ptr(DrawListID p_id); Buffer *_get_buffer_from_owner(RID p_buffer, VkPipelineStageFlags &dst_stage_mask, VkAccessFlags &dst_access, uint32_t p_post_barrier); + Error _draw_list_allocate(const Rect2i &p_viewport, uint32_t p_splits, uint32_t p_subpass); + void _draw_list_free(Rect2i *r_last_viewport = nullptr); /**********************/ /**** COMPUTE LIST ****/ @@ -907,6 +1014,9 @@ class RenderingDeviceVulkan : public RenderingDevice { VulkanContext *context = nullptr; + uint64_t image_memory = 0; + uint64_t buffer_memory = 0; + void _free_internal(RID p_id); void _flush(bool p_current_frame); @@ -938,11 +1048,13 @@ public: /**** FRAMEBUFFER ****/ /*********************/ - virtual FramebufferFormatID framebuffer_format_create(const Vector<AttachmentFormat> &p_format); + virtual FramebufferFormatID framebuffer_format_create(const Vector<AttachmentFormat> &p_format, uint32_t p_view_count = 1); + virtual FramebufferFormatID framebuffer_format_create_multipass(const Vector<AttachmentFormat> &p_attachments, Vector<FramebufferPass> &p_passes, uint32_t p_view_count = 1); virtual FramebufferFormatID framebuffer_format_create_empty(TextureSamples p_samples = TEXTURE_SAMPLES_1); - virtual TextureSamples framebuffer_format_get_texture_samples(FramebufferFormatID p_format); + virtual TextureSamples framebuffer_format_get_texture_samples(FramebufferFormatID p_format, uint32_t p_pass = 0); - virtual RID framebuffer_create(const Vector<RID> &p_texture_attachments, FramebufferFormatID p_format_check = INVALID_ID); + virtual RID framebuffer_create(const Vector<RID> &p_texture_attachments, FramebufferFormatID p_format_check = INVALID_ID, uint32_t p_view_count = 1); + virtual RID framebuffer_create_multipass(const Vector<RID> &p_texture_attachments, Vector<FramebufferPass> &p_passes, FramebufferFormatID p_format_check = INVALID_ID, uint32_t p_view_count = 1); virtual RID framebuffer_create_empty(const Size2i &p_size, TextureSamples p_samples = TEXTURE_SAMPLES_1, FramebufferFormatID p_format_check = INVALID_ID); virtual FramebufferFormatID framebuffer_get_format(RID p_framebuffer); @@ -984,6 +1096,7 @@ public: virtual RID uniform_set_create(const Vector<Uniform> &p_uniforms, RID p_shader, uint32_t p_shader_set); virtual bool uniform_set_is_valid(RID p_uniform_set); + virtual void uniform_set_set_invalidation_callback(RID p_uniform_set, UniformSetInvalidatedCallback p_callback, void *p_userdata); virtual Error buffer_update(RID p_buffer, uint32_t p_offset, uint32_t p_size, const void *p_data, uint32_t p_post_barrier = BARRIER_MASK_ALL); //works for any buffer virtual Error buffer_clear(RID p_buffer, uint32_t p_offset, uint32_t p_size, uint32_t p_post_barrier = BARRIER_MASK_ALL); @@ -993,14 +1106,14 @@ public: /**** RENDER PIPELINE ****/ /*************************/ - virtual RID render_pipeline_create(RID p_shader, FramebufferFormatID p_framebuffer_format, VertexFormatID p_vertex_format, RenderPrimitive p_render_primitive, const PipelineRasterizationState &p_rasterization_state, const PipelineMultisampleState &p_multisample_state, const PipelineDepthStencilState &p_depth_stencil_state, const PipelineColorBlendState &p_blend_state, int p_dynamic_state_flags = 0); + virtual RID render_pipeline_create(RID p_shader, FramebufferFormatID p_framebuffer_format, VertexFormatID p_vertex_format, RenderPrimitive p_render_primitive, const PipelineRasterizationState &p_rasterization_state, const PipelineMultisampleState &p_multisample_state, const PipelineDepthStencilState &p_depth_stencil_state, const PipelineColorBlendState &p_blend_state, int p_dynamic_state_flags = 0, uint32_t p_for_render_pass = 0, const Vector<PipelineSpecializationConstant> &p_specialization_constants = Vector<PipelineSpecializationConstant>()); virtual bool render_pipeline_is_valid(RID p_pipeline); /**************************/ /**** COMPUTE PIPELINE ****/ /**************************/ - virtual RID compute_pipeline_create(RID p_shader); + virtual RID compute_pipeline_create(RID p_shader, const Vector<PipelineSpecializationConstant> &p_specialization_constants = Vector<PipelineSpecializationConstant>()); virtual bool compute_pipeline_is_valid(RID p_pipeline); /****************/ @@ -1032,6 +1145,9 @@ public: virtual void draw_list_enable_scissor(DrawListID p_list, const Rect2 &p_rect); virtual void draw_list_disable_scissor(DrawListID p_list); + virtual DrawListID draw_list_switch_to_next_pass(); + virtual Error draw_list_switch_to_next_pass_split(uint32_t p_splits, DrawListID *r_split_ids); + virtual void draw_list_end(uint32_t p_post_barrier = BARRIER_MASK_ALL); /***********************/ @@ -1088,7 +1204,7 @@ public: virtual RenderingDevice *create_local_device(); - virtual uint64_t get_memory_usage() const; + virtual uint64_t get_memory_usage(MemoryType p_type) const; virtual void set_resource_name(RID p_id, const String p_name); diff --git a/drivers/vulkan/vulkan_context.cpp b/drivers/vulkan/vulkan_context.cpp index c564cee757..d35c519320 100644 --- a/drivers/vulkan/vulkan_context.cpp +++ b/drivers/vulkan/vulkan_context.cpp @@ -34,12 +34,14 @@ #include "core/config/project_settings.h" #include "core/string/ustring.h" #include "core/version.h" +#include "servers/rendering/rendering_device.h" #include "vk_enum_string_helper.h" #include <stdio.h> #include <stdlib.h> #include <string.h> +#include <vector> #define ARRAY_SIZE(a) (sizeof(a) / sizeof(a[0])) #define APP_SHORT_NAME "GodotEngine" @@ -163,7 +165,36 @@ VKAPI_ATTR VkBool32 VKAPI_CALL VulkanContext::_debug_messenger_callback( return VK_FALSE; } -VkBool32 VulkanContext::_check_layers(uint32_t check_count, const char **check_names, uint32_t layer_count, VkLayerProperties *layers) { +VKAPI_ATTR VkBool32 VKAPI_CALL VulkanContext::_debug_report_callback( + VkDebugReportFlagsEXT flags, + VkDebugReportObjectTypeEXT objectType, + uint64_t object, + size_t location, + int32_t messageCode, + const char *pLayerPrefix, + const char *pMessage, + void *pUserData) { + String debugMessage = String("Vulkan Debug Report: object - ") + + String::num_int64(object) + "\n" + pMessage; + + switch (flags) { + case VK_DEBUG_REPORT_DEBUG_BIT_EXT: + case VK_DEBUG_REPORT_INFORMATION_BIT_EXT: + print_line(debugMessage); + break; + case VK_DEBUG_REPORT_WARNING_BIT_EXT: + case VK_DEBUG_REPORT_PERFORMANCE_WARNING_BIT_EXT: + WARN_PRINT(debugMessage); + break; + case VK_DEBUG_REPORT_ERROR_BIT_EXT: + ERR_PRINT(debugMessage); + break; + } + + return VK_FALSE; +} + +VkBool32 VulkanContext::_check_layers(uint32_t check_count, const char *const *check_names, uint32_t layer_count, VkLayerProperties *layers) { for (uint32_t i = 0; i < check_count; i++) { VkBool32 found = 0; for (uint32_t j = 0; j < layer_count; j++) { @@ -180,55 +211,86 @@ VkBool32 VulkanContext::_check_layers(uint32_t check_count, const char **check_n return 1; } -Error VulkanContext::_create_validation_layers() { +Error VulkanContext::_get_preferred_validation_layers(uint32_t *count, const char *const **names) { + static const std::vector<std::vector<const char *>> instance_validation_layers_alt{ + // Preferred set of validation layers + { "VK_LAYER_KHRONOS_validation" }, + + // Alternative (deprecated, removed in SDK 1.1.126.0) set of validation layers + { "VK_LAYER_LUNARG_standard_validation" }, + + // Alternative (deprecated, removed in SDK 1.1.121.1) set of validation layers + { "VK_LAYER_GOOGLE_threading", "VK_LAYER_LUNARG_parameter_validation", "VK_LAYER_LUNARG_object_tracker", "VK_LAYER_LUNARG_core_validation", "VK_LAYER_GOOGLE_unique_objects" } + }; + + // Clear out-arguments + *count = 0; + if (names != nullptr) { + *names = nullptr; + } + VkResult err; - const char *instance_validation_layers_alt1[] = { "VK_LAYER_KHRONOS_validation" }; - const char *instance_validation_layers_alt2[] = { "VK_LAYER_LUNARG_standard_validation" }; - const char *instance_validation_layers_alt3[] = { "VK_LAYER_GOOGLE_threading", "VK_LAYER_LUNARG_parameter_validation", "VK_LAYER_LUNARG_object_tracker", "VK_LAYER_LUNARG_core_validation", "VK_LAYER_GOOGLE_unique_objects" }; + uint32_t instance_layer_count; - uint32_t instance_layer_count = 0; err = vkEnumerateInstanceLayerProperties(&instance_layer_count, nullptr); - ERR_FAIL_COND_V(err, ERR_CANT_CREATE); + if (err) { + ERR_FAIL_V(ERR_CANT_CREATE); + } - VkBool32 validation_found = 0; - uint32_t validation_layer_count = 0; - const char **instance_validation_layers = nullptr; - if (instance_layer_count > 0) { - VkLayerProperties *instance_layers = (VkLayerProperties *)malloc(sizeof(VkLayerProperties) * instance_layer_count); - err = vkEnumerateInstanceLayerProperties(&instance_layer_count, instance_layers); - if (err) { - free(instance_layers); - ERR_FAIL_V(ERR_CANT_CREATE); - } + if (instance_layer_count < 1) { + return OK; + } - validation_layer_count = ARRAY_SIZE(instance_validation_layers_alt1); - instance_validation_layers = instance_validation_layers_alt1; - validation_found = _check_layers(validation_layer_count, instance_validation_layers, instance_layer_count, instance_layers); + VkLayerProperties *instance_layers = (VkLayerProperties *)malloc(sizeof(VkLayerProperties) * instance_layer_count); + err = vkEnumerateInstanceLayerProperties(&instance_layer_count, instance_layers); + if (err) { + free(instance_layers); + ERR_FAIL_V(ERR_CANT_CREATE); + } - // use alternative (deprecated, removed in SDK 1.1.126.0) set of validation layers - if (!validation_found) { - validation_layer_count = ARRAY_SIZE(instance_validation_layers_alt2); - instance_validation_layers = instance_validation_layers_alt2; - validation_found = _check_layers(validation_layer_count, instance_validation_layers, instance_layer_count, instance_layers); + for (uint32_t i = 0; i < instance_validation_layers_alt.size(); i++) { + if (_check_layers(instance_validation_layers_alt[i].size(), instance_validation_layers_alt[i].data(), instance_layer_count, instance_layers)) { + *count = instance_validation_layers_alt[i].size(); + if (names != nullptr) { + *names = instance_validation_layers_alt[i].data(); + } + break; } + } - // use alternative (deprecated, removed in SDK 1.1.121.1) set of validation layers - if (!validation_found) { - validation_layer_count = ARRAY_SIZE(instance_validation_layers_alt3); - instance_validation_layers = instance_validation_layers_alt3; - validation_found = _check_layers(validation_layer_count, instance_validation_layers, instance_layer_count, instance_layers); - } + free(instance_layers); - free(instance_layers); - } + return OK; +} - if (validation_found) { - enabled_layer_count = validation_layer_count; - for (uint32_t i = 0; i < validation_layer_count; i++) { - enabled_layers[i] = instance_validation_layers[i]; +typedef VkResult(VKAPI_PTR *_vkEnumerateInstanceVersion)(uint32_t *); + +Error VulkanContext::_obtain_vulkan_version() { + // https://www.khronos.org/registry/vulkan/specs/1.2-extensions/man/html/VkApplicationInfo.html#_description + // for Vulkan 1.0 vkEnumerateInstanceVersion is not available, including not in the loader we compile against on Android. + _vkEnumerateInstanceVersion func = (_vkEnumerateInstanceVersion)vkGetInstanceProcAddr(nullptr, "vkEnumerateInstanceVersion"); + if (func != nullptr) { + uint32_t api_version; + VkResult res = func(&api_version); + if (res == VK_SUCCESS) { + vulkan_major = VK_VERSION_MAJOR(api_version); + vulkan_minor = VK_VERSION_MINOR(api_version); + uint32_t vulkan_patch = VK_VERSION_PATCH(api_version); + + print_line("Vulkan API " + itos(vulkan_major) + "." + itos(vulkan_minor) + "." + itos(vulkan_patch)); + } else { + // according to the documentation this shouldn't fail with anything except a memory allocation error + // in which case we're in deep trouble anyway + ERR_FAIL_V(ERR_CANT_CREATE); } } else { - return ERR_CANT_CREATE; + print_line("vkEnumerateInstanceVersion not available, assuming Vulkan 1.0"); + } + + // we don't go above 1.2 + if ((vulkan_major > 1) || (vulkan_major == 1 && vulkan_minor > 2)) { + vulkan_major = 1; + vulkan_minor = 2; } return OK; @@ -238,8 +300,8 @@ Error VulkanContext::_initialize_extensions() { uint32_t instance_extension_count = 0; enabled_extension_count = 0; - enabled_layer_count = 0; enabled_debug_utils = false; + enabled_debug_report = false; /* Look for instance extensions */ VkBool32 surfaceExtFound = 0; VkBool32 platformSurfaceExtFound = 0; @@ -266,14 +328,18 @@ Error VulkanContext::_initialize_extensions() { extension_names[enabled_extension_count++] = _get_platform_surface_extension(); } if (!strcmp(VK_EXT_DEBUG_REPORT_EXTENSION_NAME, instance_extensions[i].extensionName)) { - if (use_validation_layers) { + if (_use_validation_layers()) { extension_names[enabled_extension_count++] = VK_EXT_DEBUG_REPORT_EXTENSION_NAME; + enabled_debug_report = true; } } if (!strcmp(VK_EXT_DEBUG_UTILS_EXTENSION_NAME, instance_extensions[i].extensionName)) { extension_names[enabled_extension_count++] = VK_EXT_DEBUG_UTILS_EXTENSION_NAME; enabled_debug_utils = true; } + if (!strcmp(VK_KHR_GET_PHYSICAL_DEVICE_PROPERTIES_2_EXTENSION_NAME, instance_extensions[i].extensionName)) { + extension_names[enabled_extension_count++] = VK_KHR_GET_PHYSICAL_DEVICE_PROPERTIES_2_EXTENSION_NAME; + } if (enabled_extension_count >= MAX_EXTENSIONS) { free(instance_extensions); ERR_FAIL_V_MSG(ERR_BUG, "Enabled extension count reaches MAX_EXTENSIONS, BUG"); @@ -289,12 +355,258 @@ Error VulkanContext::_initialize_extensions() { return OK; } -Error VulkanContext::_create_physical_device() { - /* Look for validation layers */ - if (use_validation_layers) { - _create_validation_layers(); +uint32_t VulkanContext::SubgroupCapabilities::supported_stages_flags_rd() const { + uint32_t flags = 0; + + if (supportedStages & VK_SHADER_STAGE_VERTEX_BIT) { + flags += RenderingDevice::ShaderStage::SHADER_STAGE_VERTEX_BIT; + } + if (supportedStages & VK_SHADER_STAGE_TESSELLATION_CONTROL_BIT) { + flags += RenderingDevice::ShaderStage::SHADER_STAGE_TESSELATION_CONTROL_BIT; + } + if (supportedStages & VK_SHADER_STAGE_TESSELLATION_EVALUATION_BIT) { + flags += RenderingDevice::ShaderStage::SHADER_STAGE_TESSELATION_EVALUATION_BIT; + } + // if (supportedStages & VK_SHADER_STAGE_GEOMETRY_BIT) { + // flags += RenderingDevice::ShaderStage::SHADER_STAGE_GEOMETRY_BIT; + // } + if (supportedStages & VK_SHADER_STAGE_FRAGMENT_BIT) { + flags += RenderingDevice::ShaderStage::SHADER_STAGE_FRAGMENT_BIT; + } + if (supportedStages & VK_SHADER_STAGE_COMPUTE_BIT) { + flags += RenderingDevice::ShaderStage::SHADER_STAGE_COMPUTE_BIT; + } + + return flags; +} + +String VulkanContext::SubgroupCapabilities::supported_stages_desc() const { + String res; + + if (supportedStages & VK_SHADER_STAGE_VERTEX_BIT) { + res += ", STAGE_VERTEX"; + } + if (supportedStages & VK_SHADER_STAGE_TESSELLATION_CONTROL_BIT) { + res += ", STAGE_TESSELLATION_CONTROL"; + } + if (supportedStages & VK_SHADER_STAGE_TESSELLATION_EVALUATION_BIT) { + res += ", STAGE_TESSELLATION_EVALUATION"; + } + if (supportedStages & VK_SHADER_STAGE_GEOMETRY_BIT) { + res += ", STAGE_GEOMETRY"; + } + if (supportedStages & VK_SHADER_STAGE_FRAGMENT_BIT) { + res += ", STAGE_FRAGMENT"; + } + if (supportedStages & VK_SHADER_STAGE_COMPUTE_BIT) { + res += ", STAGE_COMPUTE"; + } + + /* these are not defined on Android GRMBL */ + if (supportedStages & 0x00000100 /* VK_SHADER_STAGE_RAYGEN_BIT_KHR */) { + res += ", STAGE_RAYGEN_KHR"; + } + if (supportedStages & 0x00000200 /* VK_SHADER_STAGE_ANY_HIT_BIT_KHR */) { + res += ", STAGE_ANY_HIT_KHR"; + } + if (supportedStages & 0x00000400 /* VK_SHADER_STAGE_CLOSEST_HIT_BIT_KHR */) { + res += ", STAGE_CLOSEST_HIT_KHR"; + } + if (supportedStages & 0x00000800 /* VK_SHADER_STAGE_MISS_BIT_KHR */) { + res += ", STAGE_MISS_KHR"; + } + if (supportedStages & 0x00001000 /* VK_SHADER_STAGE_INTERSECTION_BIT_KHR */) { + res += ", STAGE_INTERSECTION_KHR"; + } + if (supportedStages & 0x00002000 /* VK_SHADER_STAGE_CALLABLE_BIT_KHR */) { + res += ", STAGE_CALLABLE_KHR"; + } + if (supportedStages & 0x00000040 /* VK_SHADER_STAGE_TASK_BIT_NV */) { + res += ", STAGE_TASK_NV"; + } + if (supportedStages & 0x00000080 /* VK_SHADER_STAGE_MESH_BIT_NV */) { + res += ", STAGE_MESH_NV"; + } + + return res.substr(2); // remove first ", " +} + +uint32_t VulkanContext::SubgroupCapabilities::supported_operations_flags_rd() const { + uint32_t flags = 0; + + if (supportedOperations & VK_SUBGROUP_FEATURE_BASIC_BIT) { + flags += RenderingDevice::SubgroupOperations::SUBGROUP_BASIC_BIT; + } + if (supportedOperations & VK_SUBGROUP_FEATURE_VOTE_BIT) { + flags += RenderingDevice::SubgroupOperations::SUBGROUP_VOTE_BIT; + } + if (supportedOperations & VK_SUBGROUP_FEATURE_ARITHMETIC_BIT) { + flags += RenderingDevice::SubgroupOperations::SUBGROUP_ARITHMETIC_BIT; + } + if (supportedOperations & VK_SUBGROUP_FEATURE_BALLOT_BIT) { + flags += RenderingDevice::SubgroupOperations::SUBGROUP_BALLOT_BIT; + } + if (supportedOperations & VK_SUBGROUP_FEATURE_SHUFFLE_BIT) { + flags += RenderingDevice::SubgroupOperations::SUBGROUP_SHUFFLE_BIT; + } + if (supportedOperations & VK_SUBGROUP_FEATURE_SHUFFLE_RELATIVE_BIT) { + flags += RenderingDevice::SubgroupOperations::SUBGROUP_SHUFFLE_RELATIVE_BIT; + } + if (supportedOperations & VK_SUBGROUP_FEATURE_CLUSTERED_BIT) { + flags += RenderingDevice::SubgroupOperations::SUBGROUP_CLUSTERED_BIT; + } + if (supportedOperations & VK_SUBGROUP_FEATURE_QUAD_BIT) { + flags += RenderingDevice::SubgroupOperations::SUBGROUP_QUAD_BIT; + } + + return flags; +} + +String VulkanContext::SubgroupCapabilities::supported_operations_desc() const { + String res; + + if (supportedOperations & VK_SUBGROUP_FEATURE_BASIC_BIT) { + res += ", FEATURE_BASIC"; + } + if (supportedOperations & VK_SUBGROUP_FEATURE_VOTE_BIT) { + res += ", FEATURE_VOTE"; + } + if (supportedOperations & VK_SUBGROUP_FEATURE_ARITHMETIC_BIT) { + res += ", FEATURE_ARITHMETIC"; + } + if (supportedOperations & VK_SUBGROUP_FEATURE_BALLOT_BIT) { + res += ", FEATURE_BALLOT"; + } + if (supportedOperations & VK_SUBGROUP_FEATURE_SHUFFLE_BIT) { + res += ", FEATURE_SHUFFLE"; + } + if (supportedOperations & VK_SUBGROUP_FEATURE_SHUFFLE_RELATIVE_BIT) { + res += ", FEATURE_SHUFFLE_RELATIVE"; + } + if (supportedOperations & VK_SUBGROUP_FEATURE_CLUSTERED_BIT) { + res += ", FEATURE_CLUSTERED"; + } + if (supportedOperations & VK_SUBGROUP_FEATURE_QUAD_BIT) { + res += ", FEATURE_QUAD"; + } + if (supportedOperations & VK_SUBGROUP_FEATURE_PARTITIONED_BIT_NV) { + res += ", FEATURE_PARTITIONED_NV"; + } + + return res.substr(2); // remove first ", " +} + +Error VulkanContext::_check_capabilities() { + // https://www.khronos.org/registry/vulkan/specs/1.2-extensions/man/html/VK_KHR_multiview.html + // https://www.khronos.org/blog/vulkan-subgroup-tutorial + + // for Vulkan 1.0 vkGetPhysicalDeviceProperties2 is not available, including not in the loader we compile against on Android. + + // so we check if the functions are accessible by getting their function pointers and skipping if not + // (note that the desktop loader does a better job here but the android loader doesn't) + + // assume not supported until proven otherwise + multiview_capabilities.is_supported = false; + multiview_capabilities.geometry_shader_is_supported = false; + multiview_capabilities.tessellation_shader_is_supported = false; + multiview_capabilities.max_view_count = 0; + multiview_capabilities.max_instance_count = 0; + subgroup_capabilities.size = 0; + subgroup_capabilities.supportedStages = 0; + subgroup_capabilities.supportedOperations = 0; + subgroup_capabilities.quadOperationsInAllStages = false; + + // check for extended features + PFN_vkGetPhysicalDeviceFeatures2 device_features_func = (PFN_vkGetPhysicalDeviceFeatures2)vkGetInstanceProcAddr(inst, "vkGetPhysicalDeviceFeatures2"); + if (device_features_func == nullptr) { + // In Vulkan 1.0 might be accessible under its original extension name + device_features_func = (PFN_vkGetPhysicalDeviceFeatures2)vkGetInstanceProcAddr(inst, "vkGetPhysicalDeviceFeatures2KHR"); + } + if (device_features_func != nullptr) { + // check our extended features + VkPhysicalDeviceMultiviewFeatures multiview_features; + multiview_features.sType = VK_STRUCTURE_TYPE_PHYSICAL_DEVICE_MULTIVIEW_FEATURES; + multiview_features.pNext = NULL; + + VkPhysicalDeviceFeatures2 device_features; + device_features.sType = VK_STRUCTURE_TYPE_PHYSICAL_DEVICE_FEATURES_2; + device_features.pNext = &multiview_features; + + device_features_func(gpu, &device_features); + multiview_capabilities.is_supported = multiview_features.multiview; + multiview_capabilities.geometry_shader_is_supported = multiview_features.multiviewGeometryShader; + multiview_capabilities.tessellation_shader_is_supported = multiview_features.multiviewTessellationShader; + } + + // check extended properties + PFN_vkGetPhysicalDeviceProperties2 device_properties_func = (PFN_vkGetPhysicalDeviceProperties2)vkGetInstanceProcAddr(inst, "vkGetPhysicalDeviceProperties2"); + if (device_properties_func == nullptr) { + // In Vulkan 1.0 might be accessible under its original extension name + device_properties_func = (PFN_vkGetPhysicalDeviceProperties2)vkGetInstanceProcAddr(inst, "vkGetPhysicalDeviceProperties2KHR"); + } + if (device_properties_func != nullptr) { + VkPhysicalDeviceMultiviewProperties multiviewProperties; + VkPhysicalDeviceSubgroupProperties subgroupProperties; + VkPhysicalDeviceProperties2 physicalDeviceProperties; + + subgroupProperties.sType = VK_STRUCTURE_TYPE_PHYSICAL_DEVICE_SUBGROUP_PROPERTIES; + subgroupProperties.pNext = nullptr; + + physicalDeviceProperties.sType = VK_STRUCTURE_TYPE_PHYSICAL_DEVICE_PROPERTIES_2; + + if (multiview_capabilities.is_supported) { + multiviewProperties.sType = VK_STRUCTURE_TYPE_PHYSICAL_DEVICE_MULTIVIEW_PROPERTIES; + multiviewProperties.pNext = &subgroupProperties; + + physicalDeviceProperties.pNext = &multiviewProperties; + } else { + physicalDeviceProperties.pNext = &subgroupProperties; + } + + device_properties_func(gpu, &physicalDeviceProperties); + + subgroup_capabilities.size = subgroupProperties.subgroupSize; + subgroup_capabilities.supportedStages = subgroupProperties.supportedStages; + subgroup_capabilities.supportedOperations = subgroupProperties.supportedOperations; + // Note: quadOperationsInAllStages will be true if: + // - supportedStages has VK_SHADER_STAGE_ALL_GRAPHICS + VK_SHADER_STAGE_COMPUTE_BIT + // - supportedOperations has VK_SUBGROUP_FEATURE_QUAD_BIT + subgroup_capabilities.quadOperationsInAllStages = subgroupProperties.quadOperationsInAllStages; + + if (multiview_capabilities.is_supported) { + multiview_capabilities.max_view_count = multiviewProperties.maxMultiviewViewCount; + multiview_capabilities.max_instance_count = multiviewProperties.maxMultiviewInstanceIndex; + +#ifdef DEBUG_ENABLED + print_line("- Vulkan multiview supported:"); + print_line(" max view count: " + itos(multiview_capabilities.max_view_count)); + print_line(" max instances: " + itos(multiview_capabilities.max_instance_count)); + } else { + print_line("- Vulkan multiview not supported"); +#endif + } + +#ifdef DEBUG_ENABLED + print_line("- Vulkan subgroup:"); + print_line(" size: " + itos(subgroup_capabilities.size)); + print_line(" stages: " + subgroup_capabilities.supported_stages_desc()); + print_line(" supported ops: " + subgroup_capabilities.supported_operations_desc()); + if (subgroup_capabilities.quadOperationsInAllStages) { + print_line(" quad operations in all stages"); + } + } else { + print_line("- Couldn't call vkGetPhysicalDeviceProperties2"); +#endif } + return OK; +} + +Error VulkanContext::_create_physical_device() { + /* obtain version */ + _obtain_vulkan_version(); + + /* initialise extensions */ { Error err = _initialize_extensions(); if (err != OK) { @@ -312,18 +624,16 @@ Error VulkanContext::_create_physical_device() { /*applicationVersion*/ 0, /*pEngineName*/ namecs.get_data(), /*engineVersion*/ 0, - /*apiVersion*/ VK_API_VERSION_1_0, - }; - VkInstanceCreateInfo inst_info = { - /*sType*/ VK_STRUCTURE_TYPE_INSTANCE_CREATE_INFO, - /*pNext*/ nullptr, - /*flags*/ 0, - /*pApplicationInfo*/ &app, - /*enabledLayerCount*/ enabled_layer_count, - /*ppEnabledLayerNames*/ (const char *const *)enabled_layers, - /*enabledExtensionCount*/ enabled_extension_count, - /*ppEnabledExtensionNames*/ (const char *const *)extension_names, + /*apiVersion*/ VK_MAKE_VERSION(vulkan_major, vulkan_minor, 0) }; + VkInstanceCreateInfo inst_info{}; + inst_info.sType = VK_STRUCTURE_TYPE_INSTANCE_CREATE_INFO; + inst_info.pApplicationInfo = &app; + inst_info.enabledExtensionCount = enabled_extension_count; + inst_info.ppEnabledExtensionNames = (const char *const *)extension_names; + if (_use_validation_layers()) { + _get_preferred_validation_layers(&inst_info.enabledLayerCount, &inst_info.ppEnabledLayerNames); + } /* * This is info for a temp callback to use during CreateInstance. @@ -331,6 +641,7 @@ Error VulkanContext::_create_physical_device() { * function to register the final callback. */ VkDebugUtilsMessengerCreateInfoEXT dbg_messenger_create_info; + VkDebugReportCallbackCreateInfoEXT dbg_report_callback_create_info{}; if (enabled_debug_utils) { // VK_EXT_debug_utils style dbg_messenger_create_info.sType = VK_STRUCTURE_TYPE_DEBUG_UTILS_MESSENGER_CREATE_INFO_EXT; @@ -344,6 +655,16 @@ Error VulkanContext::_create_physical_device() { dbg_messenger_create_info.pfnUserCallback = _debug_messenger_callback; dbg_messenger_create_info.pUserData = this; inst_info.pNext = &dbg_messenger_create_info; + } else if (enabled_debug_report) { + dbg_report_callback_create_info.sType = VK_STRUCTURE_TYPE_DEBUG_REPORT_CALLBACK_CREATE_INFO_EXT; + dbg_report_callback_create_info.flags = VK_DEBUG_REPORT_INFORMATION_BIT_EXT | + VK_DEBUG_REPORT_WARNING_BIT_EXT | + VK_DEBUG_REPORT_PERFORMANCE_WARNING_BIT_EXT | + VK_DEBUG_REPORT_ERROR_BIT_EXT | + VK_DEBUG_REPORT_DEBUG_BIT_EXT; + dbg_report_callback_create_info.pfnCallback = _debug_report_callback; + dbg_report_callback_create_info.pUserData = this; + inst_info.pNext = &dbg_report_callback_create_info; } uint32_t gpu_count; @@ -379,8 +700,25 @@ Error VulkanContext::_create_physical_device() { free(physical_devices); ERR_FAIL_V(ERR_CANT_CREATE); } - /* for now, just grab the first physical device */ + + // TODO: At least on Linux Laptops integrated GPUs fail with Vulkan in many instances. + // The device should really be a preference, but for now choosing a discrete GPU over the + // integrated one is better than the default. + + // Default to first device uint32_t device_index = 0; + + for (uint32_t i = 0; i < gpu_count; ++i) { + VkPhysicalDeviceProperties props; + vkGetPhysicalDeviceProperties(physical_devices[i], &props); + + if (props.deviceType == VkPhysicalDeviceType::VK_PHYSICAL_DEVICE_TYPE_DISCRETE_GPU) { + // Prefer discrete GPU. + device_index = i; + break; + } + } + gpu = physical_devices[device_index]; free(physical_devices); @@ -440,6 +778,10 @@ Error VulkanContext::_create_physical_device() { swapchainExtFound = 1; extension_names[enabled_extension_count++] = VK_KHR_SWAPCHAIN_EXTENSION_NAME; } + if (!strcmp(VK_KHR_MULTIVIEW_EXTENSION_NAME, device_extensions[i].extensionName)) { + // if multiview is supported, enable it + extension_names[enabled_extension_count++] = VK_KHR_MULTIVIEW_EXTENSION_NAME; + } if (enabled_extension_count >= MAX_EXTENSIONS) { free(device_extensions); ERR_FAIL_V_MSG(ERR_BUG, "Enabled extension count reaches MAX_EXTENSIONS, BUG"); @@ -532,9 +874,36 @@ Error VulkanContext::_create_physical_device() { ERR_FAIL_V(ERR_CANT_CREATE); break; } + } else if (enabled_debug_report) { + CreateDebugReportCallbackEXT = (PFN_vkCreateDebugReportCallbackEXT)vkGetInstanceProcAddr(inst, "vkCreateDebugReportCallbackEXT"); + DebugReportMessageEXT = (PFN_vkDebugReportMessageEXT)vkGetInstanceProcAddr(inst, "vkDebugReportMessageEXT"); + DestroyDebugReportCallbackEXT = (PFN_vkDestroyDebugReportCallbackEXT)vkGetInstanceProcAddr(inst, "vkDestroyDebugReportCallbackEXT"); + + if (nullptr == CreateDebugReportCallbackEXT || nullptr == DebugReportMessageEXT || nullptr == DestroyDebugReportCallbackEXT) { + ERR_FAIL_V_MSG(ERR_CANT_CREATE, + "GetProcAddr: Failed to init VK_EXT_debug_report\n" + "GetProcAddr: Failure"); + } + + err = CreateDebugReportCallbackEXT(inst, &dbg_report_callback_create_info, nullptr, &dbg_debug_report); + switch (err) { + case VK_SUCCESS: + break; + case VK_ERROR_OUT_OF_HOST_MEMORY: + ERR_FAIL_V_MSG(ERR_CANT_CREATE, + "CreateDebugReportCallbackEXT: out of host memory\n" + "CreateDebugReportCallbackEXT Failure"); + break; + default: + ERR_FAIL_V_MSG(ERR_CANT_CREATE, + "CreateDebugReportCallbackEXT: unknown failure\n" + "CreateDebugReportCallbackEXT Failure"); + ERR_FAIL_V(ERR_CANT_CREATE); + break; + } } - /* Call with NULL data to get count */ + /* Call with nullptr data to get count */ vkGetPhysicalDeviceQueueFamilyProperties(gpu, &queue_family_count, nullptr); ERR_FAIL_COND_V(queue_family_count == 0, ERR_CANT_CREATE); @@ -561,6 +930,14 @@ Error VulkanContext::_create_physical_device() { GET_INSTANCE_PROC_ADDR(inst, GetPhysicalDeviceSurfacePresentModesKHR); GET_INSTANCE_PROC_ADDR(inst, GetSwapchainImagesKHR); + // get info about what our vulkan driver is capable off + { + Error res = _check_capabilities(); + if (res != OK) { + return res; + } + } + return OK; } @@ -597,17 +974,50 @@ Error VulkanContext::_create_device() { queues[1].flags = 0; sdevice.queueCreateInfoCount = 2; } + +#ifdef VK_VERSION_1_2 + VkPhysicalDeviceVulkan11Features vulkan11features; + + vulkan11features.sType = VK_STRUCTURE_TYPE_PHYSICAL_DEVICE_VULKAN_1_1_FEATURES; + vulkan11features.pNext = nullptr; + // !BAS! Need to figure out which ones of these we want enabled... + vulkan11features.storageBuffer16BitAccess = 0; + vulkan11features.uniformAndStorageBuffer16BitAccess = 0; + vulkan11features.storagePushConstant16 = 0; + vulkan11features.storageInputOutput16 = 0; + vulkan11features.multiview = multiview_capabilities.is_supported; + vulkan11features.multiviewGeometryShader = multiview_capabilities.geometry_shader_is_supported; + vulkan11features.multiviewTessellationShader = multiview_capabilities.tessellation_shader_is_supported; + vulkan11features.variablePointersStorageBuffer = 0; + vulkan11features.variablePointers = 0; + vulkan11features.protectedMemory = 0; + vulkan11features.samplerYcbcrConversion = 0; + vulkan11features.shaderDrawParameters = 0; + + sdevice.pNext = &vulkan11features; +#elif VK_VERSION_1_1 + VkPhysicalDeviceMultiviewFeatures multiview_features; + + multiview_features.sType = VK_STRUCTURE_TYPE_PHYSICAL_DEVICE_MULTIVIEW_FEATURES; + multiview_features.pNext = nullptr; + multiview_features.multiview = multiview_capabilities.is_supported; + multiview_features.multiviewGeometryShader = multiview_capabilities.geometry_shader_is_supported; + multiview_features.multiviewTessellationShader = multiview_capabilities.tessellation_shader_is_supported; + + sdevice.pNext = &multiview_features; +#endif + err = vkCreateDevice(gpu, &sdevice, nullptr, &device); ERR_FAIL_COND_V(err, ERR_CANT_CREATE); return OK; } -Error VulkanContext::_initialize_queues(VkSurfaceKHR surface) { +Error VulkanContext::_initialize_queues(VkSurfaceKHR p_surface) { // Iterate over each queue to learn whether it supports presenting: VkBool32 *supportsPresent = (VkBool32 *)malloc(queue_family_count * sizeof(VkBool32)); for (uint32_t i = 0; i < queue_family_count; i++) { - fpGetPhysicalDeviceSurfaceSupportKHR(gpu, i, surface, &supportsPresent[i]); + fpGetPhysicalDeviceSurfaceSupportKHR(gpu, i, p_surface, &supportsPresent[i]); } // Search for a graphics and a present queue in the array of queue @@ -681,10 +1091,10 @@ Error VulkanContext::_initialize_queues(VkSurfaceKHR surface) { // Get the list of VkFormat's that are supported: uint32_t formatCount; - VkResult err = fpGetPhysicalDeviceSurfaceFormatsKHR(gpu, surface, &formatCount, nullptr); + VkResult err = fpGetPhysicalDeviceSurfaceFormatsKHR(gpu, p_surface, &formatCount, nullptr); ERR_FAIL_COND_V(err, ERR_CANT_CREATE); VkSurfaceFormatKHR *surfFormats = (VkSurfaceFormatKHR *)malloc(formatCount * sizeof(VkSurfaceFormatKHR)); - err = fpGetPhysicalDeviceSurfaceFormatsKHR(gpu, surface, &formatCount, surfFormats); + err = fpGetPhysicalDeviceSurfaceFormatsKHR(gpu, p_surface, &formatCount, surfFormats); if (err) { free(surfFormats); ERR_FAIL_V(ERR_CANT_CREATE); @@ -692,16 +1102,39 @@ Error VulkanContext::_initialize_queues(VkSurfaceKHR surface) { // If the format list includes just one entry of VK_FORMAT_UNDEFINED, // the surface has no preferred format. Otherwise, at least one // supported format will be returned. - if (true || (formatCount == 1 && surfFormats[0].format == VK_FORMAT_UNDEFINED)) { + if (formatCount == 1 && surfFormats[0].format == VK_FORMAT_UNDEFINED) { format = VK_FORMAT_B8G8R8A8_UNORM; + color_space = surfFormats[0].colorSpace; } else { + // These should be ordered with the ones we want to use on top and fallback modes further down + // we want an 32bit RGBA unsigned normalised buffer or similar + const VkFormat allowed_formats[] = { + VK_FORMAT_B8G8R8A8_UNORM, + VK_FORMAT_R8G8B8A8_UNORM + }; + uint32_t allowed_formats_count = sizeof(allowed_formats) / sizeof(VkFormat); + if (formatCount < 1) { free(surfFormats); ERR_FAIL_V_MSG(ERR_CANT_CREATE, "formatCount less than 1"); } - format = surfFormats[0].format; + + // Find the first format that we support + format = VK_FORMAT_UNDEFINED; + for (uint32_t af = 0; af < allowed_formats_count && format == VK_FORMAT_UNDEFINED; af++) { + for (uint32_t sf = 0; sf < formatCount && format == VK_FORMAT_UNDEFINED; sf++) { + if (surfFormats[sf].format == allowed_formats[af]) { + format = surfFormats[sf].format; + color_space = surfFormats[sf].colorSpace; + } + } + } + + if (format == VK_FORMAT_UNDEFINED) { + free(surfFormats); + ERR_FAIL_V_MSG(ERR_CANT_CREATE, "No usable surface format found."); + } } - color_space = surfFormats[0].colorSpace; free(surfFormats); @@ -736,9 +1169,6 @@ Error VulkanContext::_create_semaphores() { err = vkCreateFence(device, &fence_ci, nullptr, &fences[i]); ERR_FAIL_COND_V(err, ERR_CANT_CREATE); - err = vkCreateSemaphore(device, &semaphoreCreateInfo, nullptr, &image_acquired_semaphores[i]); - ERR_FAIL_COND_V(err, ERR_CANT_CREATE); - err = vkCreateSemaphore(device, &semaphoreCreateInfo, nullptr, &draw_complete_semaphores[i]); ERR_FAIL_COND_V(err, ERR_CANT_CREATE); @@ -755,7 +1185,11 @@ Error VulkanContext::_create_semaphores() { return OK; } -Error VulkanContext::_window_create(DisplayServer::WindowID p_window_id, VkSurfaceKHR p_surface, int p_width, int p_height) { +bool VulkanContext::_use_validation_layers() { + return Engine::get_singleton()->is_validation_layers_enabled(); +} + +Error VulkanContext::_window_create(DisplayServer::WindowID p_window_id, DisplayServer::VSyncMode p_vsync_mode, VkSurfaceKHR p_surface, int p_width, int p_height) { ERR_FAIL_COND_V(windows.has(p_window_id), ERR_INVALID_PARAMETER); if (!queues_initialized) { @@ -764,15 +1198,40 @@ Error VulkanContext::_window_create(DisplayServer::WindowID p_window_id, VkSurfa // is created. Error err = _initialize_queues(p_surface); ERR_FAIL_COND_V(err != OK, ERR_CANT_CREATE); + } else { + // make sure any of the surfaces supports present (validation layer complains if this is not done). + bool any_supports_present = false; + for (uint32_t i = 0; i < queue_family_count; i++) { + VkBool32 supports; + fpGetPhysicalDeviceSurfaceSupportKHR(gpu, i, p_surface, &supports); + if (supports) { + any_supports_present = true; + break; + } + } + + ERR_FAIL_COND_V_MSG(!any_supports_present, ERR_CANT_CREATE, "Surface passed for sub-window creation does not support presenting"); } Window window; window.surface = p_surface; window.width = p_width; window.height = p_height; + window.vsync_mode = p_vsync_mode; Error err = _update_swap_chain(&window); ERR_FAIL_COND_V(err != OK, ERR_CANT_CREATE); + VkSemaphoreCreateInfo semaphoreCreateInfo = { + /*sType*/ VK_STRUCTURE_TYPE_SEMAPHORE_CREATE_INFO, + /*pNext*/ nullptr, + /*flags*/ 0, + }; + + for (uint32_t i = 0; i < FRAME_LAG; i++) { + VkResult vkerr = vkCreateSemaphore(device, &semaphoreCreateInfo, nullptr, &window.image_acquired_semaphores[i]); + ERR_FAIL_COND_V(vkerr, ERR_CANT_CREATE); + } + windows[p_window_id] = window; return OK; } @@ -812,6 +1271,10 @@ VkFramebuffer VulkanContext::window_get_framebuffer(DisplayServer::WindowID p_wi void VulkanContext::window_destroy(DisplayServer::WindowID p_window_id) { ERR_FAIL_COND(!windows.has(p_window_id)); _clean_up_swap_chain(&windows[p_window_id]); + for (uint32_t i = 0; i < FRAME_LAG; i++) { + vkDestroySemaphore(device, windows[p_window_id].image_acquired_semaphores[i], nullptr); + } + vkDestroySurfaceKHR(inst, windows[p_window_id].surface, nullptr); windows.erase(p_window_id); } @@ -898,7 +1361,6 @@ Error VulkanContext::_update_swap_chain(Window *window) { } // The FIFO present mode is guaranteed by the spec to be supported // and to have no tearing. It's a great default present mode to use. - VkPresentModeKHR swapchainPresentMode = VK_PRESENT_MODE_FIFO_KHR; // There are times when you may wish to use another present mode. The // following code shows how to select them, and the comments provide some @@ -927,16 +1389,41 @@ Error VulkanContext::_update_swap_chain(Window *window) { // the application wants the late image to be immediately displayed, even // though that may mean some tearing. - if (window->presentMode != swapchainPresentMode) { - for (size_t i = 0; i < presentModeCount; ++i) { - if (presentModes[i] == window->presentMode) { - swapchainPresentMode = window->presentMode; - break; - } + VkPresentModeKHR requested_present_mode = VkPresentModeKHR::VK_PRESENT_MODE_FIFO_KHR; + switch (window->vsync_mode) { + case DisplayServer::VSYNC_MAILBOX: + requested_present_mode = VkPresentModeKHR::VK_PRESENT_MODE_MAILBOX_KHR; + break; + case DisplayServer::VSYNC_ADAPTIVE: + requested_present_mode = VkPresentModeKHR::VK_PRESENT_MODE_FIFO_RELAXED_KHR; + break; + case DisplayServer::VSYNC_ENABLED: + requested_present_mode = VkPresentModeKHR::VK_PRESENT_MODE_FIFO_KHR; + break; + case DisplayServer::VSYNC_DISABLED: + requested_present_mode = VkPresentModeKHR::VK_PRESENT_MODE_IMMEDIATE_KHR; + break; + } + + // Check if the requested mode is available. + bool present_mode_available = false; + for (uint32_t i = 0; i < presentModeCount; i++) { + if (presentModes[i] == requested_present_mode) { + present_mode_available = true; } } + + // Set the windows present mode if it is available, otherwise FIFO is used (guaranteed supported). + if (present_mode_available) { + window->presentMode = requested_present_mode; + } else { + WARN_PRINT("Requested VSync mode is not available!"); + window->vsync_mode = DisplayServer::VSYNC_ENABLED; //Set to default + } + + print_verbose("Using present mode: " + String(string_VkPresentModeKHR(window->presentMode))); + free(presentModes); - ERR_FAIL_COND_V_MSG(swapchainPresentMode != window->presentMode, ERR_CANT_CREATE, "Present mode specified is not supported\n"); // Determine the number of VkImages to use in the swap chain. // Application desires to acquire 3 images at a time for triple @@ -993,7 +1480,7 @@ Error VulkanContext::_update_swap_chain(Window *window) { /*pQueueFamilyIndices*/ nullptr, /*preTransform*/ (VkSurfaceTransformFlagBitsKHR)preTransform, /*compositeAlpha*/ compositeAlpha, - /*presentMode*/ swapchainPresentMode, + /*presentMode*/ window->presentMode, /*clipped*/ true, /*oldSwapchain*/ VK_NULL_HANDLE, }; @@ -1266,6 +1753,8 @@ Error VulkanContext::prepare_buffers() { for (Map<int, Window>::Element *E = windows.front(); E; E = E->next()) { Window *w = &E->get(); + w->semaphore_acquired = false; + if (w->swapchain == VK_NULL_HANDLE) { continue; } @@ -1274,21 +1763,23 @@ Error VulkanContext::prepare_buffers() { // Get the index of the next available swapchain image: err = fpAcquireNextImageKHR(device, w->swapchain, UINT64_MAX, - image_acquired_semaphores[frame_index], VK_NULL_HANDLE, &w->current_buffer); + w->image_acquired_semaphores[frame_index], VK_NULL_HANDLE, &w->current_buffer); if (err == VK_ERROR_OUT_OF_DATE_KHR) { // swapchain is out of date (e.g. the window was resized) and // must be recreated: - print_line("early out of data"); + print_verbose("Vulkan: Early out of date swapchain, recreating."); //resize_notify(); _update_swap_chain(w); } else if (err == VK_SUBOPTIMAL_KHR) { - print_line("early suboptimal"); // swapchain is not as optimal as it could be, but the platform's // presentation engine will still present the image correctly. + print_verbose("Vulkan: Early suboptimal swapchain."); break; + } else if (err != VK_SUCCESS) { + ERR_BREAK_MSG(err != VK_SUCCESS, "Vulkan: Did not create swapchain successfully."); } else { - ERR_FAIL_COND_V(err, ERR_CANT_CREATE); + w->semaphore_acquired = true; } } while (err != VK_SUCCESS); } @@ -1313,13 +1804,13 @@ Error VulkanContext::swap_buffers() { DemoUpdateTargetIPD(demo); // Note: a real application would position its geometry to that it's in - // the correct locatoin for when the next image is presented. It might + // the correct location for when the next image is presented. It might // also wait, so that there's less latency between any input and when // the next image is rendered/presented. This demo program is so // simple that it doesn't do either of those. } #endif - // Wait for the image acquired semaphore to be signaled to ensure + // Wait for the image acquired semaphore to be signalled to ensure // that the image won't be rendered to until the presentation // engine has fully released ownership to the application, and it is // okay to render to the image. @@ -1338,14 +1829,25 @@ Error VulkanContext::swap_buffers() { commands_to_submit = command_buffer_count; } + VkSemaphore *semaphores_to_acquire = (VkSemaphore *)alloca(windows.size() * sizeof(VkSemaphore)); + uint32_t semaphores_to_acquire_count = 0; + + for (Map<int, Window>::Element *E = windows.front(); E; E = E->next()) { + Window *w = &E->get(); + + if (w->semaphore_acquired) { + semaphores_to_acquire[semaphores_to_acquire_count++] = w->image_acquired_semaphores[frame_index]; + } + } + VkPipelineStageFlags pipe_stage_flags; VkSubmitInfo submit_info; submit_info.sType = VK_STRUCTURE_TYPE_SUBMIT_INFO; submit_info.pNext = nullptr; submit_info.pWaitDstStageMask = &pipe_stage_flags; pipe_stage_flags = VK_PIPELINE_STAGE_COLOR_ATTACHMENT_OUTPUT_BIT; - submit_info.waitSemaphoreCount = 1; - submit_info.pWaitSemaphores = &image_acquired_semaphores[frame_index]; + submit_info.waitSemaphoreCount = semaphores_to_acquire_count; + submit_info.pWaitSemaphores = semaphores_to_acquire; submit_info.commandBufferCount = commands_to_submit; submit_info.pCommandBuffers = commands_ptr; submit_info.signalSemaphoreCount = 1; @@ -1385,7 +1887,7 @@ Error VulkanContext::swap_buffers() { ERR_FAIL_COND_V(err, ERR_CANT_CREATE); } - // If we are using separate queues we have to wait for image ownership, + // If we are using separate queues, we have to wait for image ownership, // otherwise wait for draw complete VkPresentInfoKHR present = { /*sType*/ VK_STRUCTURE_TYPE_PRESENT_INFO_KHR, @@ -1493,12 +1995,12 @@ Error VulkanContext::swap_buffers() { if (err == VK_ERROR_OUT_OF_DATE_KHR) { // swapchain is out of date (e.g. the window was resized) and // must be recreated: - print_line("out of date"); + print_verbose("Vulkan: Swapchain is out of date, recreating."); resize_notify(); } else if (err == VK_SUBOPTIMAL_KHR) { // swapchain is not as optimal as it could be, but the platform's // presentation engine will still present the image correctly. - print_line("suboptimal"); + print_verbose("Vulkan: Swapchain is suboptimal."); } else { ERR_FAIL_COND_V(err, ERR_CANT_CREATE); } @@ -1594,13 +2096,13 @@ void VulkanContext::local_device_push_command_buffers(RID p_local_device, const VkResult err = vkQueueSubmit(ld->queue, 1, &submit_info, VK_NULL_HANDLE); if (err == VK_ERROR_OUT_OF_HOST_MEMORY) { - print_line("out of host memory"); + print_line("Vulkan: Out of host memory!"); } if (err == VK_ERROR_OUT_OF_DEVICE_MEMORY) { - print_line("out of device memory"); + print_line("Vulkan: Out of device memory!"); } if (err == VK_ERROR_DEVICE_LOST) { - print_line("device lost"); + print_line("Vulkan: Device lost!"); } ERR_FAIL_COND(err); @@ -1685,9 +2187,18 @@ String VulkanContext::get_device_pipeline_cache_uuid() const { return pipeline_cache_id; } -VulkanContext::VulkanContext() { - use_validation_layers = Engine::get_singleton()->is_validation_layers_enabled(); +DisplayServer::VSyncMode VulkanContext::get_vsync_mode(DisplayServer::WindowID p_window) const { + ERR_FAIL_COND_V_MSG(!windows.has(p_window), DisplayServer::VSYNC_ENABLED, "Could not get VSync mode for window with WindowID " + itos(p_window) + " because it does not exist."); + return windows[p_window].vsync_mode; +} + +void VulkanContext::set_vsync_mode(DisplayServer::WindowID p_window, DisplayServer::VSyncMode p_mode) { + ERR_FAIL_COND_MSG(!windows.has(p_window), "Could not set VSync mode for window with WindowID " + itos(p_window) + " because it does not exist."); + windows[p_window].vsync_mode = p_mode; + _update_swap_chain(&windows[p_window]); +} +VulkanContext::VulkanContext() { command_buffer_queue.resize(1); // First one is always the setup command. command_buffer_queue.write[0] = nullptr; } @@ -1699,15 +2210,17 @@ VulkanContext::~VulkanContext() { if (device_initialized) { for (uint32_t i = 0; i < FRAME_LAG; i++) { vkDestroyFence(device, fences[i], nullptr); - vkDestroySemaphore(device, image_acquired_semaphores[i], nullptr); vkDestroySemaphore(device, draw_complete_semaphores[i], nullptr); if (separate_present_queue) { vkDestroySemaphore(device, image_ownership_semaphores[i], nullptr); } } - if (inst_initialized && use_validation_layers) { + if (inst_initialized && enabled_debug_utils) { DestroyDebugUtilsMessengerEXT(inst, dbg_messenger, nullptr); } + if (inst_initialized && dbg_debug_report != VK_NULL_HANDLE) { + DestroyDebugReportCallbackEXT(inst, dbg_debug_report, nullptr); + } vkDestroyDevice(device, nullptr); } if (inst_initialized) { diff --git a/drivers/vulkan/vulkan_context.h b/drivers/vulkan/vulkan_context.h index dc6b0410bc..83e9524046 100644 --- a/drivers/vulkan/vulkan_context.h +++ b/drivers/vulkan/vulkan_context.h @@ -41,6 +41,28 @@ #include <vulkan/vulkan.h> class VulkanContext { +public: + struct SubgroupCapabilities { + uint32_t size; + VkShaderStageFlags supportedStages; + VkSubgroupFeatureFlags supportedOperations; + VkBool32 quadOperationsInAllStages; + + uint32_t supported_stages_flags_rd() const; + String supported_stages_desc() const; + uint32_t supported_operations_flags_rd() const; + String supported_operations_desc() const; + }; + + struct MultiviewCapabilities { + bool is_supported; + bool geometry_shader_is_supported; + bool tessellation_shader_is_supported; + uint32_t max_view_count; + uint32_t max_instance_count; + }; + +private: enum { MAX_EXTENSIONS = 128, MAX_LAYERS = 64, @@ -48,7 +70,6 @@ class VulkanContext { }; VkInstance inst = VK_NULL_HANDLE; - VkSurfaceKHR surface = VK_NULL_HANDLE; VkPhysicalDevice gpu = VK_NULL_HANDLE; VkPhysicalDeviceProperties gpu_props; uint32_t queue_family_count = 0; @@ -57,6 +78,12 @@ class VulkanContext { bool device_initialized = false; bool inst_initialized = false; + // Vulkan 1.0 doesn't return version info so we assume this by default until we know otherwise + uint32_t vulkan_major = 1; + uint32_t vulkan_minor = 0; + SubgroupCapabilities subgroup_capabilities; + MultiviewCapabilities multiview_capabilities; + String device_vendor; String device_name; String pipeline_cache_id; @@ -73,7 +100,6 @@ class VulkanContext { VkQueue present_queue = VK_NULL_HANDLE; VkColorSpaceKHR color_space; VkFormat format; - VkSemaphore image_acquired_semaphores[FRAME_LAG]; VkSemaphore draw_complete_semaphores[FRAME_LAG]; VkSemaphore image_ownership_semaphores[FRAME_LAG]; int frame_index = 0; @@ -93,9 +119,12 @@ class VulkanContext { VkSwapchainKHR swapchain = VK_NULL_HANDLE; SwapchainImageResources *swapchain_image_resources = VK_NULL_HANDLE; VkPresentModeKHR presentMode = VK_PRESENT_MODE_FIFO_KHR; + VkSemaphore image_acquired_semaphores[FRAME_LAG]; + bool semaphore_acquired = false; uint32_t current_buffer = 0; int width = 0; int height = 0; + DisplayServer::VSyncMode vsync_mode = DisplayServer::VSYNC_ENABLED; VkCommandPool present_cmd_pool = VK_NULL_HANDLE; // For separate present queue. VkRenderPass render_pass = VK_NULL_HANDLE; }; @@ -126,8 +155,11 @@ class VulkanContext { const char *extension_names[MAX_EXTENSIONS]; bool enabled_debug_utils = false; - uint32_t enabled_layer_count = 0; - const char *enabled_layers[MAX_LAYERS]; + /** + * True if VK_EXT_debug_report extension is used. VK_EXT_debug_report is deprecated but it is + * still used if VK_EXT_debug_utils is not available. + */ + bool enabled_debug_report = false; PFN_vkCreateDebugUtilsMessengerEXT CreateDebugUtilsMessengerEXT; PFN_vkDestroyDebugUtilsMessengerEXT DestroyDebugUtilsMessengerEXT; @@ -136,6 +168,9 @@ class VulkanContext { PFN_vkCmdEndDebugUtilsLabelEXT CmdEndDebugUtilsLabelEXT; PFN_vkCmdInsertDebugUtilsLabelEXT CmdInsertDebugUtilsLabelEXT; PFN_vkSetDebugUtilsObjectNameEXT SetDebugUtilsObjectNameEXT; + PFN_vkCreateDebugReportCallbackEXT CreateDebugReportCallbackEXT; + PFN_vkDebugReportMessageEXT DebugReportMessageEXT; + PFN_vkDestroyDebugReportCallbackEXT DestroyDebugReportCallbackEXT; PFN_vkGetPhysicalDeviceSurfaceSupportKHR fpGetPhysicalDeviceSurfaceSupportKHR; PFN_vkGetPhysicalDeviceSurfaceCapabilitiesKHR fpGetPhysicalDeviceSurfaceCapabilitiesKHR; PFN_vkGetPhysicalDeviceSurfaceFormatsKHR fpGetPhysicalDeviceSurfaceFormatsKHR; @@ -149,20 +184,32 @@ class VulkanContext { PFN_vkGetPastPresentationTimingGOOGLE fpGetPastPresentationTimingGOOGLE; VkDebugUtilsMessengerEXT dbg_messenger = VK_NULL_HANDLE; + VkDebugReportCallbackEXT dbg_debug_report = VK_NULL_HANDLE; - Error _create_validation_layers(); + Error _obtain_vulkan_version(); Error _initialize_extensions(); + Error _check_capabilities(); - VkBool32 _check_layers(uint32_t check_count, const char **check_names, uint32_t layer_count, VkLayerProperties *layers); + VkBool32 _check_layers(uint32_t check_count, const char *const *check_names, uint32_t layer_count, VkLayerProperties *layers); static VKAPI_ATTR VkBool32 VKAPI_CALL _debug_messenger_callback( VkDebugUtilsMessageSeverityFlagBitsEXT messageSeverity, VkDebugUtilsMessageTypeFlagsEXT messageType, const VkDebugUtilsMessengerCallbackDataEXT *pCallbackData, void *pUserData); + static VKAPI_ATTR VkBool32 VKAPI_CALL _debug_report_callback( + VkDebugReportFlagsEXT flags, + VkDebugReportObjectTypeEXT objectType, + uint64_t object, + size_t location, + int32_t messageCode, + const char *pLayerPrefix, + const char *pMessage, + void *pUserData); + Error _create_physical_device(); - Error _initialize_queues(VkSurfaceKHR surface); + Error _initialize_queues(VkSurfaceKHR p_surface); Error _create_device(); @@ -176,16 +223,22 @@ class VulkanContext { protected: virtual const char *_get_platform_surface_extension() const = 0; - // Enabled via command line argument. - bool use_validation_layers = false; + virtual Error _window_create(DisplayServer::WindowID p_window_id, DisplayServer::VSyncMode p_vsync_mode, VkSurfaceKHR p_surface, int p_width, int p_height); + + virtual bool _use_validation_layers(); - virtual Error _window_create(DisplayServer::WindowID p_window_id, VkSurfaceKHR p_surface, int p_width, int p_height); + Error _get_preferred_validation_layers(uint32_t *count, const char *const **names); VkInstance _get_instance() { return inst; } public: + uint32_t get_vulkan_major() const { return vulkan_major; }; + uint32_t get_vulkan_minor() const { return vulkan_minor; }; + SubgroupCapabilities get_subgroup_capabilities() const { return subgroup_capabilities; }; + MultiviewCapabilities get_multiview_capabilities() const { return multiview_capabilities; }; + VkDevice get_device(); VkPhysicalDevice get_physical_device(); int get_swapchain_image_count() const; @@ -224,6 +277,9 @@ public: String get_device_name() const; String get_device_pipeline_cache_uuid() const; + void set_vsync_mode(DisplayServer::WindowID p_window, DisplayServer::VSyncMode p_mode); + DisplayServer::VSyncMode get_vsync_mode(DisplayServer::WindowID p_window = 0) const; + VulkanContext(); virtual ~VulkanContext(); }; |