diff options
Diffstat (limited to 'drivers/vulkan')
-rw-r--r-- | drivers/vulkan/SCsub | 8 | ||||
-rw-r--r-- | drivers/vulkan/rendering_device_vulkan.cpp | 1629 | ||||
-rw-r--r-- | drivers/vulkan/rendering_device_vulkan.h | 164 | ||||
-rw-r--r-- | drivers/vulkan/vulkan_context.cpp | 373 | ||||
-rw-r--r-- | drivers/vulkan/vulkan_context.h | 82 | ||||
-rw-r--r-- | drivers/vulkan/vulkan_hooks.h | 2 |
6 files changed, 1342 insertions, 916 deletions
diff --git a/drivers/vulkan/SCsub b/drivers/vulkan/SCsub index b6ceb1cdea..a076c0ac54 100644 --- a/drivers/vulkan/SCsub +++ b/drivers/vulkan/SCsub @@ -15,11 +15,11 @@ if env["use_volk"]: if env["platform"] == "android": env.AppendUnique(CPPDEFINES=["VK_USE_PLATFORM_ANDROID_KHR"]) -elif env["platform"] == "iphone": +elif env["platform"] == "ios": env.AppendUnique(CPPDEFINES=["VK_USE_PLATFORM_IOS_MVK"]) -elif env["platform"] == "linuxbsd": +elif env["platform"] == "linuxbsd" and env["x11"]: env.AppendUnique(CPPDEFINES=["VK_USE_PLATFORM_XLIB_KHR"]) -elif env["platform"] == "osx": +elif env["platform"] == "macos": env.AppendUnique(CPPDEFINES=["VK_USE_PLATFORM_MACOS_MVK"]) elif env["platform"] == "windows": env.AppendUnique(CPPDEFINES=["VK_USE_PLATFORM_WIN32_KHR"]) @@ -40,7 +40,7 @@ elif env["platform"] == "android": # Our current NDK version only provides old Vulkan headers, # so we have to limit VMA. env_thirdparty_vma.AppendUnique(CPPDEFINES=["VMA_VULKAN_VERSION=1000000"]) -elif env["platform"] == "osx" or env["platform"] == "iphone": +elif env["platform"] == "macos" or env["platform"] == "ios": # MoltenVK supports only Vulkan 1.1 API, limit VMA to the same version. env_thirdparty_vma.AppendUnique(CPPDEFINES=["VMA_VULKAN_VERSION=1001000"]) diff --git a/drivers/vulkan/rendering_device_vulkan.cpp b/drivers/vulkan/rendering_device_vulkan.cpp index 59b4c34c3f..2debba1b83 100644 --- a/drivers/vulkan/rendering_device_vulkan.cpp +++ b/drivers/vulkan/rendering_device_vulkan.cpp @@ -36,6 +36,7 @@ #include "core/io/marshalls.h" #include "core/os/os.h" #include "core/templates/hashfuncs.h" +#include "core/version.h" #include "drivers/vulkan/vulkan_context.h" #include "thirdparty/misc/smolv.h" @@ -45,7 +46,7 @@ static const uint32_t SMALL_ALLOCATION_MAX_SIZE = 4096; -// Get the Vulkan object information and possible stage access types (bitwise OR'd with incoming values) +// Get the Vulkan object information and possible stage access types (bitwise OR'd with incoming values). RenderingDeviceVulkan::Buffer *RenderingDeviceVulkan::_get_buffer_from_owner(RID p_buffer, VkPipelineStageFlags &r_stage_mask, VkAccessFlags &r_access_mask, uint32_t p_post_barrier) { Buffer *buffer = nullptr; if (vertex_buffer_owner.owns(p_buffer)) { @@ -106,9 +107,9 @@ RenderingDeviceVulkan::Buffer *RenderingDeviceVulkan::_get_buffer_from_owner(RID return buffer; } -static void update_external_dependency_for_store(VkSubpassDependency &dependency, bool is_sampled, bool is_storage, bool is_depth) { - // Transitioning from write to read, protect the shaders that may use this next - // Allow for copies/image layout transitions +static void update_external_dependency_for_store(VkSubpassDependency2KHR &dependency, bool is_sampled, bool is_storage, bool is_depth) { + // Transitioning from write to read, protect the shaders that may use this next. + // Allow for copies/image layout transitions. dependency.dstStageMask |= VK_PIPELINE_STAGE_TRANSFER_BIT; dependency.dstAccessMask |= VK_ACCESS_TRANSFER_READ_BIT; @@ -124,7 +125,7 @@ static void update_external_dependency_for_store(VkSubpassDependency &dependency } if (is_depth) { - // Depth resources have additional 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; } @@ -132,41 +133,41 @@ static void update_external_dependency_for_store(VkSubpassDependency &dependency void RenderingDeviceVulkan::_add_dependency(RID p_id, RID p_depends_on) { if (!dependency_map.has(p_depends_on)) { - dependency_map[p_depends_on] = Set<RID>(); + dependency_map[p_depends_on] = HashSet<RID>(); } dependency_map[p_depends_on].insert(p_id); if (!reverse_dependency_map.has(p_id)) { - reverse_dependency_map[p_id] = Set<RID>(); + reverse_dependency_map[p_id] = HashSet<RID>(); } reverse_dependency_map[p_id].insert(p_depends_on); } void RenderingDeviceVulkan::_free_dependencies(RID p_id) { - //direct dependencies must be freed + // Direct dependencies must be freed. - Map<RID, Set<RID>>::Element *E = dependency_map.find(p_id); + HashMap<RID, HashSet<RID>>::Iterator E = dependency_map.find(p_id); if (E) { - while (E->get().size()) { - free(E->get().front()->get()); + while (E->value.size()) { + free(*E->value.begin()); } - dependency_map.erase(E); + dependency_map.remove(E); } - //reverse dependencies must be unreferenced + // Reverse dependencies must be unreferenced. E = reverse_dependency_map.find(p_id); if (E) { - for (Set<RID>::Element *F = E->get().front(); F; F = F->next()) { - Map<RID, Set<RID>>::Element *G = dependency_map.find(F->get()); + for (const RID &F : E->value) { + HashMap<RID, HashSet<RID>>::Iterator G = dependency_map.find(F); ERR_CONTINUE(!G); - ERR_CONTINUE(!G->get().has(p_id)); - G->get().erase(p_id); + ERR_CONTINUE(!G->value.has(p_id)); + G->value.erase(p_id); } - reverse_dependency_map.erase(E); + reverse_dependency_map.remove(E); } } @@ -859,7 +860,7 @@ uint32_t RenderingDeviceVulkan::get_image_format_pixel_size(DataFormat p_format) case DATA_FORMAT_D24_UNORM_S8_UINT: return 4; case DATA_FORMAT_D32_SFLOAT_S8_UINT: - return 5; //? + return 5; // ? case DATA_FORMAT_BC1_RGB_UNORM_BLOCK: case DATA_FORMAT_BC1_RGB_SRGB_BLOCK: case DATA_FORMAT_BC1_RGBA_UNORM_BLOCK: @@ -994,7 +995,7 @@ void RenderingDeviceVulkan::get_compressed_image_format_block_dimensions(DataFor case DATA_FORMAT_EAC_R11_SNORM_BLOCK: case DATA_FORMAT_EAC_R11G11_UNORM_BLOCK: case DATA_FORMAT_EAC_R11G11_SNORM_BLOCK: - case DATA_FORMAT_ASTC_4x4_UNORM_BLOCK: //again, not sure about astc + case DATA_FORMAT_ASTC_4x4_UNORM_BLOCK: // Again, not sure about astc. case DATA_FORMAT_ASTC_4x4_SRGB_BLOCK: case DATA_FORMAT_ASTC_5x4_UNORM_BLOCK: case DATA_FORMAT_ASTC_5x4_SRGB_BLOCK: @@ -1072,7 +1073,7 @@ uint32_t RenderingDeviceVulkan::get_compressed_image_format_block_byte_size(Data case DATA_FORMAT_EAC_R11G11_UNORM_BLOCK: case DATA_FORMAT_EAC_R11G11_SNORM_BLOCK: return 16; - case DATA_FORMAT_ASTC_4x4_UNORM_BLOCK: //again, not sure about astc + case DATA_FORMAT_ASTC_4x4_UNORM_BLOCK: // Again, not sure about astc. case DATA_FORMAT_ASTC_4x4_SRGB_BLOCK: case DATA_FORMAT_ASTC_5x4_UNORM_BLOCK: case DATA_FORMAT_ASTC_5x4_SRGB_BLOCK: @@ -1100,7 +1101,7 @@ uint32_t RenderingDeviceVulkan::get_compressed_image_format_block_byte_size(Data case DATA_FORMAT_ASTC_12x10_SRGB_BLOCK: case DATA_FORMAT_ASTC_12x12_UNORM_BLOCK: case DATA_FORMAT_ASTC_12x12_SRGB_BLOCK: - return 8; //wrong + return 8; // Wrong. default: { } } @@ -1109,7 +1110,7 @@ uint32_t RenderingDeviceVulkan::get_compressed_image_format_block_byte_size(Data uint32_t RenderingDeviceVulkan::get_compressed_image_format_pixel_rshift(DataFormat p_format) { switch (p_format) { - case DATA_FORMAT_BC1_RGB_UNORM_BLOCK: //these formats are half byte size, so rshift is 1 + case DATA_FORMAT_BC1_RGB_UNORM_BLOCK: // These formats are half byte size, so rshift is 1. case DATA_FORMAT_BC1_RGB_SRGB_BLOCK: case DATA_FORMAT_BC1_RGBA_UNORM_BLOCK: case DATA_FORMAT_BC1_RGBA_SRGB_BLOCK: @@ -1183,7 +1184,7 @@ uint32_t RenderingDeviceVulkan::get_image_format_required_size(DataFormat p_form } uint32_t RenderingDeviceVulkan::get_image_required_mipmaps(uint32_t p_width, uint32_t p_height, uint32_t p_depth) { - //formats and block size don't really matter here since they can all go down to 1px (even if block is larger) + // Formats and block size don't really matter here since they can all go down to 1px (even if block is larger). uint32_t w = p_width; uint32_t h = p_height; uint32_t d = p_depth; @@ -1400,17 +1401,17 @@ Error RenderingDeviceVulkan::_insert_staging_block() { return OK; } -Error RenderingDeviceVulkan::_staging_buffer_allocate(uint32_t p_amount, uint32_t p_required_align, uint32_t &r_alloc_offset, uint32_t &r_alloc_size, bool p_can_segment, bool p_on_draw_command_buffer) { - //determine a block to use +Error RenderingDeviceVulkan::_staging_buffer_allocate(uint32_t p_amount, uint32_t p_required_align, uint32_t &r_alloc_offset, uint32_t &r_alloc_size, bool p_can_segment) { + // Determine a block to use. r_alloc_size = p_amount; while (true) { r_alloc_offset = 0; - //see if we can use current block + // See if we can use current block. if (staging_buffer_blocks[staging_buffer_current].frame_used == frames_drawn) { - //we used this block this frame, let's see if there is still room + // We used this block this frame, let's see if there is still room. uint32_t write_from = staging_buffer_blocks[staging_buffer_current].fill_amount; @@ -1424,107 +1425,107 @@ Error RenderingDeviceVulkan::_staging_buffer_allocate(uint32_t p_amount, uint32_ int32_t available_bytes = int32_t(staging_buffer_block_size) - int32_t(write_from); if ((int32_t)p_amount < available_bytes) { - //all is good, we should be ok, all will fit + // All is good, we should be ok, all will fit. r_alloc_offset = write_from; } else if (p_can_segment && available_bytes >= (int32_t)p_required_align) { - //ok all won't fit but at least we can fit a chunkie - //all is good, update what needs to be written to + // Ok all won't fit but at least we can fit a chunkie. + // All is good, update what needs to be written to. r_alloc_offset = write_from; r_alloc_size = available_bytes - (available_bytes % p_required_align); } else { - //can't fit it into this buffer. - //will need to try next buffer + // Can't fit it into this buffer. + // Will need to try next buffer. staging_buffer_current = (staging_buffer_current + 1) % staging_buffer_blocks.size(); - // before doing anything, though, let's check that we didn't manage to fill all blocks - // possible in a single frame + // Before doing anything, though, let's check that we didn't manage to fill all blocks. + // 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.. + // Guess we did.. ok, let's see if we can insert a new block. if ((uint64_t)staging_buffer_blocks.size() * staging_buffer_block_size < staging_buffer_max_size) { - //we can, so we are safe + // We can, so we are safe. Error err = _insert_staging_block(); if (err) { return err; } - //claim for this frame + // Claim for this frame. staging_buffer_blocks.write[staging_buffer_current].frame_used = frames_drawn; } else { // Ok, worst case scenario, all the staging buffers belong to this frame // and this frame is not even done. - // If this is the main thread, it means the user is likely loading a lot of resources at once, - // otherwise, the thread should just be blocked until the next frame (currently unimplemented) + // If this is the main thread, it means the user is likely loading a lot of resources at once,. + // Otherwise, the thread should just be blocked until the next frame (currently unimplemented). - if (false) { //separate thread from render + if (false) { // Separate thread from render. //block_until_next_frame() continue; } else { - //flush EVERYTHING including setup commands. IF not immediate, also need to flush the draw commands + // Flush EVERYTHING including setup commands. IF not immediate, also need to flush the draw commands. _flush(true); - //clear the whole staging buffer + // Clear the whole staging buffer. for (int i = 0; i < staging_buffer_blocks.size(); i++) { staging_buffer_blocks.write[i].frame_used = 0; staging_buffer_blocks.write[i].fill_amount = 0; } - //claim current + // Claim current. staging_buffer_blocks.write[staging_buffer_current].frame_used = frames_drawn; } } } else { - //not from current frame, so continue and try again + // Not from current frame, so continue and try again. continue; } } } else if (staging_buffer_blocks[staging_buffer_current].frame_used <= frames_drawn - frame_count) { - //this is an old block, which was already processed, let's reuse + // This is an old block, which was already processed, let's reuse. staging_buffer_blocks.write[staging_buffer_current].frame_used = frames_drawn; staging_buffer_blocks.write[staging_buffer_current].fill_amount = 0; } else { - //this block may still be in use, let's not touch it unless we have to, so.. can we create a new one? + // This block may still be in use, let's not touch it unless we have to, so.. can we create a new one? 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 + // 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) { return err; } - //claim for this frame + // Claim for this frame. staging_buffer_blocks.write[staging_buffer_current].frame_used = frames_drawn; } else { - // oops, we are out of room and we can't create more. - // let's flush older frames. + // Oops, we are out of room and we can't create more. + // Let's flush older frames. // The logic here is that if a game is loading a lot of data from the main thread, it will need to be stalled anyway. // If loading from a separate thread, we can block that thread until next frame when more room is made (not currently implemented, though). if (false) { - //separate thread from render + // Separate thread from render. //block_until_next_frame() - continue; //and try again + continue; // And try again. } else { _flush(false); for (int i = 0; i < staging_buffer_blocks.size(); i++) { - //clear all blocks but the ones from this frame + // Clear all blocks but the ones from this frame. int block_idx = (i + staging_buffer_current) % staging_buffer_blocks.size(); if (staging_buffer_blocks[block_idx].frame_used == frames_drawn) { - break; //ok, we reached something from this frame, abort + break; // Ok, we reached something from this frame, abort. } staging_buffer_blocks.write[block_idx].frame_used = 0; staging_buffer_blocks.write[block_idx].fill_amount = 0; } - //claim for current frame + // Claim for current frame. staging_buffer_blocks.write[staging_buffer_current].frame_used = frames_drawn; } } } - //all was good, break + // All was good, break. break; } @@ -1534,7 +1535,7 @@ Error RenderingDeviceVulkan::_staging_buffer_allocate(uint32_t p_amount, uint32_ } Error RenderingDeviceVulkan::_buffer_update(Buffer *p_buffer, size_t p_offset, const uint8_t *p_data, size_t p_data_size, bool p_use_draw_command_buffer, uint32_t p_required_align) { - //submitting may get chunked for various reasons, so convert this to a task + // Submitting may get chunked for various reasons, so convert this to a task. size_t to_submit = p_data_size; size_t submit_from = 0; @@ -1542,12 +1543,12 @@ Error RenderingDeviceVulkan::_buffer_update(Buffer *p_buffer, size_t p_offset, c uint32_t block_write_offset; uint32_t block_write_amount; - Error err = _staging_buffer_allocate(MIN(to_submit, staging_buffer_block_size), p_required_align, block_write_offset, block_write_amount, p_use_draw_command_buffer); + Error err = _staging_buffer_allocate(MIN(to_submit, staging_buffer_block_size), p_required_align, block_write_offset, block_write_amount); if (err) { return err; } - //map staging buffer (It's CPU and coherent) + // Map staging buffer (It's CPU and coherent). void *data_ptr = nullptr; { @@ -1555,12 +1556,12 @@ Error RenderingDeviceVulkan::_buffer_update(Buffer *p_buffer, size_t p_offset, c ERR_FAIL_COND_V_MSG(vkerr, ERR_CANT_CREATE, "vmaMapMemory failed with error " + itos(vkerr) + "."); } - //copy to staging buffer + // Copy to staging buffer. memcpy(((uint8_t *)data_ptr) + block_write_offset, p_data + submit_from, block_write_amount); - //unmap + // Unmap. vmaUnmapMemory(allocator, staging_buffer_blocks[staging_buffer_current].allocation); - //insert a command to copy this + // Insert a command to copy this. VkBufferCopy region; region.srcOffset = block_write_offset; @@ -1586,13 +1587,13 @@ void RenderingDeviceVulkan::_memory_barrier(VkPipelineStageFlags p_src_stage_mas mem_barrier.dstAccessMask = p_dst_sccess; if (p_src_stage_mask == 0 || p_dst_stage_mask == 0) { - return; //no barrier, since this is invalid + return; // No barrier, since this is invalid. } vkCmdPipelineBarrier(p_sync_with_draw ? frames[frame].draw_command_buffer : frames[frame].setup_command_buffer, p_src_stage_mask, p_dst_stage_mask, 0, 1, &mem_barrier, 0, nullptr, 0, nullptr); } void RenderingDeviceVulkan::_full_barrier(bool p_sync_with_draw) { - //used for debug + // Used for debug. _memory_barrier(VK_PIPELINE_STAGE_ALL_COMMANDS_BIT, VK_PIPELINE_STAGE_ALL_COMMANDS_BIT, VK_ACCESS_INDIRECT_COMMAND_READ_BIT | VK_ACCESS_INDEX_READ_BIT | @@ -1661,8 +1662,8 @@ RID RenderingDeviceVulkan::texture_create(const TextureFormat &p_format, const T #ifndef ANDROID_ENABLED // vkCreateImage fails with format list on Android (VK_ERROR_OUT_OF_HOST_MEMORY) - VkImageFormatListCreateInfoKHR format_list_create_info; //keep out of the if, needed for creation - Vector<VkFormat> allowed_formats; //keep out of the if, needed for creation + VkImageFormatListCreateInfoKHR format_list_create_info; // Keep out of the if, needed for creation. + Vector<VkFormat> allowed_formats; // Keep out of the if, needed for creation. #endif if (p_format.shareable_formats.size()) { image_create_info.flags |= VK_IMAGE_CREATE_MUTABLE_FORMAT_BIT; @@ -1735,7 +1736,7 @@ RID RenderingDeviceVulkan::texture_create(const TextureFormat &p_format, const T image_create_info.samples = rasterization_sample_count[p_format.samples]; image_create_info.tiling = (p_format.usage_bits & TEXTURE_USAGE_CPU_READ_BIT) ? VK_IMAGE_TILING_LINEAR : VK_IMAGE_TILING_OPTIMAL; - //usage + // Usage. image_create_info.usage = 0; if (p_format.usage_bits & TEXTURE_USAGE_SAMPLING_BIT) { @@ -1758,6 +1759,10 @@ RID RenderingDeviceVulkan::texture_create(const TextureFormat &p_format, const T image_create_info.usage |= VK_IMAGE_USAGE_INPUT_ATTACHMENT_BIT; } + if (p_format.usage_bits & TEXTURE_USAGE_VRS_ATTACHMENT_BIT) { + image_create_info.usage |= VK_IMAGE_USAGE_FRAGMENT_SHADING_RATE_ATTACHMENT_BIT_KHR; + } + if (p_format.usage_bits & TEXTURE_USAGE_CAN_UPDATE_BIT) { image_create_info.usage |= VK_IMAGE_USAGE_TRANSFER_DST_BIT; } @@ -1795,7 +1800,7 @@ RID RenderingDeviceVulkan::texture_create(const TextureFormat &p_format, const T } { - //validate that this image is supported for the intended use + // Validate that this image is supported for the intended use. VkFormatProperties properties; vkGetPhysicalDeviceFormatProperties(context->get_physical_device(), image_create_info.format, &properties); VkFormatFeatureFlags flags; @@ -1829,9 +1834,14 @@ RID RenderingDeviceVulkan::texture_create(const TextureFormat &p_format, const T if (p_format.usage_bits & TEXTURE_USAGE_STORAGE_ATOMIC_BIT && !(flags & VK_FORMAT_FEATURE_STORAGE_IMAGE_ATOMIC_BIT)) { ERR_FAIL_V_MSG(RID(), "Format " + format_text + " does not support usage as atomic storage image."); } + + // Validation via VK_FORMAT_FEATURE_FRAGMENT_SHADING_RATE_ATTACHMENT_BIT_KHR fails if VRS attachment is not supported. + if (p_format.usage_bits & TEXTURE_USAGE_VRS_ATTACHMENT_BIT && p_format.format != DATA_FORMAT_R8_UINT) { + ERR_FAIL_V_MSG(RID(), "Format " + format_text + " does not support usage as VRS attachment."); + } } - //some view validation + // Some view validation. if (p_view.format_override != DATA_FORMAT_MAX) { ERR_FAIL_INDEX_V(p_view.format_override, DATA_FORMAT_MAX, RID()); @@ -1841,7 +1851,7 @@ RID RenderingDeviceVulkan::texture_create(const TextureFormat &p_format, const T ERR_FAIL_INDEX_V(p_view.swizzle_b, TEXTURE_SWIZZLE_MAX, RID()); ERR_FAIL_INDEX_V(p_view.swizzle_a, TEXTURE_SWIZZLE_MAX, RID()); - //allocate memory + // Allocate memory. uint32_t width, height; uint32_t image_size = get_image_format_required_size(p_format.format, p_format.width, p_format.height, p_format.depth, p_format.mipmaps, &width, &height); @@ -1878,19 +1888,19 @@ RID RenderingDeviceVulkan::texture_create(const TextureFormat &p_format, const T texture.samples = p_format.samples; texture.allowed_shared_formats = p_format.shareable_formats; - //set base layout based on usage priority + // Set base layout based on usage priority. if (p_format.usage_bits & TEXTURE_USAGE_SAMPLING_BIT) { - //first priority, readable + // First priority, readable. texture.layout = VK_IMAGE_LAYOUT_SHADER_READ_ONLY_OPTIMAL; } else if (p_format.usage_bits & TEXTURE_USAGE_STORAGE_BIT) { - //second priority, storage + // Second priority, storage. texture.layout = VK_IMAGE_LAYOUT_GENERAL; } else if (p_format.usage_bits & TEXTURE_USAGE_COLOR_ATTACHMENT_BIT) { - //third priority, color or depth + // Third priority, color or depth. texture.layout = VK_IMAGE_LAYOUT_COLOR_ATTACHMENT_OPTIMAL; @@ -1915,7 +1925,7 @@ RID RenderingDeviceVulkan::texture_create(const TextureFormat &p_format, const T texture.bound = false; - //create view + // Create view. VkImageViewCreateInfo image_view_create_info; image_view_create_info.sType = VK_STRUCTURE_TYPE_IMAGE_VIEW_CREATE_INFO; @@ -1972,7 +1982,7 @@ RID RenderingDeviceVulkan::texture_create(const TextureFormat &p_format, const T ERR_FAIL_V_MSG(RID(), "vkCreateImageView failed with error " + itos(err) + "."); } - //barrier to set layout + // Barrier to set layout. { VkImageMemoryBarrier image_memory_barrier; image_memory_barrier.sType = VK_STRUCTURE_TYPE_IMAGE_MEMORY_BARRIER; @@ -1994,6 +2004,9 @@ RID RenderingDeviceVulkan::texture_create(const TextureFormat &p_format, const T } RID id = texture_owner.make_rid(texture); +#ifdef DEV_ENABLED + set_resource_name(id, "RID:" + itos(id.get_id())); +#endif if (p_data.size()) { for (uint32_t i = 0; i < image_create_info.arrayLayers; i++) { @@ -2009,13 +2022,13 @@ RID RenderingDeviceVulkan::texture_create_shared(const TextureView &p_view, RID Texture *src_texture = texture_owner.get_or_null(p_with_texture); ERR_FAIL_COND_V(!src_texture, RID()); - if (src_texture->owner.is_valid()) { //ahh this is a share + if (src_texture->owner.is_valid()) { // Ahh this is a share. p_with_texture = src_texture->owner; src_texture = texture_owner.get_or_null(src_texture->owner); - ERR_FAIL_COND_V(!src_texture, RID()); //this is a bug + ERR_FAIL_COND_V(!src_texture, RID()); // This is a bug. } - //create view + // Create view. Texture texture = *src_texture; @@ -2076,7 +2089,7 @@ RID RenderingDeviceVulkan::texture_create_shared(const TextureView &p_view, RID usage_info.sType = VK_STRUCTURE_TYPE_IMAGE_VIEW_USAGE_CREATE_INFO; usage_info.pNext = nullptr; if (p_view.format_override != DATA_FORMAT_MAX) { - //need to validate usage with vulkan + // Need to validate usage with vulkan. usage_info.usage = 0; @@ -2123,6 +2136,9 @@ RID RenderingDeviceVulkan::texture_create_shared(const TextureView &p_view, RID texture.owner = p_with_texture; RID id = texture_owner.make_rid(texture); +#ifdef DEV_ENABLED + set_resource_name(id, "RID:" + itos(id.get_id())); +#endif _add_dependency(id, p_with_texture); return id; @@ -2135,9 +2151,9 @@ RID RenderingDeviceVulkan::texture_create_from_extension(TextureType p_type, Dat Texture texture; texture.image = image; - // if we leave texture.allocation as a nullptr, would that be enough to detect we don't "own" the image? - // also leave texture.allocation_info alone - // we'll set texture.view later on + // If we leave texture.allocation as a nullptr, would that be enough to detect we don't "own" the image? + // Also leave texture.allocation_info alone. + // We'll set texture.view later on. texture.type = p_type; texture.format = p_format; texture.samples = p_samples; @@ -2145,14 +2161,14 @@ RID RenderingDeviceVulkan::texture_create_from_extension(TextureType p_type, Dat texture.height = p_height; texture.depth = p_depth; texture.layers = p_layers; - texture.mipmaps = 0; // maybe make this settable too? + texture.mipmaps = 0; // Maybe make this settable too? texture.usage_flags = p_flags; texture.base_mipmap = 0; texture.base_layer = 0; texture.allowed_shared_formats.push_back(RD::DATA_FORMAT_R8G8B8A8_UNORM); texture.allowed_shared_formats.push_back(RD::DATA_FORMAT_R8G8B8A8_SRGB); - // Do we need to do something with texture.layout ? + // Do we need to do something with texture.layout? if (texture.usage_flags & TEXTURE_USAGE_DEPTH_STENCIL_ATTACHMENT_BIT) { texture.read_aspect_mask = VK_IMAGE_ASPECT_DEPTH_BIT; @@ -2166,7 +2182,7 @@ RID RenderingDeviceVulkan::texture_create_from_extension(TextureType p_type, Dat texture.barrier_aspect_mask = VK_IMAGE_ASPECT_COLOR_BIT; } - // Create a view for us to use + // Create a view for us to use. VkImageViewCreateInfo image_view_create_info; image_view_create_info.sType = VK_STRUCTURE_TYPE_IMAGE_VIEW_CREATE_INFO; @@ -2197,7 +2213,7 @@ RID RenderingDeviceVulkan::texture_create_from_extension(TextureType p_type, Dat VK_COMPONENT_SWIZZLE_A }; - // hardcode for now, maybe make this settable from outside.. + // Hardcode for now, maybe make this settable from outside. image_view_create_info.components.r = component_swizzles[TEXTURE_SWIZZLE_R]; image_view_create_info.components.g = component_swizzles[TEXTURE_SWIZZLE_G]; image_view_create_info.components.b = component_swizzles[TEXTURE_SWIZZLE_B]; @@ -2220,7 +2236,7 @@ RID RenderingDeviceVulkan::texture_create_from_extension(TextureType p_type, Dat ERR_FAIL_V_MSG(RID(), "vkCreateImageView failed with error " + itos(err) + "."); } - //barrier to set layout + // Barrier to set layout. { VkImageMemoryBarrier image_memory_barrier; image_memory_barrier.sType = VK_STRUCTURE_TYPE_IMAGE_MEMORY_BARRIER; @@ -2242,6 +2258,9 @@ RID RenderingDeviceVulkan::texture_create_from_extension(TextureType p_type, Dat } RID id = texture_owner.make_rid(texture); +#ifdef DEV_ENABLED + set_resource_name(id, "RID:" + itos(id.get_id())); +#endif return id; } @@ -2252,10 +2271,10 @@ RID RenderingDeviceVulkan::texture_create_shared_from_slice(const TextureView &p Texture *src_texture = texture_owner.get_or_null(p_with_texture); ERR_FAIL_COND_V(!src_texture, RID()); - if (src_texture->owner.is_valid()) { //ahh this is a share + if (src_texture->owner.is_valid()) { // Ahh this is a share. p_with_texture = src_texture->owner; src_texture = texture_owner.get_or_null(src_texture->owner); - ERR_FAIL_COND_V(!src_texture, RID()); //this is a bug + ERR_FAIL_COND_V(!src_texture, RID()); // This is a bug. } ERR_FAIL_COND_V_MSG(p_slice_type == TEXTURE_SLICE_CUBEMAP && (src_texture->type != TEXTURE_TYPE_CUBE && src_texture->type != TEXTURE_TYPE_CUBE_ARRAY), RID(), @@ -2267,7 +2286,7 @@ RID RenderingDeviceVulkan::texture_create_shared_from_slice(const TextureView &p ERR_FAIL_COND_V_MSG(p_slice_type == TEXTURE_SLICE_2D_ARRAY && (src_texture->type != TEXTURE_TYPE_2D_ARRAY), RID(), "Can only create an array slice from a 2D array mipmap"); - //create view + // Create view. ERR_FAIL_UNSIGNED_INDEX_V(p_mipmap, src_texture->mipmaps, RID()); ERR_FAIL_COND_V(p_mipmap + p_mipmaps > src_texture->mipmaps, RID()); @@ -2314,6 +2333,12 @@ RID RenderingDeviceVulkan::texture_create_shared_from_slice(const TextureView &p image_view_create_info.viewType = VK_IMAGE_VIEW_TYPE_2D_ARRAY; } + if (p_slice_type == TEXTURE_SLICE_2D) { + texture.type = TEXTURE_TYPE_2D; + } else if (p_slice_type == TEXTURE_SLICE_3D) { + texture.type = TEXTURE_TYPE_3D; + } + if (p_view.format_override == DATA_FORMAT_MAX || p_view.format_override == texture.format) { image_view_create_info.format = vulkan_formats[texture.format]; } else { @@ -2361,6 +2386,9 @@ RID RenderingDeviceVulkan::texture_create_shared_from_slice(const TextureView &p texture.owner = p_with_texture; RID id = texture_owner.make_rid(texture); +#ifdef DEV_ENABLED + set_resource_name(id, "RID:" + itos(id.get_id())); +#endif _add_dependency(id, p_with_texture); return id; @@ -2370,6 +2398,22 @@ Error RenderingDeviceVulkan::texture_update(RID p_texture, uint32_t p_layer, con return _texture_update(p_texture, p_layer, p_data, p_post_barrier, false); } +static _ALWAYS_INLINE_ void _copy_region(uint8_t const *__restrict p_src, uint8_t *__restrict p_dst, uint32_t p_src_x, uint32_t p_src_y, uint32_t p_src_w, uint32_t p_src_h, uint32_t p_src_full_w, uint32_t p_unit_size) { + uint32_t src_offset = (p_src_y * p_src_full_w + p_src_x) * p_unit_size; + uint32_t dst_offset = 0; + for (uint32_t y = p_src_h; y > 0; y--) { + uint8_t const *__restrict src = p_src + src_offset; + uint8_t *__restrict dst = p_dst + dst_offset; + for (uint32_t x = p_src_w * p_unit_size; x > 0; x--) { + *dst = *src; + src++; + dst++; + } + src_offset += p_src_full_w * p_unit_size; + dst_offset += p_src_w * p_unit_size; + } +} + 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_ @@ -2382,7 +2426,7 @@ Error RenderingDeviceVulkan::_texture_update(RID p_texture, uint32_t p_layer, co if (texture->owner != RID()) { p_texture = texture->owner; texture = texture_owner.get_or_null(texture->owner); - ERR_FAIL_COND_V(!texture, ERR_BUG); //this is a bug + ERR_FAIL_COND_V(!texture, ERR_BUG); // This is a bug. } ERR_FAIL_COND_V_MSG(texture->bound, ERR_CANT_ACQUIRE_RESOURCE, @@ -2404,7 +2448,7 @@ Error RenderingDeviceVulkan::_texture_update(RID p_texture, uint32_t p_layer, co if (required_align == 1) { required_align = get_image_format_pixel_size(texture->format); } - if ((required_align % 4) != 0) { //alignment rules are really strange + if ((required_align % 4) != 0) { // Alignment rules are really strange. required_align *= 4; } @@ -2417,7 +2461,7 @@ Error RenderingDeviceVulkan::_texture_update(RID p_texture, uint32_t p_layer, co VkCommandBuffer command_buffer = p_use_setup_queue ? frames[frame].setup_command_buffer : frames[frame].draw_command_buffer; - //barrier to transfer + // Barrier to transfer. { VkImageMemoryBarrier image_memory_barrier; image_memory_barrier.sType = VK_STRUCTURE_TYPE_IMAGE_MEMORY_BARRIER; @@ -2451,12 +2495,12 @@ Error RenderingDeviceVulkan::_texture_update(RID p_texture, uint32_t p_layer, co const uint8_t *read_ptr_mipmap = r + mipmap_offset; image_size = image_total - mipmap_offset; - for (uint32_t z = 0; z < depth; z++) { //for 3D textures, depth may be > 0 + for (uint32_t z = 0; z < depth; z++) { // For 3D textures, depth may be > 0. const uint8_t *read_ptr = read_ptr_mipmap + image_size * z / depth; - for (uint32_t x = 0; x < width; x += region_size) { - for (uint32_t y = 0; y < height; y += region_size) { + for (uint32_t y = 0; y < height; y += region_size) { + for (uint32_t x = 0; x < width; x += region_size) { uint32_t region_w = MIN(region_size, width - x); uint32_t region_h = MIN(region_size, height - y); @@ -2468,12 +2512,12 @@ Error RenderingDeviceVulkan::_texture_update(RID p_texture, uint32_t p_layer, co 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_use_setup_queue); + Error err = _staging_buffer_allocate(to_allocate, required_align, alloc_offset, alloc_size, false); ERR_FAIL_COND_V(err, ERR_CANT_CREATE); uint8_t *write_ptr; - { //map + { // Map. void *data_ptr = nullptr; VkResult vkerr = vmaMapMemory(allocator, staging_buffer_blocks[staging_buffer_current].allocation, &data_ptr); ERR_FAIL_COND_V_MSG(vkerr, ERR_CANT_CREATE, "vmaMapMemory failed with error " + itos(vkerr) + "."); @@ -2488,52 +2532,32 @@ Error RenderingDeviceVulkan::_texture_update(RID p_texture, uint32_t p_layer, co ERR_FAIL_COND_V(region_h % block_h, ERR_BUG); if (block_w != 1 || block_h != 1) { - //compressed image (blocks) - //must copy a block region + // Compressed image (blocks). + // Must copy a block region. uint32_t block_size = get_compressed_image_format_block_byte_size(texture->format); - //re-create current variables in blocky format + // Re-create current variables in blocky format. uint32_t xb = x / block_w; uint32_t yb = y / block_h; uint32_t wb = width / block_w; //uint32_t hb = height / block_h; uint32_t region_wb = region_w / block_w; uint32_t region_hb = region_h / block_h; - for (uint32_t xr = 0; xr < region_wb; xr++) { - for (uint32_t yr = 0; yr < region_hb; yr++) { - uint32_t src_offset = ((yr + yb) * wb + xr + xb) * block_size; - uint32_t dst_offset = (yr * region_wb + xr) * block_size; - //copy block - for (uint32_t i = 0; i < block_size; i++) { - write_ptr[dst_offset + i] = read_ptr[src_offset + i]; - } - } - } - + _copy_region(read_ptr, write_ptr, xb, yb, region_wb, region_hb, wb, block_size); } else { - //regular image (pixels) - //must copy a pixel region - - for (uint32_t xr = 0; xr < region_w; xr++) { - for (uint32_t yr = 0; yr < region_h; yr++) { - uint32_t src_offset = ((yr + y) * width + xr + x) * pixel_size; - uint32_t dst_offset = (yr * region_w + xr) * pixel_size; - //copy block - for (uint32_t i = 0; i < pixel_size; i++) { - write_ptr[dst_offset + i] = read_ptr[src_offset + i]; - } - } - } + // Regular image (pixels). + // Must copy a pixel region. + _copy_region(read_ptr, write_ptr, x, y, region_w, region_h, width, pixel_size); } - { //unmap + { // Unmap. vmaUnmapMemory(allocator, staging_buffer_blocks[staging_buffer_current].allocation); } VkBufferImageCopy buffer_image_copy; buffer_image_copy.bufferOffset = alloc_offset; - buffer_image_copy.bufferRowLength = 0; //tightly packed - buffer_image_copy.bufferImageHeight = 0; //tightly packed + buffer_image_copy.bufferRowLength = 0; // Tightly packed. + buffer_image_copy.bufferImageHeight = 0; // Tightly packed. buffer_image_copy.imageSubresource.aspectMask = texture->read_aspect_mask; buffer_image_copy.imageSubresource.mipLevel = mm_i; @@ -2560,17 +2584,17 @@ Error RenderingDeviceVulkan::_texture_update(RID p_texture, uint32_t p_layer, co logic_height = MAX(1u, logic_height >> 1); } - //barrier to restore layout + // Barrier to restore layout. { uint32_t barrier_flags = 0; uint32_t access_flags = 0; if (p_post_barrier & BARRIER_MASK_COMPUTE) { barrier_flags |= VK_PIPELINE_STAGE_COMPUTE_SHADER_BIT; - access_flags |= VK_ACCESS_SHADER_READ_BIT; + access_flags |= VK_ACCESS_SHADER_READ_BIT | VK_ACCESS_SHADER_WRITE_BIT; } if (p_post_barrier & BARRIER_MASK_RASTER) { barrier_flags |= VK_PIPELINE_STAGE_VERTEX_SHADER_BIT | VK_PIPELINE_STAGE_FRAGMENT_SHADER_BIT; - access_flags |= VK_ACCESS_SHADER_READ_BIT; + access_flags |= VK_ACCESS_SHADER_READ_BIT | VK_ACCESS_SHADER_WRITE_BIT; } if (p_post_barrier & BARRIER_MASK_TRANSFER) { barrier_flags |= VK_PIPELINE_STAGE_TRANSFER_BIT; @@ -2647,7 +2671,7 @@ Vector<uint8_t> RenderingDeviceVulkan::_texture_get_data_from_image(Texture *tex const uint8_t *slice_read_ptr = ((uint8_t *)img_mem) + layout.offset + z * layout.depthPitch; if (block_size > 1) { - //compressed + // Compressed. uint32_t line_width = (block_size * (width / blockw)); for (uint32_t y = 0; y < height / blockh; y++) { const uint8_t *rptr = slice_read_ptr + y * layout.rowPitch; @@ -2657,7 +2681,7 @@ Vector<uint8_t> RenderingDeviceVulkan::_texture_get_data_from_image(Texture *tex } } else { - //uncompressed + // Uncompressed. 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; @@ -2693,19 +2717,19 @@ Vector<uint8_t> RenderingDeviceVulkan::texture_get_data(RID p_texture, uint32_t ERR_FAIL_COND_V(p_layer >= layer_count, Vector<uint8_t>()); if (tex->usage_flags & TEXTURE_USAGE_CPU_READ_BIT) { - //does not need anything fancy, map and read. + // Does not need anything fancy, map and read. return _texture_get_data_from_image(tex, tex->image, tex->allocation, p_layer); } else { - //compute total image size + // Compute total image size. uint32_t width, height, depth; uint32_t buffer_size = get_image_format_required_size(tex->format, tex->width, tex->height, tex->depth, tex->mipmaps, &width, &height, &depth); - //allocate buffer - VkCommandBuffer command_buffer = frames[frame].draw_command_buffer; //makes more sense to retrieve + // Allocate buffer. + VkCommandBuffer command_buffer = frames[frame].draw_command_buffer; // Makes more sense to retrieve. Buffer tmp_buffer; _buffer_allocate(&tmp_buffer, buffer_size, VK_BUFFER_USAGE_TRANSFER_DST_BIT, VMA_MEMORY_USAGE_AUTO_PREFER_HOST, VMA_ALLOCATION_CREATE_HOST_ACCESS_RANDOM_BIT); - { //Source image barrier + { // Source image barrier. VkImageMemoryBarrier image_memory_barrier; image_memory_barrier.sType = VK_STRUCTURE_TYPE_IMAGE_MEMORY_BARRIER; image_memory_barrier.pNext = nullptr; @@ -2761,7 +2785,7 @@ Vector<uint8_t> RenderingDeviceVulkan::texture_get_data(RID p_texture, uint32_t offset += size; } - { //restore src + { // Restore src. VkImageMemoryBarrier image_memory_barrier; image_memory_barrier.sType = VK_STRUCTURE_TYPE_IMAGE_MEMORY_BARRIER; image_memory_barrier.pNext = nullptr; @@ -2876,9 +2900,9 @@ Error RenderingDeviceVulkan::texture_copy(RID p_from_texture, RID p_to_texture, VkCommandBuffer command_buffer = frames[frame].draw_command_buffer; { - //PRE Copy the image + // PRE Copy the image. - { //Source + { // Source. VkImageMemoryBarrier image_memory_barrier; image_memory_barrier.sType = VK_STRUCTURE_TYPE_IMAGE_MEMORY_BARRIER; image_memory_barrier.pNext = nullptr; @@ -2898,7 +2922,7 @@ Error RenderingDeviceVulkan::texture_copy(RID p_from_texture, RID p_to_texture, vkCmdPipelineBarrier(command_buffer, VK_PIPELINE_STAGE_TOP_OF_PIPE_BIT, VK_PIPELINE_STAGE_TRANSFER_BIT, 0, 0, nullptr, 0, nullptr, 1, &image_memory_barrier); } - { //Dest + { // Dest. VkImageMemoryBarrier image_memory_barrier; image_memory_barrier.sType = VK_STRUCTURE_TYPE_IMAGE_MEMORY_BARRIER; image_memory_barrier.pNext = nullptr; @@ -2919,7 +2943,7 @@ Error RenderingDeviceVulkan::texture_copy(RID p_from_texture, RID p_to_texture, vkCmdPipelineBarrier(command_buffer, VK_PIPELINE_STAGE_TOP_OF_PIPE_BIT, VK_PIPELINE_STAGE_TRANSFER_BIT, 0, 0, nullptr, 0, nullptr, 1, &image_memory_barrier); } - //COPY + // COPY. { VkImageCopy image_copy_region; @@ -2946,7 +2970,7 @@ Error RenderingDeviceVulkan::texture_copy(RID p_from_texture, RID p_to_texture, vkCmdCopyImage(command_buffer, src_tex->image, VK_IMAGE_LAYOUT_TRANSFER_SRC_OPTIMAL, dst_tex->image, VK_IMAGE_LAYOUT_TRANSFER_DST_OPTIMAL, 1, &image_copy_region); } - // RESTORE LAYOUT for SRC and DST + // RESTORE LAYOUT for SRC and DST. uint32_t barrier_flags = 0; uint32_t access_flags = 0; @@ -2967,7 +2991,7 @@ Error RenderingDeviceVulkan::texture_copy(RID p_from_texture, RID p_to_texture, barrier_flags = VK_PIPELINE_STAGE_BOTTOM_OF_PIPE_BIT; } - { //restore src + { // Restore src. VkImageMemoryBarrier image_memory_barrier; image_memory_barrier.sType = VK_STRUCTURE_TYPE_IMAGE_MEMORY_BARRIER; image_memory_barrier.pNext = nullptr; @@ -2984,10 +3008,10 @@ Error RenderingDeviceVulkan::texture_copy(RID p_from_texture, RID p_to_texture, image_memory_barrier.subresourceRange.baseArrayLayer = p_src_layer; image_memory_barrier.subresourceRange.layerCount = 1; - vkCmdPipelineBarrier(command_buffer, VK_ACCESS_TRANSFER_WRITE_BIT, barrier_flags, 0, 0, nullptr, 0, nullptr, 1, &image_memory_barrier); + vkCmdPipelineBarrier(command_buffer, VK_PIPELINE_STAGE_TRANSFER_BIT, barrier_flags, 0, 0, nullptr, 0, nullptr, 1, &image_memory_barrier); } - { //make dst readable + { // Make dst readable. VkImageMemoryBarrier image_memory_barrier; image_memory_barrier.sType = VK_STRUCTURE_TYPE_IMAGE_MEMORY_BARRIER; @@ -3010,6 +3034,13 @@ Error RenderingDeviceVulkan::texture_copy(RID p_from_texture, RID p_to_texture, } } + if (dst_tex->used_in_frame != frames_drawn) { + dst_tex->used_in_raster = false; + dst_tex->used_in_compute = false; + dst_tex->used_in_frame = frames_drawn; + } + dst_tex->used_in_transfer = true; + return OK; } @@ -3047,9 +3078,9 @@ Error RenderingDeviceVulkan::texture_resolve_multisample(RID p_from_texture, RID VkCommandBuffer command_buffer = frames[frame].draw_command_buffer; { - //PRE Copy the image + // PRE Copy the image. - { //Source + { // Source. VkImageMemoryBarrier image_memory_barrier; image_memory_barrier.sType = VK_STRUCTURE_TYPE_IMAGE_MEMORY_BARRIER; image_memory_barrier.pNext = nullptr; @@ -3069,7 +3100,7 @@ Error RenderingDeviceVulkan::texture_resolve_multisample(RID p_from_texture, RID vkCmdPipelineBarrier(command_buffer, VK_PIPELINE_STAGE_BOTTOM_OF_PIPE_BIT, VK_PIPELINE_STAGE_TRANSFER_BIT, 0, 0, nullptr, 0, nullptr, 1, &image_memory_barrier); } - { //Dest + { // Dest. VkImageMemoryBarrier image_memory_barrier; image_memory_barrier.sType = VK_STRUCTURE_TYPE_IMAGE_MEMORY_BARRIER; image_memory_barrier.pNext = nullptr; @@ -3090,7 +3121,7 @@ Error RenderingDeviceVulkan::texture_resolve_multisample(RID p_from_texture, RID vkCmdPipelineBarrier(command_buffer, VK_PIPELINE_STAGE_TOP_OF_PIPE_BIT, VK_PIPELINE_STAGE_TRANSFER_BIT, 0, 0, nullptr, 0, nullptr, 1, &image_memory_barrier); } - //COPY + // COPY. { VkImageResolve image_copy_region; @@ -3117,7 +3148,7 @@ Error RenderingDeviceVulkan::texture_resolve_multisample(RID p_from_texture, RID vkCmdResolveImage(command_buffer, src_tex->image, VK_IMAGE_LAYOUT_TRANSFER_SRC_OPTIMAL, dst_tex->image, VK_IMAGE_LAYOUT_TRANSFER_DST_OPTIMAL, 1, &image_copy_region); } - // RESTORE LAYOUT for SRC and DST + // RESTORE LAYOUT for SRC and DST. uint32_t barrier_flags = 0; uint32_t access_flags = 0; @@ -3138,7 +3169,7 @@ Error RenderingDeviceVulkan::texture_resolve_multisample(RID p_from_texture, RID barrier_flags = VK_PIPELINE_STAGE_BOTTOM_OF_PIPE_BIT; } - { //restore src + { // Restore src. VkImageMemoryBarrier image_memory_barrier; image_memory_barrier.sType = VK_STRUCTURE_TYPE_IMAGE_MEMORY_BARRIER; image_memory_barrier.pNext = nullptr; @@ -3158,7 +3189,7 @@ Error RenderingDeviceVulkan::texture_resolve_multisample(RID p_from_texture, RID vkCmdPipelineBarrier(command_buffer, VK_ACCESS_TRANSFER_WRITE_BIT, barrier_flags, 0, 0, nullptr, 0, nullptr, 1, &image_memory_barrier); } - { //make dst readable + { // Make dst readable. VkImageMemoryBarrier image_memory_barrier; image_memory_barrier.sType = VK_STRUCTURE_TYPE_IMAGE_MEMORY_BARRIER; @@ -3211,13 +3242,13 @@ 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 owner 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; const VkAccessFlags valid_texture_access = (src_tex->usage_flags & TEXTURE_USAGE_STORAGE_BIT) ? read_write_access : read_access; - { // Barrier from previous access with optional layout change (see clear_layout logic above) + { // Barrier from previous access with optional layout change (see clear_layout logic above). VkImageMemoryBarrier image_memory_barrier; image_memory_barrier.sType = VK_STRUCTURE_TYPE_IMAGE_MEMORY_BARRIER; image_memory_barrier.pNext = nullptr; @@ -3253,7 +3284,7 @@ Error RenderingDeviceVulkan::texture_clear(RID p_texture, const Color &p_color, vkCmdClearColorImage(command_buffer, src_tex->image, clear_layout, &clear_color, 1, &range); - { // Barrier to post clear accesses (changing back the layout if needed) + { // Barrier to post clear accesses (changing back the layout if needed). uint32_t barrier_flags = 0; uint32_t access_flags = 0; @@ -3309,7 +3340,7 @@ bool RenderingDeviceVulkan::texture_is_format_supported_for_usage(DataFormat p_f _THREAD_SAFE_METHOD_ - //validate that this image is supported for the intended use + // Validate that this image is supported for the intended use. VkFormatProperties properties; vkGetPhysicalDeviceFormatProperties(context->get_physical_device(), vulkan_formats[p_format], &properties); VkFormatFeatureFlags flags; @@ -3340,6 +3371,11 @@ bool RenderingDeviceVulkan::texture_is_format_supported_for_usage(DataFormat p_f return false; } + // Validation via VK_FORMAT_FEATURE_FRAGMENT_SHADING_RATE_ATTACHMENT_BIT_KHR fails if VRS attachment is not supported. + if (p_usage & TEXTURE_USAGE_VRS_ATTACHMENT_BIT && p_format != DATA_FORMAT_R8_UINT) { + return false; + } + return true; } @@ -3348,22 +3384,29 @@ bool RenderingDeviceVulkan::texture_is_format_supported_for_usage(DataFormat p_f /********************/ 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 + // 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 | VK_ACCESS_DEPTH_STENCIL_ATTACHMENT_READ_BIT | - VK_ACCESS_DEPTH_STENCIL_ATTACHMENT_WRITE_BIT; // From Section 7.1 of Vulkan API Spec v1.1.148 + VK_ACCESS_DEPTH_STENCIL_ATTACHMENT_WRITE_BIT | // From Section 7.1 of Vulkan API Spec v1.1.148. + VK_ACCESS_FRAGMENT_SHADING_RATE_ATTACHMENT_READ_BIT_KHR; VkPipelineStageFlags reading_stages = VK_PIPELINE_STAGE_COMPUTE_SHADER_BIT | VK_PIPELINE_STAGE_VERTEX_SHADER_BIT | VK_PIPELINE_STAGE_FRAGMENT_SHADER_BIT | VK_PIPELINE_STAGE_TRANSFER_BIT; - VkSubpassDependency dependencies[2] = { { VK_SUBPASS_EXTERNAL, 0, VK_PIPELINE_STAGE_TOP_OF_PIPE_BIT, VK_PIPELINE_STAGE_ALL_GRAPHICS_BIT, 0, default_access_mask, 0 }, - { 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]; + VkSubpassDependency2KHR dependencies[2] = { + { VK_STRUCTURE_TYPE_SUBPASS_DESCRIPTION_2_KHR, nullptr, VK_SUBPASS_EXTERNAL, 0, VK_PIPELINE_STAGE_TOP_OF_PIPE_BIT, VK_PIPELINE_STAGE_ALL_GRAPHICS_BIT, 0, default_access_mask, 0, 0 }, + { VK_STRUCTURE_TYPE_SUBPASS_DESCRIPTION_2_KHR, nullptr, 0, VK_SUBPASS_EXTERNAL, VK_PIPELINE_STAGE_ALL_GRAPHICS_BIT, VK_PIPELINE_STAGE_BOTTOM_OF_PIPE_BIT, default_access_mask, 0, 0, 0 } + }; + VkSubpassDependency2KHR &dependency_from_external = dependencies[0]; + VkSubpassDependency2KHR &dependency_to_external = dependencies[1]; LocalVector<int32_t> attachment_last_pass; attachment_last_pass.resize(p_attachments.size()); - Vector<VkAttachmentDescription> attachments; + // These are only used if we use multiview but we need to define them in scope. + const uint32_t view_mask = (1 << p_view_count) - 1; + const uint32_t correlation_mask = (1 << p_view_count) - 1; + + Vector<VkAttachmentDescription2KHR> attachments; Vector<int> attachment_remap; for (int i = 0; i < p_attachments.size(); i++) { @@ -3374,10 +3417,12 @@ VkRenderPass RenderingDeviceVulkan::_render_pass_create(const Vector<AttachmentF 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_INPUT_ATTACHMENT_BIT)), - VK_NULL_HANDLE, "Texture format for index (" + itos(i) + ") requires an attachment (color, depth, input or stencil) bit set."); + ERR_FAIL_COND_V_MSG(!(p_attachments[i].usage_flags & (TEXTURE_USAGE_COLOR_ATTACHMENT_BIT | TEXTURE_USAGE_DEPTH_STENCIL_ATTACHMENT_BIT | TEXTURE_USAGE_INPUT_ATTACHMENT_BIT | TEXTURE_USAGE_VRS_ATTACHMENT_BIT)), + VK_NULL_HANDLE, "Texture format for index (" + itos(i) + ") requires an attachment (color, depth-stencil, input or VRS) bit set."); - VkAttachmentDescription description = {}; + VkAttachmentDescription2KHR description = {}; + description.sType = VK_STRUCTURE_TYPE_ATTACHMENT_DESCRIPTION_2_KHR; + description.pNext = nullptr; description.flags = 0; description.format = vulkan_formats[p_attachments[i].format]; description.samples = rasterization_sample_count[p_attachments[i].samples]; @@ -3386,83 +3431,95 @@ VkRenderPass RenderingDeviceVulkan::_render_pass_create(const Vector<AttachmentF 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 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 ? p_initial_depth_action : p_initial_action) { - case INITIAL_ACTION_CLEAR_REGION: - case INITIAL_ACTION_CLEAR: { - if (p_attachments[i].usage_flags & TEXTURE_USAGE_COLOR_ATTACHMENT_BIT) { - description.loadOp = VK_ATTACHMENT_LOAD_OP_CLEAR; - 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_attachments[i].usage_flags & TEXTURE_USAGE_DEPTH_STENCIL_ATTACHMENT_BIT) { - description.loadOp = VK_ATTACHMENT_LOAD_OP_CLEAR; - 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_CLEAR; - dependency_from_external.srcStageMask |= reading_stages; - } else { - description.loadOp = VK_ATTACHMENT_LOAD_OP_DONT_CARE; - description.stencilLoadOp = VK_ATTACHMENT_LOAD_OP_DONT_CARE; - description.initialLayout = VK_IMAGE_LAYOUT_UNDEFINED; //don't care what is there - dependency_from_external.srcStageMask |= reading_stages; - } - } break; - case INITIAL_ACTION_KEEP: { - 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_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; - dependency_from_external.srcStageMask |= reading_stages; - } else { - description.loadOp = VK_ATTACHMENT_LOAD_OP_DONT_CARE; - description.stencilLoadOp = VK_ATTACHMENT_LOAD_OP_DONT_CARE; - description.initialLayout = VK_IMAGE_LAYOUT_UNDEFINED; //don't care what is there - dependency_from_external.srcStageMask |= reading_stages; - } - } break; - case INITIAL_ACTION_DROP: { - 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_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; - dependency_from_external.srcStageMask |= reading_stages; - } else { - description.loadOp = VK_ATTACHMENT_LOAD_OP_DONT_CARE; - description.stencilLoadOp = VK_ATTACHMENT_LOAD_OP_DONT_CARE; - description.initialLayout = VK_IMAGE_LAYOUT_UNDEFINED; //don't care what is there - dependency_from_external.srcStageMask |= reading_stages; - } - } break; - case INITIAL_ACTION_CLEAR_REGION_CONTINUE: - case INITIAL_ACTION_CONTINUE: { - 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_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; - } else { - description.loadOp = VK_ATTACHMENT_LOAD_OP_DONT_CARE; - description.stencilLoadOp = VK_ATTACHMENT_LOAD_OP_DONT_CARE; - description.initialLayout = VK_IMAGE_LAYOUT_UNDEFINED; //don't care what is there - dependency_from_external.srcStageMask |= reading_stages; + // We can setup a framebuffer where we write to our VRS texture to set it up. + // We make the assumption here that if our texture is actually used as our VRS attachment. + // It is used as such for each subpass. This is fairly certain seeing the restrictions on subpasses. + bool is_vrs = p_attachments[i].usage_flags & TEXTURE_USAGE_VRS_ATTACHMENT_BIT && i == p_passes[0].vrs_attachment; + + if (is_vrs) { + // For VRS we only read, there is no writing to this texture. + description.loadOp = VK_ATTACHMENT_LOAD_OP_LOAD; + description.initialLayout = VK_IMAGE_LAYOUT_SHADER_READ_ONLY_OPTIMAL; + description.stencilLoadOp = VK_ATTACHMENT_LOAD_OP_LOAD; + } else { + // 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 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 ? p_initial_depth_action : p_initial_action) { + case INITIAL_ACTION_CLEAR_REGION: + case INITIAL_ACTION_CLEAR: { + if (p_attachments[i].usage_flags & TEXTURE_USAGE_COLOR_ATTACHMENT_BIT) { + description.loadOp = VK_ATTACHMENT_LOAD_OP_CLEAR; + 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_attachments[i].usage_flags & TEXTURE_USAGE_DEPTH_STENCIL_ATTACHMENT_BIT) { + description.loadOp = VK_ATTACHMENT_LOAD_OP_CLEAR; + 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_CLEAR; + dependency_from_external.srcStageMask |= reading_stages; + } else { + description.loadOp = VK_ATTACHMENT_LOAD_OP_DONT_CARE; + description.stencilLoadOp = VK_ATTACHMENT_LOAD_OP_DONT_CARE; + description.initialLayout = VK_IMAGE_LAYOUT_UNDEFINED; // Don't care what is there. + dependency_from_external.srcStageMask |= reading_stages; + } + } break; + case INITIAL_ACTION_KEEP: { + 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_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; + dependency_from_external.srcStageMask |= reading_stages; + } else { + description.loadOp = VK_ATTACHMENT_LOAD_OP_DONT_CARE; + description.stencilLoadOp = VK_ATTACHMENT_LOAD_OP_DONT_CARE; + description.initialLayout = VK_IMAGE_LAYOUT_UNDEFINED; // Don't care what is there. + dependency_from_external.srcStageMask |= reading_stages; + } + } break; + case INITIAL_ACTION_DROP: { + 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_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; + dependency_from_external.srcStageMask |= reading_stages; + } else { + description.loadOp = VK_ATTACHMENT_LOAD_OP_DONT_CARE; + description.stencilLoadOp = VK_ATTACHMENT_LOAD_OP_DONT_CARE; + description.initialLayout = VK_IMAGE_LAYOUT_UNDEFINED; // Don't care what is there. + dependency_from_external.srcStageMask |= reading_stages; + } + } break; + case INITIAL_ACTION_CLEAR_REGION_CONTINUE: + case INITIAL_ACTION_CONTINUE: { + 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_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; + } else { + description.loadOp = VK_ATTACHMENT_LOAD_OP_DONT_CARE; + description.stencilLoadOp = VK_ATTACHMENT_LOAD_OP_DONT_CARE; + description.initialLayout = VK_IMAGE_LAYOUT_UNDEFINED; // Don't care what is there. + dependency_from_external.srcStageMask |= reading_stages; + } + } break; + default: { + ERR_FAIL_V(VK_NULL_HANDLE); // Should never reach here. } - } break; - default: { - ERR_FAIL_V(VK_NULL_HANDLE); //should never reach here } } @@ -3472,13 +3529,17 @@ VkRenderPass RenderingDeviceVulkan::_render_pass_create(const Vector<AttachmentF int last_pass = p_passes.size() - 1; if (is_depth) { - //likely missing depth resolve? + // Likely missing depth resolve? if (p_passes[last_pass].depth_attachment == i) { used_last = true; } + } else if (is_vrs) { + if (p_passes[last_pass].vrs_attachment == i) { + used_last = true; + } } else { if (p_passes[last_pass].resolve_attachments.size()) { - //if using resolve attachments, check resolve attachments + // If using resolve attachments, check resolve attachments. for (int j = 0; j < p_passes[last_pass].resolve_attachments.size(); j++) { if (p_passes[last_pass].resolve_attachments[j] == i) { used_last = true; @@ -3517,58 +3578,69 @@ VkRenderPass RenderingDeviceVulkan::_render_pass_create(const Vector<AttachmentF } } - switch (is_depth ? final_depth_action : final_action) { - case FINAL_ACTION_READ: { - 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_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); - update_external_dependency_for_store(dependency_to_external, is_sampled, is_storage, true); - } else { - description.loadOp = VK_ATTACHMENT_LOAD_OP_DONT_CARE; - description.stencilLoadOp = VK_ATTACHMENT_LOAD_OP_DONT_CARE; - description.initialLayout = VK_IMAGE_LAYOUT_UNDEFINED; //don't care what is there - // TODO: What does this mean about the next usage (and thus appropriate dependency masks - } - } break; - case FINAL_ACTION_DISCARD: { - 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_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); - } else { - description.loadOp = VK_ATTACHMENT_LOAD_OP_DONT_CARE; - description.stencilLoadOp = VK_ATTACHMENT_LOAD_OP_DONT_CARE; - description.initialLayout = VK_IMAGE_LAYOUT_UNDEFINED; //don't care what is there - } - } break; - case FINAL_ACTION_CONTINUE: { - 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_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; - } else { - description.loadOp = VK_ATTACHMENT_LOAD_OP_DONT_CARE; - description.stencilLoadOp = VK_ATTACHMENT_LOAD_OP_DONT_CARE; - description.initialLayout = VK_IMAGE_LAYOUT_UNDEFINED; //don't care what is there - } + if (is_vrs) { + // We don't change our VRS texture during this process. - } break; - default: { - ERR_FAIL_V(VK_NULL_HANDLE); //should never reach here + description.storeOp = VK_ATTACHMENT_STORE_OP_DONT_CARE; + description.stencilStoreOp = VK_ATTACHMENT_STORE_OP_DONT_CARE; + description.finalLayout = VK_IMAGE_LAYOUT_SHADER_READ_ONLY_OPTIMAL; + + // TODO: Do we need to update our external dependency? + // update_external_dependency_for_store(dependency_to_external, is_sampled, is_storage, false); + } else { + switch (is_depth ? final_depth_action : final_action) { + case FINAL_ACTION_READ: { + 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_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); + update_external_dependency_for_store(dependency_to_external, is_sampled, is_storage, true); + } else { + description.loadOp = VK_ATTACHMENT_LOAD_OP_DONT_CARE; + description.stencilLoadOp = VK_ATTACHMENT_LOAD_OP_DONT_CARE; + description.initialLayout = VK_IMAGE_LAYOUT_UNDEFINED; // Don't care what is there. + // TODO: What does this mean about the next usage (and thus appropriate dependency masks. + } + } break; + case FINAL_ACTION_DISCARD: { + 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_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); + } else { + description.storeOp = VK_ATTACHMENT_STORE_OP_DONT_CARE; + description.stencilStoreOp = VK_ATTACHMENT_STORE_OP_DONT_CARE; + description.finalLayout = VK_IMAGE_LAYOUT_UNDEFINED; // Don't care what is there. + } + } break; + case FINAL_ACTION_CONTINUE: { + 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_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; + } else { + description.storeOp = VK_ATTACHMENT_STORE_OP_DONT_CARE; + description.stencilStoreOp = VK_ATTACHMENT_STORE_OP_DONT_CARE; + description.finalLayout = VK_IMAGE_LAYOUT_UNDEFINED; // Don't care what is there. + } + + } break; + default: { + ERR_FAIL_V(VK_NULL_HANDLE); // Should never reach here. + } } } @@ -3577,12 +3649,14 @@ VkRenderPass RenderingDeviceVulkan::_render_pass_create(const Vector<AttachmentF attachments.push_back(description); } - LocalVector<VkSubpassDescription> subpasses; - LocalVector<LocalVector<VkAttachmentReference>> color_reference_array; - LocalVector<LocalVector<VkAttachmentReference>> input_reference_array; - LocalVector<LocalVector<VkAttachmentReference>> resolve_reference_array; + LocalVector<VkSubpassDescription2KHR> subpasses; + LocalVector<LocalVector<VkAttachmentReference2KHR>> color_reference_array; + LocalVector<LocalVector<VkAttachmentReference2KHR>> input_reference_array; + LocalVector<LocalVector<VkAttachmentReference2KHR>> resolve_reference_array; LocalVector<LocalVector<uint32_t>> preserve_reference_array; - LocalVector<VkAttachmentReference> depth_reference_array; + LocalVector<VkAttachmentReference2KHR> depth_reference_array; + LocalVector<VkAttachmentReference2KHR> vrs_reference_array; + LocalVector<VkFragmentShadingRateAttachmentInfoKHR> vrs_attachment_info_array; subpasses.resize(p_passes.size()); color_reference_array.resize(p_passes.size()); @@ -3590,20 +3664,25 @@ VkRenderPass RenderingDeviceVulkan::_render_pass_create(const Vector<AttachmentF resolve_reference_array.resize(p_passes.size()); preserve_reference_array.resize(p_passes.size()); depth_reference_array.resize(p_passes.size()); + vrs_reference_array.resize(p_passes.size()); + vrs_attachment_info_array.resize(p_passes.size()); - LocalVector<VkSubpassDependency> subpass_dependencies; + LocalVector<VkSubpassDependency2KHR> 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]; + LocalVector<VkAttachmentReference2KHR> &color_references = color_reference_array[i]; TextureSamples texture_samples = TEXTURE_SAMPLES_1; bool is_multisample_first = true; + void *subpass_nextptr = nullptr; for (int j = 0; j < pass->color_attachments.size(); j++) { int32_t attachment = pass->color_attachments[j]; - VkAttachmentReference reference; + VkAttachmentReference2KHR reference; + reference.sType = VK_STRUCTURE_TYPE_ATTACHMENT_REFERENCE_2_KHR; + reference.pNext = nullptr; if (attachment == FramebufferPass::ATTACHMENT_UNUSED) { reference.attachment = VK_ATTACHMENT_UNUSED; reference.layout = VK_IMAGE_LAYOUT_UNDEFINED; @@ -3622,14 +3701,17 @@ VkRenderPass RenderingDeviceVulkan::_render_pass_create(const Vector<AttachmentF reference.layout = VK_IMAGE_LAYOUT_COLOR_ATTACHMENT_OPTIMAL; attachment_last_pass[attachment] = i; } + reference.aspectMask = 0; color_references.push_back(reference); } - LocalVector<VkAttachmentReference> &input_references = input_reference_array[i]; + LocalVector<VkAttachmentReference2KHR> &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; + VkAttachmentReference2KHR reference; + reference.sType = VK_STRUCTURE_TYPE_ATTACHMENT_REFERENCE_2_KHR; + reference.pNext = nullptr; if (attachment == FramebufferPass::ATTACHMENT_UNUSED) { reference.attachment = VK_ATTACHMENT_UNUSED; reference.layout = VK_IMAGE_LAYOUT_UNDEFINED; @@ -3641,10 +3723,11 @@ VkRenderPass RenderingDeviceVulkan::_render_pass_create(const Vector<AttachmentF reference.layout = VK_IMAGE_LAYOUT_SHADER_READ_ONLY_OPTIMAL; attachment_last_pass[attachment] = i; } + reference.aspectMask = 0; // TODO: We need to set this here, possibly VK_IMAGE_ASPECT_COLOR_BIT? input_references.push_back(reference); } - LocalVector<VkAttachmentReference> &resolve_references = resolve_reference_array[i]; + LocalVector<VkAttachmentReference2KHR> &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()) + ")."); @@ -3652,7 +3735,9 @@ VkRenderPass RenderingDeviceVulkan::_render_pass_create(const Vector<AttachmentF } for (int j = 0; j < pass->resolve_attachments.size(); j++) { int32_t attachment = pass->resolve_attachments[j]; - VkAttachmentReference reference; + VkAttachmentReference2KHR reference; + reference.sType = VK_STRUCTURE_TYPE_ATTACHMENT_REFERENCE_2_KHR; + reference.pNext = nullptr; if (attachment == FramebufferPass::ATTACHMENT_UNUSED) { reference.attachment = VK_ATTACHMENT_UNUSED; reference.layout = VK_IMAGE_LAYOUT_UNDEFINED; @@ -3664,13 +3749,16 @@ VkRenderPass RenderingDeviceVulkan::_render_pass_create(const Vector<AttachmentF 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_remap[attachment]; - reference.layout = VK_IMAGE_LAYOUT_COLOR_ATTACHMENT_OPTIMAL; // VK_IMAGE_LAYOUT_SHADER_READ_ONLY_OPTIMAL; + reference.layout = VK_IMAGE_LAYOUT_COLOR_ATTACHMENT_OPTIMAL; // VK_IMAGE_LAYOUT_SHADER_READ_ONLY_OPTIMAL attachment_last_pass[attachment] = i; } + reference.aspectMask = 0; resolve_references.push_back(reference); } - VkAttachmentReference &depth_stencil_reference = depth_reference_array[i]; + VkAttachmentReference2KHR &depth_stencil_reference = depth_reference_array[i]; + depth_stencil_reference.sType = VK_STRUCTURE_TYPE_ATTACHMENT_REFERENCE_2_KHR; + depth_stencil_reference.pNext = nullptr; if (pass->depth_attachment != FramebufferPass::ATTACHMENT_UNUSED) { int32_t attachment = pass->depth_attachment; @@ -3679,6 +3767,7 @@ VkRenderPass RenderingDeviceVulkan::_render_pass_create(const Vector<AttachmentF 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_remap[attachment]; depth_stencil_reference.layout = VK_IMAGE_LAYOUT_DEPTH_STENCIL_ATTACHMENT_OPTIMAL; + depth_stencil_reference.aspectMask = 0; attachment_last_pass[attachment] = i; if (is_multisample_first) { @@ -3693,6 +3782,32 @@ VkRenderPass RenderingDeviceVulkan::_render_pass_create(const Vector<AttachmentF depth_stencil_reference.layout = VK_IMAGE_LAYOUT_UNDEFINED; } + if (context->get_vrs_capabilities().attachment_vrs_supported && pass->vrs_attachment != FramebufferPass::ATTACHMENT_UNUSED) { + int32_t attachment = pass->vrs_attachment; + ERR_FAIL_INDEX_V_MSG(attachment, p_attachments.size(), VK_NULL_HANDLE, "Invalid framebuffer VRS format attachment(" + itos(attachment) + "), in pass (" + itos(i) + "), VRS attachment."); + ERR_FAIL_COND_V_MSG(!(p_attachments[attachment].usage_flags & TEXTURE_USAGE_VRS_ATTACHMENT_BIT), VK_NULL_HANDLE, "Invalid framebuffer VRS format attachment(" + itos(attachment) + "), in pass (" + itos(i) + "), it's marked as VRS, but it's not a VRS attachment."); + ERR_FAIL_COND_V_MSG(attachment_last_pass[attachment] == i, VK_NULL_HANDLE, "Invalid framebuffer VRS attachment(" + itos(attachment) + "), in pass (" + itos(i) + "), it already was used for something else before in this pass."); + + VkAttachmentReference2KHR &vrs_reference = vrs_reference_array[i]; + vrs_reference.sType = VK_STRUCTURE_TYPE_ATTACHMENT_REFERENCE_2_KHR; + vrs_reference.pNext = nullptr; + vrs_reference.attachment = attachment_remap[attachment]; + vrs_reference.layout = VK_IMAGE_LAYOUT_FRAGMENT_SHADING_RATE_ATTACHMENT_OPTIMAL_KHR; + vrs_reference.aspectMask = 0; + + Size2i texel_size = context->get_vrs_capabilities().max_texel_size; + + VkFragmentShadingRateAttachmentInfoKHR &vrs_attachment_info = vrs_attachment_info_array[i]; + vrs_attachment_info.sType = VK_STRUCTURE_TYPE_FRAGMENT_SHADING_RATE_ATTACHMENT_INFO_KHR; + vrs_attachment_info.pNext = nullptr; + vrs_attachment_info.pFragmentShadingRateAttachment = &vrs_reference; + vrs_attachment_info.shadingRateAttachmentTexelSize = { uint32_t(texel_size.x), uint32_t(texel_size.y) }; + + attachment_last_pass[attachment] = i; + + subpass_nextptr = &vrs_attachment_info; + } + LocalVector<uint32_t> &preserve_references = preserve_reference_array[i]; for (int j = 0; j < pass->preserve_attachments.size(); j++) { @@ -3703,15 +3818,23 @@ VkRenderPass RenderingDeviceVulkan::_render_pass_create(const Vector<AttachmentF 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) + ")."); if (attachment_last_pass[attachment] != i) { - //preserve can still be used to keep depth or color from being discarded after use + // Preserve can still be used to keep depth or color from being discarded after use. attachment_last_pass[attachment] = i; preserve_references.push_back(attachment); } } - VkSubpassDescription &subpass = subpasses[i]; + VkSubpassDescription2KHR &subpass = subpasses[i]; + subpass.sType = VK_STRUCTURE_TYPE_SUBPASS_DESCRIPTION_2_KHR; + subpass.pNext = subpass_nextptr; subpass.flags = 0; subpass.pipelineBindPoint = VK_PIPELINE_BIND_POINT_GRAPHICS; + if (p_view_count == 1) { + // VUID-VkSubpassDescription2-multiview-06558: If the multiview feature is not enabled, viewMask must be 0. + subpass.viewMask = 0; + } else { + subpass.viewMask = view_mask; + } subpass.inputAttachmentCount = input_references.size(); if (input_references.size()) { subpass.pInputAttachments = input_references.ptr(); @@ -3748,7 +3871,9 @@ VkRenderPass RenderingDeviceVulkan::_render_pass_create(const Vector<AttachmentF } if (i > 0) { - VkSubpassDependency dependency; + VkSubpassDependency2KHR dependency; + dependency.sType = VK_STRUCTURE_TYPE_SUBPASS_DEPENDENCY_2_KHR; + dependency.pNext = nullptr; dependency.srcSubpass = i - 1; dependency.dstSubpass = i; dependency.srcStageMask = 0; @@ -3758,27 +3883,29 @@ VkRenderPass RenderingDeviceVulkan::_render_pass_create(const Vector<AttachmentF 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; + dependency.viewOffset = 0; subpass_dependencies.push_back(dependency); } /* - // NOTE: Big Mallet Approach -- any layout transition causes a full barrier + // NOTE: Big Mallet Approach -- any layout transition causes a full barrier. if (reference.layout != description.initialLayout) { - // NOTE: this should be smarter based on the texture's knowledge of its 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 texture's knowledge of its 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; } */ } - VkRenderPassCreateInfo render_pass_create_info; - render_pass_create_info.sType = VK_STRUCTURE_TYPE_RENDER_PASS_CREATE_INFO; + VkRenderPassCreateInfo2KHR render_pass_create_info; + render_pass_create_info.sType = VK_STRUCTURE_TYPE_RENDER_PASS_CREATE_INFO_2_KHR; 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 = subpasses.size(); @@ -3795,13 +3922,21 @@ VkRenderPass RenderingDeviceVulkan::_render_pass_create(const Vector<AttachmentF render_pass_create_info.pDependencies = nullptr; } - // These are only used if we use multiview but we need to define them in scope. - const uint32_t view_mask = (1 << p_view_count) - 1; - const uint32_t correlation_mask = (1 << p_view_count) - 1; + if (p_view_count == 1) { + // VUID-VkRenderPassCreateInfo2-viewMask-03057: If the VkSubpassDescription2::viewMask member of all elements of pSubpasses is 0, correlatedViewMaskCount must be 0. + render_pass_create_info.correlatedViewMaskCount = 0; + render_pass_create_info.pCorrelatedViewMasks = nullptr; + } else { + render_pass_create_info.correlatedViewMaskCount = 1; + render_pass_create_info.pCorrelatedViewMasks = &correlation_mask; + } + Vector<uint32_t> view_masks; VkRenderPassMultiviewCreateInfo render_pass_multiview_create_info; if (p_view_count > 1) { + // This may no longer be needed with the new settings already including this. + const VulkanContext::MultiviewCapabilities capabilities = context->get_multiview_capabilities(); // For now this only works with multiview! @@ -3810,7 +3945,7 @@ VkRenderPass RenderingDeviceVulkan::_render_pass_create(const Vector<AttachmentF // 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"); - // Set view masks for each subpass + // Set view masks for each subpass. for (uint32_t i = 0; i < subpasses.size(); i++) { view_masks.push_back(view_mask); } @@ -3828,8 +3963,8 @@ VkRenderPass RenderingDeviceVulkan::_render_pass_create(const Vector<AttachmentF } 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) + "."); + VkResult res = context->vkCreateRenderPass2KHR(device, &render_pass_create_info, nullptr, &render_pass); + ERR_FAIL_COND_V_MSG(res, VK_NULL_HANDLE, "vkCreateRenderPass2KHR failed with error " + itos(res) + "."); return render_pass; } @@ -3848,7 +3983,7 @@ RenderingDevice::FramebufferFormatID RenderingDeviceVulkan::framebuffer_format_c 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) { +RenderingDevice::FramebufferFormatID RenderingDeviceVulkan::framebuffer_format_create_multipass(const Vector<AttachmentFormat> &p_attachments, const Vector<FramebufferPass> &p_passes, uint32_t p_view_count) { _THREAD_SAFE_METHOD_ FramebufferFormatKey key; @@ -3856,16 +3991,16 @@ RenderingDevice::FramebufferFormatID RenderingDeviceVulkan::framebuffer_format_c key.passes = p_passes; key.view_count = p_view_count; - const Map<FramebufferFormatKey, FramebufferFormatID>::Element *E = framebuffer_format_cache.find(key); + const RBMap<FramebufferFormatKey, FramebufferFormatID>::Element *E = framebuffer_format_cache.find(key); if (E) { - //exists, return + // Exists, return. return E->get(); } 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 + 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 + if (render_pass == VK_NULL_HANDLE) { // Was likely invalid. return INVALID_ID; } FramebufferFormatID id = FramebufferFormatID(framebuffer_format_cache.size()) | (FramebufferFormatID(ID_TYPE_FRAMEBUFFER_FORMAT) << FramebufferFormatID(ID_BASE_SHIFT)); @@ -3884,16 +4019,19 @@ RenderingDevice::FramebufferFormatID RenderingDeviceVulkan::framebuffer_format_c FramebufferFormatKey key; key.passes.push_back(FramebufferPass()); - const Map<FramebufferFormatKey, FramebufferFormatID>::Element *E = framebuffer_format_cache.find(key); + const RBMap<FramebufferFormatKey, FramebufferFormatID>::Element *E = framebuffer_format_cache.find(key); if (E) { - //exists, return + // Exists, return. return E->get(); } - VkSubpassDescription subpass; + VkSubpassDescription2KHR subpass; + subpass.sType = VK_STRUCTURE_TYPE_SUBPASS_DESCRIPTION_2_KHR; + subpass.pNext = nullptr; subpass.flags = 0; subpass.pipelineBindPoint = VK_PIPELINE_BIND_POINT_GRAPHICS; - subpass.inputAttachmentCount = 0; //unsupported for now + subpass.viewMask = 0; + subpass.inputAttachmentCount = 0; // Unsupported for now. subpass.pInputAttachments = nullptr; subpass.colorAttachmentCount = 0; subpass.pColorAttachments = nullptr; @@ -3902,8 +4040,8 @@ RenderingDevice::FramebufferFormatID RenderingDeviceVulkan::framebuffer_format_c subpass.preserveAttachmentCount = 0; subpass.pPreserveAttachments = nullptr; - VkRenderPassCreateInfo render_pass_create_info; - render_pass_create_info.sType = VK_STRUCTURE_TYPE_RENDER_PASS_CREATE_INFO; + VkRenderPassCreateInfo2KHR render_pass_create_info; + render_pass_create_info.sType = VK_STRUCTURE_TYPE_RENDER_PASS_CREATE_INFO_2_KHR; render_pass_create_info.pNext = nullptr; render_pass_create_info.flags = 0; render_pass_create_info.attachmentCount = 0; @@ -3912,13 +4050,15 @@ RenderingDevice::FramebufferFormatID RenderingDeviceVulkan::framebuffer_format_c render_pass_create_info.pSubpasses = &subpass; render_pass_create_info.dependencyCount = 0; render_pass_create_info.pDependencies = nullptr; + render_pass_create_info.correlatedViewMaskCount = 0; + render_pass_create_info.pCorrelatedViewMasks = nullptr; VkRenderPass render_pass; - VkResult res = vkCreateRenderPass(device, &render_pass_create_info, nullptr, &render_pass); + VkResult res = context->vkCreateRenderPass2KHR(device, &render_pass_create_info, nullptr, &render_pass); - ERR_FAIL_COND_V_MSG(res, 0, "vkCreateRenderPass for empty fb failed with error " + itos(res) + "."); + ERR_FAIL_COND_V_MSG(res, 0, "vkCreateRenderPass2KHR for empty fb failed with error " + itos(res) + "."); - if (render_pass == VK_NULL_HANDLE) { //was likely invalid + if (render_pass == VK_NULL_HANDLE) { // Was likely invalid. return INVALID_ID; } @@ -3935,11 +4075,11 @@ RenderingDevice::FramebufferFormatID RenderingDeviceVulkan::framebuffer_format_c } 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); + HashMap<FramebufferFormatID, FramebufferFormat>::Iterator 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); + ERR_FAIL_COND_V(p_pass >= uint32_t(E->value.pass_samples.size()), TEXTURE_SAMPLES_1); - return E->get().pass_samples[p_pass]; + return E->value.pass_samples[p_pass]; } /***********************/ @@ -3954,7 +4094,11 @@ RID RenderingDeviceVulkan::framebuffer_create_empty(const Size2i &p_size, Textur framebuffer.size = p_size; framebuffer.view_count = 1; - return framebuffer_owner.make_rid(framebuffer); + RID id = framebuffer_owner.make_rid(framebuffer); +#ifdef DEV_ENABLED + set_resource_name(id, "RID:" + itos(id.get_id())); +#endif + return id; } RID RenderingDeviceVulkan::framebuffer_create(const Vector<RID> &p_texture_attachments, FramebufferFormatID p_format_check, uint32_t p_view_count) { @@ -3969,6 +4113,8 @@ RID RenderingDeviceVulkan::framebuffer_create(const Vector<RID> &p_texture_attac if (texture && texture->usage_flags & TEXTURE_USAGE_DEPTH_STENCIL_ATTACHMENT_BIT) { pass.depth_attachment = i; + } else if (texture && texture->usage_flags & TEXTURE_USAGE_VRS_ATTACHMENT_BIT) { + pass.vrs_attachment = i; } else { pass.color_attachments.push_back(texture ? i : FramebufferPass::ATTACHMENT_UNUSED); } @@ -3980,7 +4126,7 @@ RID RenderingDeviceVulkan::framebuffer_create(const Vector<RID> &p_texture_attac 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) { +RID RenderingDeviceVulkan::framebuffer_create_multipass(const Vector<RID> &p_texture_attachments, const Vector<FramebufferPass> &p_passes, FramebufferFormatID p_format_check, uint32_t p_view_count) { _THREAD_SAFE_METHOD_ Vector<AttachmentFormat> attachments; @@ -3999,6 +4145,10 @@ RID RenderingDeviceVulkan::framebuffer_create_multipass(const Vector<RID> &p_tex size.width = texture->width; size.height = texture->height; size_set = true; + } else if (texture->usage_flags & TEXTURE_USAGE_VRS_ATTACHMENT_BIT) { + // If this is not the first attachement we assume this is used as the VRS attachment. + // In this case this texture will be 1/16th the size of the color attachement. + // So we skip the size check. } else { ERR_FAIL_COND_V_MSG((uint32_t)size.width != texture->width || (uint32_t)size.height != texture->height, RID(), "All textures in a framebuffer should be the same size."); @@ -4028,6 +4178,9 @@ RID RenderingDeviceVulkan::framebuffer_create_multipass(const Vector<RID> &p_tex framebuffer.view_count = p_view_count; RID id = framebuffer_owner.make_rid(framebuffer); +#ifdef DEV_ENABLED + set_resource_name(id, "RID:" + itos(id.get_id())); +#endif for (int i = 0; i < p_texture_attachments.size(); i++) { if (p_texture_attachments[i].is_valid()) { @@ -4047,6 +4200,22 @@ RenderingDevice::FramebufferFormatID RenderingDeviceVulkan::framebuffer_get_form return framebuffer->format_id; } +bool RenderingDeviceVulkan::framebuffer_is_valid(RID p_framebuffer) const { + _THREAD_SAFE_METHOD_ + + return framebuffer_owner.owns(p_framebuffer); +} + +void RenderingDeviceVulkan::framebuffer_set_invalidation_callback(RID p_framebuffer, InvalidationCallback p_callback, void *p_userdata) { + _THREAD_SAFE_METHOD_ + + Framebuffer *framebuffer = framebuffer_owner.get_or_null(p_framebuffer); + ERR_FAIL_COND(!framebuffer); + + framebuffer->invalidated_callback = p_callback; + framebuffer->invalidated_callback_userdata = p_userdata; +} + /*****************/ /**** SAMPLER ****/ /*****************/ @@ -4089,7 +4258,11 @@ RID RenderingDeviceVulkan::sampler_create(const SamplerState &p_state) { VkResult res = vkCreateSampler(device, &sampler_create_info, nullptr, &sampler); ERR_FAIL_COND_V_MSG(res, RID(), "vkCreateSampler failed with error " + itos(res) + "."); - return sampler_owner.make_rid(sampler); + RID id = sampler_owner.make_rid(sampler); +#ifdef DEV_ENABLED + set_resource_name(id, "RID:" + itos(id.get_id())); +#endif + return id; } /**********************/ @@ -4118,10 +4291,14 @@ RID RenderingDeviceVulkan::vertex_buffer_create(uint32_t p_size_bytes, const Vec _buffer_memory_barrier(buffer.buffer, 0, data_size, VK_PIPELINE_STAGE_TRANSFER_BIT, VK_PIPELINE_STAGE_VERTEX_INPUT_BIT, VK_ACCESS_TRANSFER_WRITE_BIT, VK_ACCESS_VERTEX_ATTRIBUTE_READ_BIT, false); } - return vertex_buffer_owner.make_rid(buffer); + RID id = vertex_buffer_owner.make_rid(buffer); +#ifdef DEV_ENABLED + set_resource_name(id, "RID:" + itos(id.get_id())); +#endif + return id; } -// Internally reference counted, this ID is warranted to be unique for the same description, but needs to be freed as many times as it was allocated +// Internally reference counted, this ID is warranted to be unique for the same description, but needs to be freed as many times as it was allocated. RenderingDevice::VertexFormatID RenderingDeviceVulkan::vertex_format_create(const Vector<VertexAttribute> &p_vertex_formats) { _THREAD_SAFE_METHOD_ @@ -4133,12 +4310,12 @@ RenderingDevice::VertexFormatID RenderingDeviceVulkan::vertex_format_create(cons return *idptr; } - //does not exist, create one and cache it + // Does not exist, create one and cache it. VertexDescriptionCache vdcache; vdcache.bindings = memnew_arr(VkVertexInputBindingDescription, p_vertex_formats.size()); vdcache.attributes = memnew_arr(VkVertexInputAttributeDescription, p_vertex_formats.size()); - Set<int> used_locations; + HashSet<int> used_locations; for (int i = 0; i < p_vertex_formats.size(); i++) { ERR_CONTINUE(p_vertex_formats[i].format >= DATA_FORMAT_MAX); ERR_FAIL_COND_V(used_locations.has(p_vertex_formats[i].location), INVALID_ID); @@ -4189,25 +4366,25 @@ RID RenderingDeviceVulkan::vertex_array_create(uint32_t p_vertex_count, VertexFo vertex_array.vertex_count = p_vertex_count; vertex_array.description = p_vertex_format; - vertex_array.max_instances_allowed = 0xFFFFFFFF; //by default as many as you want + vertex_array.max_instances_allowed = 0xFFFFFFFF; // By default as many as you want. for (int i = 0; i < p_src_buffers.size(); i++) { Buffer *buffer = vertex_buffer_owner.get_or_null(p_src_buffers[i]); - //validate with buffer + // Validate with buffer. { const VertexAttribute &atf = vd.vertex_formats[i]; uint32_t element_size = get_format_vertex_size(atf.format); - ERR_FAIL_COND_V(element_size == 0, RID()); //should never happens since this was prevalidated + ERR_FAIL_COND_V(element_size == 0, RID()); // Should never happens since this was prevalidated. if (atf.frequency == VERTEX_FREQUENCY_VERTEX) { - //validate size for regular drawing + // Validate size for regular drawing. uint64_t total_size = uint64_t(atf.stride) * (p_vertex_count - 1) + atf.offset + element_size; ERR_FAIL_COND_V_MSG(total_size > buffer->size, RID(), "Attachment (" + itos(i) + ") will read past the end of the buffer."); } else { - //validate size for instances drawing + // Validate size for instances drawing. uint64_t available = buffer->size - atf.offset; ERR_FAIL_COND_V_MSG(available < element_size, RID(), "Attachment (" + itos(i) + ") uses instancing, but it's just too small."); @@ -4218,7 +4395,7 @@ RID RenderingDeviceVulkan::vertex_array_create(uint32_t p_vertex_count, VertexFo } vertex_array.buffers.push_back(buffer->buffer); - vertex_array.offsets.push_back(0); //offset unused, but passing anyway + vertex_array.offsets.push_back(0); // Offset unused, but passing anyway. } RID id = vertex_array_owner.make_rid(vertex_array); @@ -4253,7 +4430,7 @@ RID RenderingDeviceVulkan::index_buffer_create(uint32_t p_index_count, IndexBuff const uint16_t *index16 = (const uint16_t *)r; for (uint32_t i = 0; i < p_index_count; i++) { if (p_use_restart_indices && index16[i] == 0xFFFF) { - continue; //restart index, ignore + continue; // Restart index, ignore. } index_buffer.max_index = MAX(index16[i], index_buffer.max_index); } @@ -4261,7 +4438,7 @@ RID RenderingDeviceVulkan::index_buffer_create(uint32_t p_index_count, IndexBuff const uint32_t *index32 = (const uint32_t *)r; for (uint32_t i = 0; i < p_index_count; i++) { if (p_use_restart_indices && index32[i] == 0xFFFFFFFF) { - continue; //restart index, ignore + continue; // Restart index, ignore. } index_buffer.max_index = MAX(index32[i], index_buffer.max_index); } @@ -4279,7 +4456,11 @@ RID RenderingDeviceVulkan::index_buffer_create(uint32_t p_index_count, IndexBuff _buffer_update(&index_buffer, 0, r, data_size); _buffer_memory_barrier(index_buffer.buffer, 0, data_size, VK_PIPELINE_STAGE_TRANSFER_BIT, VK_PIPELINE_STAGE_VERTEX_INPUT_BIT, VK_ACCESS_TRANSFER_WRITE_BIT, VK_ACCESS_INDEX_READ_BIT, false); } - return index_buffer_owner.make_rid(index_buffer); + RID id = index_buffer_owner.make_rid(index_buffer); +#ifdef DEV_ENABLED + set_resource_name(id, "RID:" + itos(id.get_id())); +#endif + return id; } RID RenderingDeviceVulkan::index_array_create(RID p_index_buffer, uint32_t p_index_offset, uint32_t p_index_count) { @@ -4342,7 +4523,7 @@ String RenderingDeviceVulkan::_shader_uniform_debug(RID p_shader, int p_set) { if (!ret.is_empty()) { ret += "\n"; } - ret += "Set: " + itos(i) + " Binding: " + itos(ui.binding) + " Type: " + shader_uniform_names[ui.type] + " Length: " + itos(ui.length); + ret += "Set: " + itos(i) + " Binding: " + itos(ui.binding) + " Type: " + shader_uniform_names[ui.type] + " Writable: " + (ui.writable ? "Y" : "N") + " Length: " + itos(ui.length); } } return ret; @@ -4356,7 +4537,7 @@ bool RenderingDeviceVulkan::_uniform_add_binding(Vector<Vector<VkDescriptorSetLa case glslang::EbtSampler: { //print_line("DEBUG: IsSampler"); if (reflection.getType()->getSampler().dim == glslang::EsdBuffer) { - //texture buffers + // Texture buffers. if (reflection.getType()->getSampler().isCombined()) { layout_binding.descriptorType = VK_DESCRIPTOR_TYPE_UNIFORM_TEXEL_BUFFER; info.type = UNIFORM_TYPE_SAMPLER_WITH_TEXTURE_BUFFER; @@ -4456,7 +4637,7 @@ bool RenderingDeviceVulkan::_uniform_add_binding(Vector<Vector<VkDescriptorSetLa } break;*/ default: { if (reflection.getType()->getQualifier().hasOffset() || reflection.name.find(".") != std::string::npos) { - //member of uniform block? + // Member of uniform block? return true; } @@ -4493,10 +4674,10 @@ bool RenderingDeviceVulkan::_uniform_add_binding(Vector<Vector<VkDescriptorSetLa uint32_t binding = reflection.getType()->getQualifier().layoutBinding; if (set < (uint32_t)bindings.size()) { - //check if this already exists + // Check if this already exists. for (int i = 0; i < bindings[set].size(); i++) { if (bindings[set][i].binding == binding) { - //already exists, verify that it's the same type + // Already exists, verify that it's the same type. if (bindings[set][i].descriptorType != layout_binding.descriptorType) { if (r_error) { *r_error = "On shader stage '" + String(shader_stage_names[p_stage]) + "', uniform '" + reflection.name + "' trying to re-use location for set=" + itos(set) + ", binding=" + itos(binding) + " with different uniform type."; @@ -4504,7 +4685,7 @@ bool RenderingDeviceVulkan::_uniform_add_binding(Vector<Vector<VkDescriptorSetLa return false; } - //also, verify that it's the same size + // Also, verify that it's the same size. if (bindings[set][i].descriptorCount != layout_binding.descriptorCount || uniform_infos[set][i].length != info.length) { if (r_error) { *r_error = "On shader stage '" + String(shader_stage_names[p_stage]) + "', uniform '" + reflection.name + "' trying to re-use location for set=" + itos(set) + ", binding=" + itos(binding) + " with different uniform size."; @@ -4512,7 +4693,7 @@ bool RenderingDeviceVulkan::_uniform_add_binding(Vector<Vector<VkDescriptorSetLa return false; } - //just append stage mask and return + // Just append stage mask and return. bindings.write[set].write[i].stageFlags |= shader_stage_masks[p_stage]; uniform_infos.write[set].write[i].stages |= 1 << p_stage; return true; @@ -4521,7 +4702,7 @@ bool RenderingDeviceVulkan::_uniform_add_binding(Vector<Vector<VkDescriptorSetLa } layout_binding.binding = binding; layout_binding.stageFlags = shader_stage_masks[p_stage]; - layout_binding.pImmutableSamplers = nullptr; //no support for this yet + layout_binding.pImmutableSamplers = nullptr; // No support for this yet. info.stages = 1 << p_stage; info.binding = binding; @@ -4540,20 +4721,22 @@ bool RenderingDeviceVulkan::_uniform_add_binding(Vector<Vector<VkDescriptorSetLa } #endif -//version 1: initial -//version 2: Added shader name +// Version 1: initial. +// Version 2: Added shader name. +// Version 3: Added writable. -#define SHADER_BINARY_VERSION 2 +#define SHADER_BINARY_VERSION 3 String RenderingDeviceVulkan::shader_get_binary_cache_key() const { - return "Vulkan-SV" + itos(SHADER_BINARY_VERSION); + return "Vulkan-SV" + itos(SHADER_BINARY_VERSION) + "-" + String(VERSION_NUMBER) + "-" + String(VERSION_HASH); } struct RenderingDeviceVulkanShaderBinaryDataBinding { uint32_t type; uint32_t binding; uint32_t stages; - uint32_t length; //size of arrays (in total elements), or ubos (in bytes * total elements) + uint32_t length; // Size of arrays (in total elements), or ubos (in bytes * total elements). + uint32_t writable; }; struct RenderingDeviceVulkanShaderBinarySpecializationConstant { @@ -4593,7 +4776,7 @@ Vector<uint8_t> RenderingDeviceVulkan::shader_compile_binary_from_spirv(const Ve binary_data.push_constant_size = 0; binary_data.push_constants_vk_stage = 0; - Vector<Vector<RenderingDeviceVulkanShaderBinaryDataBinding>> uniform_info; //set bindings + Vector<Vector<RenderingDeviceVulkanShaderBinaryDataBinding>> uniform_info; // Set bindings. Vector<RenderingDeviceVulkanShaderBinarySpecializationConstant> specialization_constants; uint32_t stages_processed = 0; @@ -4627,7 +4810,7 @@ Vector<uint8_t> RenderingDeviceVulkan::shader_compile_binary_from_spirv(const Ve uint32_t stage = p_spirv[i].shader_stage; if (binding_count > 0) { - //Parse bindings + // Parse bindings. Vector<SpvReflectDescriptorBinding *> bindings; bindings.resize(binding_count); @@ -4643,6 +4826,7 @@ Vector<uint8_t> RenderingDeviceVulkan::shader_compile_binary_from_spirv(const Ve bool need_array_dimensions = false; bool need_block_size = false; + bool may_be_writable = false; switch (binding.descriptor_type) { case SPV_REFLECT_DESCRIPTOR_TYPE_SAMPLER: { @@ -4660,6 +4844,7 @@ Vector<uint8_t> RenderingDeviceVulkan::shader_compile_binary_from_spirv(const Ve case SPV_REFLECT_DESCRIPTOR_TYPE_STORAGE_IMAGE: { info.type = UNIFORM_TYPE_IMAGE; need_array_dimensions = true; + may_be_writable = true; } break; case SPV_REFLECT_DESCRIPTOR_TYPE_UNIFORM_TEXEL_BUFFER: { info.type = UNIFORM_TYPE_TEXTURE_BUFFER; @@ -4668,6 +4853,7 @@ Vector<uint8_t> RenderingDeviceVulkan::shader_compile_binary_from_spirv(const Ve case SPV_REFLECT_DESCRIPTOR_TYPE_STORAGE_TEXEL_BUFFER: { info.type = UNIFORM_TYPE_IMAGE_BUFFER; need_array_dimensions = true; + may_be_writable = true; } break; case SPV_REFLECT_DESCRIPTOR_TYPE_UNIFORM_BUFFER: { info.type = UNIFORM_TYPE_UNIFORM_BUFFER; @@ -4676,6 +4862,7 @@ Vector<uint8_t> RenderingDeviceVulkan::shader_compile_binary_from_spirv(const Ve case SPV_REFLECT_DESCRIPTOR_TYPE_STORAGE_BUFFER: { info.type = UNIFORM_TYPE_STORAGE_BUFFER; need_block_size = true; + may_be_writable = true; } break; case SPV_REFLECT_DESCRIPTOR_TYPE_UNIFORM_BUFFER_DYNAMIC: { ERR_PRINT("Dynamic uniform buffer not supported."); @@ -4714,6 +4901,12 @@ Vector<uint8_t> RenderingDeviceVulkan::shader_compile_binary_from_spirv(const Ve info.length = 0; } + if (may_be_writable) { + info.writable = !(binding.type_description->decoration_flags & SPV_REFLECT_DECORATION_NON_WRITABLE) && !(binding.block.decoration_flags & SPV_REFLECT_DECORATION_NON_WRITABLE); + } else { + info.writable = false; + } + info.binding = binding.binding; uint32_t set = binding.set; @@ -4724,26 +4917,31 @@ Vector<uint8_t> RenderingDeviceVulkan::shader_compile_binary_from_spirv(const Ve "On shader stage '" + String(shader_stage_names[stage]) + "', uniform '" + binding.name + "' uses a set (" + itos(set) + ") index larger than what is supported by the hardware (" + itos(limits.maxBoundDescriptorSets) + ")."); if (set < (uint32_t)uniform_info.size()) { - //check if this already exists + // Check if this already exists. bool exists = false; for (int k = 0; k < uniform_info[set].size(); k++) { if (uniform_info[set][k].binding == (uint32_t)info.binding) { - //already exists, verify that it's the same type + // Already exists, verify that it's the same type. ERR_FAIL_COND_V_MSG(uniform_info[set][k].type != info.type, Vector<uint8_t>(), "On shader stage '" + String(shader_stage_names[stage]) + "', uniform '" + binding.name + "' trying to re-use location for set=" + itos(set) + ", binding=" + itos(info.binding) + " with different uniform type."); - //also, verify that it's the same size + // Also, verify that it's the same size. ERR_FAIL_COND_V_MSG(uniform_info[set][k].length != info.length, Vector<uint8_t>(), "On shader stage '" + String(shader_stage_names[stage]) + "', uniform '" + binding.name + "' trying to re-use location for set=" + itos(set) + ", binding=" + itos(info.binding) + " with different uniform size."); - //just append stage mask and return + // Also, verify that it has the same writability. + ERR_FAIL_COND_V_MSG(uniform_info[set][k].writable != info.writable, Vector<uint8_t>(), + "On shader stage '" + String(shader_stage_names[stage]) + "', uniform '" + binding.name + "' trying to re-use location for set=" + itos(set) + ", binding=" + itos(info.binding) + " with different writability."); + + // Just append stage mask and return. uniform_info.write[set].write[k].stages |= 1 << stage; exists = true; + break; } } if (exists) { - continue; //merged + continue; // Merged. } } @@ -4758,7 +4956,7 @@ Vector<uint8_t> RenderingDeviceVulkan::shader_compile_binary_from_spirv(const Ve } { - //specialization constants + // Specialization constants. uint32_t sc_count = 0; result = spvReflectEnumerateSpecializationConstants(&module, &sc_count, nullptr); @@ -4779,7 +4977,7 @@ Vector<uint8_t> RenderingDeviceVulkan::shader_compile_binary_from_spirv(const Ve SpvReflectSpecializationConstant *spc = spec_constants[j]; sconst.constant_id = spc->constant_id; - sconst.int_value = 0.0; // clear previous value JIC + sconst.int_value = 0.0; // Clear previous value JIC. switch (spc->constant_type) { case SPV_REFLECT_SPECIALIZATION_CONSTANT_BOOL: { sconst.type = PIPELINE_SPECIALIZATION_CONSTANT_TYPE_BOOL; @@ -4829,7 +5027,7 @@ Vector<uint8_t> RenderingDeviceVulkan::shader_compile_binary_from_spirv(const Ve "Reflection of SPIR-V shader stage '" + String(shader_stage_names[p_spirv[i].shader_stage]) + "' failed obtaining input variables."); for (uint32_t j = 0; j < iv_count; j++) { - if (input_vars[j] && input_vars[j]->decoration_flags == 0) { //regular input + if (input_vars[j] && input_vars[j]->decoration_flags == 0) { // Regular input. binary_data.vertex_input_mask |= (1 << uint32_t(input_vars[j]->location)); } } @@ -4898,7 +5096,7 @@ Vector<uint8_t> RenderingDeviceVulkan::shader_compile_binary_from_spirv(const Ve Vector<Vector<uint8_t>> compressed_stages; Vector<uint32_t> smolv_size; - Vector<uint32_t> zstd_size; //if 0, stdno t used + Vector<uint32_t> zstd_size; // If 0, zstd not used. uint32_t stages_binary_size = 0; @@ -4910,7 +5108,7 @@ Vector<uint8_t> RenderingDeviceVulkan::shader_compile_binary_from_spirv(const Ve ERR_FAIL_V_MSG(Vector<uint8_t>(), "Error compressing shader stage :" + String(shader_stage_names[p_spirv[i].shader_stage])); } else { smolv_size.push_back(smolv.size()); - { //zstd + { // zstd. Vector<uint8_t> zstd; zstd.resize(Compression::get_max_compressed_buffer_size(smolv.size(), Compression::MODE_ZSTD)); int dst_size = Compression::compress(zstd.ptrw(), &smolv[0], smolv.size(), Compression::MODE_ZSTD); @@ -4923,7 +5121,7 @@ Vector<uint8_t> RenderingDeviceVulkan::shader_compile_binary_from_spirv(const Ve Vector<uint8_t> smv; smv.resize(smolv.size()); memcpy(smv.ptrw(), &smolv[0], smolv.size()); - zstd_size.push_back(0); //not using zstd + zstd_size.push_back(0); // Not using zstd. compressed_stages.push_back(smv); } } @@ -4943,12 +5141,12 @@ Vector<uint8_t> RenderingDeviceVulkan::shader_compile_binary_from_spirv(const Ve binary_data.shader_name_len = shader_name_utf.length(); - uint32_t total_size = sizeof(uint32_t) * 3; //header + version + main datasize; + uint32_t total_size = sizeof(uint32_t) * 3; // Header + version + main datasize;. total_size += sizeof(RenderingDeviceVulkanShaderBinaryData); total_size += binary_data.shader_name_len; - if ((binary_data.shader_name_len % 4) != 0) { //alignment rules are really strange + if ((binary_data.shader_name_len % 4) != 0) { // Alignment rules are really strange. total_size += 4 - (binary_data.shader_name_len % 4); } @@ -4959,7 +5157,7 @@ Vector<uint8_t> RenderingDeviceVulkan::shader_compile_binary_from_spirv(const Ve total_size += sizeof(RenderingDeviceVulkanShaderBinarySpecializationConstant) * specialization_constants.size(); - total_size += compressed_stages.size() * sizeof(uint32_t) * 3; //sizes + total_size += compressed_stages.size() * sizeof(uint32_t) * 3; // Sizes. total_size += stages_binary_size; Vector<uint8_t> ret; @@ -4970,7 +5168,7 @@ Vector<uint8_t> RenderingDeviceVulkan::shader_compile_binary_from_spirv(const Ve binptr[0] = 'G'; binptr[1] = 'V'; binptr[2] = 'B'; - binptr[3] = 'D'; //godot vulkan binary data + binptr[3] = 'D'; // Godot vulkan binary data. offset += 4; encode_uint32(SHADER_BINARY_VERSION, binptr + offset); offset += sizeof(uint32_t); @@ -4981,7 +5179,7 @@ Vector<uint8_t> RenderingDeviceVulkan::shader_compile_binary_from_spirv(const Ve memcpy(binptr + offset, shader_name_utf.ptr(), binary_data.shader_name_len); offset += binary_data.shader_name_len; - if ((binary_data.shader_name_len % 4) != 0) { //alignment rules are really strange + if ((binary_data.shader_name_len % 4) != 0) { // Alignment rules are really strange. offset += 4 - (binary_data.shader_name_len % 4); } @@ -5029,12 +5227,12 @@ RID RenderingDeviceVulkan::shader_create_from_bytecode(const Vector<uint8_t> &p_ uint32_t binsize = p_shader_binary.size(); uint32_t read_offset = 0; - //consistency check + // Consistency check. ERR_FAIL_COND_V(binsize < sizeof(uint32_t) * 3 + sizeof(RenderingDeviceVulkanShaderBinaryData), RID()); ERR_FAIL_COND_V(binptr[0] != 'G' || binptr[1] != 'V' || binptr[2] != 'B' || binptr[3] != 'D', RID()); uint32_t bin_version = decode_uint32(binptr + 4); - ERR_FAIL_COND_V(bin_version > SHADER_BINARY_VERSION, RID()); + ERR_FAIL_COND_V(bin_version != SHADER_BINARY_VERSION, RID()); uint32_t bin_data_size = decode_uint32(binptr + 8); @@ -5059,7 +5257,7 @@ RID RenderingDeviceVulkan::shader_create_from_bytecode(const Vector<uint8_t> &p_ if (binary_data.shader_name_len) { name.parse_utf8((const char *)(binptr + read_offset), binary_data.shader_name_len); read_offset += binary_data.shader_name_len; - if ((binary_data.shader_name_len % 4) != 0) { //alignment rules are really strange + if ((binary_data.shader_name_len % 4) != 0) { // Alignment rules are really strange. read_offset += 4 - (binary_data.shader_name_len % 4); } } @@ -5081,6 +5279,7 @@ RID RenderingDeviceVulkan::shader_create_from_bytecode(const Vector<uint8_t> &p_ for (uint32_t j = 0; j < set_count; j++) { UniformInfo info; info.type = UniformType(set_ptr[j].type); + info.writable = set_ptr[j].writable; info.length = set_ptr[j].length; info.binding = set_ptr[j].binding; info.stages = set_ptr[j].stages; @@ -5175,7 +5374,7 @@ RID RenderingDeviceVulkan::shader_create_from_bytecode(const Vector<uint8_t> &p_ const uint8_t *src_smolv = nullptr; if (zstd_size > 0) { - //decompress to smolv + // Decompress to smolv. smolv.resize(smolv_size); int dec_smolv_size = Compression::decompress(smolv.ptrw(), smolv.size(), binptr + read_offset, zstd_size, Compression::MODE_ZSTD); ERR_FAIL_COND_V(dec_smolv_size != (int32_t)smolv_size, RID()); @@ -5204,7 +5403,7 @@ RID RenderingDeviceVulkan::shader_create_from_bytecode(const Vector<uint8_t> &p_ ERR_FAIL_COND_V(read_offset != binsize, RID()); - //all good, let's create modules + // All good, let's create modules. _THREAD_SAFE_METHOD_ @@ -5260,11 +5459,11 @@ RID RenderingDeviceVulkan::shader_create_from_bytecode(const Vector<uint8_t> &p_ shader.pipeline_stages.push_back(shader_stage); } - //proceed to create descriptor sets + // Proceed to create descriptor sets. if (success) { for (int i = 0; i < set_bindings.size(); i++) { - //empty ones are fine if they were not used according to spec (binding count will be 0) + // Empty ones are fine if they were not used according to spec (binding count will be 0). VkDescriptorSetLayoutCreateInfo layout_create_info; layout_create_info.sType = VK_STRUCTURE_TYPE_DESCRIPTOR_SET_LAYOUT_CREATE_INFO; layout_create_info.pNext = nullptr; @@ -5283,16 +5482,16 @@ RID RenderingDeviceVulkan::shader_create_from_bytecode(const Vector<uint8_t> &p_ Shader::Set set; set.descriptor_set_layout = layout; set.uniform_info = uniform_info[i]; - //sort and hash + // Sort and hash. set.uniform_info.sort(); - uint32_t format = 0; //no format, default + uint32_t format = 0; // No format, default. if (set.uniform_info.size()) { - //has data, needs an actual format; + // Has data, needs an actual format. UniformSetFormat usformat; usformat.uniform_info = set.uniform_info; - Map<UniformSetFormat, uint32_t>::Element *E = uniform_set_format_cache.find(usformat); + RBMap<UniformSetFormat, uint32_t>::Element *E = uniform_set_format_cache.find(usformat); if (E) { format = E->get(); } else { @@ -5307,7 +5506,7 @@ RID RenderingDeviceVulkan::shader_create_from_bytecode(const Vector<uint8_t> &p_ } if (success) { - //create pipeline layout + // Create pipeline layout. VkPipelineLayoutCreateInfo pipeline_layout_create_info; pipeline_layout_create_info.sType = VK_STRUCTURE_TYPE_PIPELINE_LAYOUT_CREATE_INFO; pipeline_layout_create_info.pNext = nullptr; @@ -5346,7 +5545,7 @@ RID RenderingDeviceVulkan::shader_create_from_bytecode(const Vector<uint8_t> &p_ } if (!success) { - //clean up if failed + // Clean up if failed. for (int i = 0; i < shader.pipeline_stages.size(); i++) { vkDestroyShaderModule(device, shader.pipeline_stages[i].module, nullptr); } @@ -5358,7 +5557,11 @@ RID RenderingDeviceVulkan::shader_create_from_bytecode(const Vector<uint8_t> &p_ ERR_FAIL_V_MSG(RID(), error_text); } - return shader_owner.make_rid(shader); + RID id = shader_owner.make_rid(shader); +#ifdef DEV_ENABLED + set_resource_name(id, "RID:" + itos(id.get_id())); +#endif + return id; } uint32_t RenderingDeviceVulkan::shader_get_vertex_input_attribute_mask(RID p_shader) { @@ -5391,7 +5594,11 @@ RID RenderingDeviceVulkan::uniform_buffer_create(uint32_t p_size_bytes, const Ve _buffer_update(&buffer, 0, r, data_size); _buffer_memory_barrier(buffer.buffer, 0, data_size, VK_PIPELINE_STAGE_TRANSFER_BIT, VK_PIPELINE_STAGE_VERTEX_SHADER_BIT | VK_PIPELINE_STAGE_FRAGMENT_SHADER_BIT | VK_PIPELINE_STAGE_COMPUTE_SHADER_BIT, VK_ACCESS_TRANSFER_WRITE_BIT, VK_ACCESS_UNIFORM_READ_BIT, false); } - return uniform_buffer_owner.make_rid(buffer); + RID id = uniform_buffer_owner.make_rid(buffer); +#ifdef DEV_ENABLED + set_resource_name(id, "RID:" + itos(id.get_id())); +#endif + return id; } RID RenderingDeviceVulkan::storage_buffer_create(uint32_t p_size_bytes, const Vector<uint8_t> &p_data, uint32_t p_usage) { @@ -5404,7 +5611,6 @@ RID RenderingDeviceVulkan::storage_buffer_create(uint32_t p_size_bytes, const Ve ERR_FAIL_COND_V(p_data.size() && (uint32_t)p_data.size() != p_size_bytes, RID()); Buffer buffer; - buffer.usage = p_usage; uint32_t flags = VK_BUFFER_USAGE_TRANSFER_SRC_BIT | VK_BUFFER_USAGE_TRANSFER_DST_BIT | VK_BUFFER_USAGE_STORAGE_BUFFER_BIT; if (p_usage & STORAGE_BUFFER_USAGE_DISPATCH_INDIRECT) { flags |= VK_BUFFER_USAGE_INDIRECT_BUFFER_BIT; @@ -5462,36 +5668,40 @@ RID RenderingDeviceVulkan::texture_buffer_create(uint32_t p_size_elements, DataF ERR_FAIL_V_MSG(RID(), "Unable to create buffer view, error " + itos(res) + "."); } - //allocate the view - return texture_buffer_owner.make_rid(texture_buffer); + // Allocate the view. + RID id = texture_buffer_owner.make_rid(texture_buffer); +#ifdef DEV_ENABLED + set_resource_name(id, "RID:" + itos(id.get_id())); +#endif + return id; } RenderingDeviceVulkan::DescriptorPool *RenderingDeviceVulkan::_descriptor_pool_allocate(const DescriptorPoolKey &p_key) { if (!descriptor_pools.has(p_key)) { - descriptor_pools[p_key] = Set<DescriptorPool *>(); + descriptor_pools[p_key] = HashSet<DescriptorPool *>(); } DescriptorPool *pool = nullptr; - for (Set<DescriptorPool *>::Element *E = descriptor_pools[p_key].front(); E; E = E->next()) { - if (E->get()->usage < max_descriptors_per_pool) { - pool = E->get(); + for (DescriptorPool *E : descriptor_pools[p_key]) { + if (E->usage < max_descriptors_per_pool) { + pool = E; break; } } if (!pool) { - //create a new one + // Create a new one. pool = memnew(DescriptorPool); pool->usage = 0; VkDescriptorPoolCreateInfo descriptor_pool_create_info; descriptor_pool_create_info.sType = VK_STRUCTURE_TYPE_DESCRIPTOR_POOL_CREATE_INFO; descriptor_pool_create_info.pNext = nullptr; - descriptor_pool_create_info.flags = VK_DESCRIPTOR_POOL_CREATE_FREE_DESCRIPTOR_SET_BIT; // can't think how somebody may NOT need this flag.. + descriptor_pool_create_info.flags = VK_DESCRIPTOR_POOL_CREATE_FREE_DESCRIPTOR_SET_BIT; // Can't think how somebody may NOT need this flag. descriptor_pool_create_info.maxSets = max_descriptors_per_pool; Vector<VkDescriptorPoolSize> sizes; - //here comes more vulkan API strangeness + // Here comes more vulkan API strangeness. if (p_key.uniform_type[UNIFORM_TYPE_SAMPLER]) { VkDescriptorPoolSize s; @@ -5591,7 +5801,7 @@ RID RenderingDeviceVulkan::uniform_set_create(const Vector<Uniform> &p_uniforms, ERR_FAIL_COND_V_MSG(p_shader_set >= (uint32_t)shader->sets.size() || shader->sets[p_shader_set].uniform_info.size() == 0, RID(), "Desired set (" + itos(p_shader_set) + ") not used by shader."); - //see that all sets in shader are satisfied + // See that all sets in shader are satisfied. const Shader::Set &set = shader->sets[p_shader_set]; @@ -5604,11 +5814,11 @@ RID RenderingDeviceVulkan::uniform_set_create(const Vector<Uniform> &p_uniforms, Vector<VkWriteDescriptorSet> writes; DescriptorPoolKey pool_key; - //to keep them alive until update call + // To keep them alive until update call. List<Vector<VkDescriptorBufferInfo>> buffer_infos; List<Vector<VkBufferView>> buffer_views; List<Vector<VkDescriptorImageInfo>> image_infos; - //used for verification to make sure a uniform set does not use a framebuffer bound texture + // Used for verification to make sure a uniform set does not use a framebuffer bound texture. LocalVector<UniformSet::AttachableTexture> attachable_textures; Vector<Texture *> mutable_sampled_textures; Vector<Texture *> mutable_storage_textures; @@ -5629,14 +5839,14 @@ RID RenderingDeviceVulkan::uniform_set_create(const Vector<Uniform> &p_uniforms, ERR_FAIL_COND_V_MSG(uniform.uniform_type != set_uniform.type, RID(), "Mismatch uniform type for binding (" + itos(set_uniform.binding) + "), set (" + itos(p_shader_set) + "). Expected '" + shader_uniform_names[set_uniform.type] + "', supplied: '" + shader_uniform_names[uniform.uniform_type] + "'."); - VkWriteDescriptorSet write; //common header + VkWriteDescriptorSet write; // Common header. write.sType = VK_STRUCTURE_TYPE_WRITE_DESCRIPTOR_SET; write.pNext = nullptr; - write.dstSet = VK_NULL_HANDLE; //will assign afterwards when everything is valid + write.dstSet = VK_NULL_HANDLE; // Will assign afterwards when everything is valid. write.dstBinding = set_uniform.binding; write.dstArrayElement = 0; write.descriptorCount = 0; - write.descriptorType = VK_DESCRIPTOR_TYPE_MAX_ENUM; //Invalid value. + write.descriptorType = VK_DESCRIPTOR_TYPE_MAX_ENUM; // Invalid value. write.pImageInfo = nullptr; write.pBufferInfo = nullptr; write.pTexelBufferView = nullptr; @@ -5709,12 +5919,12 @@ RID RenderingDeviceVulkan::uniform_set_create(const Vector<Uniform> &p_uniforms, } if (texture->usage_flags & TEXTURE_USAGE_STORAGE_BIT) { - //can also be used as storage, add to mutable sampled + // Can also be used as storage, add to mutable sampled. mutable_sampled_textures.push_back(texture); } if (texture->owner.is_valid()) { texture = texture_owner.get_or_null(texture->owner); - ERR_FAIL_COND_V(!texture, RID()); //bug, should never happen + ERR_FAIL_COND_V(!texture, RID()); // Bug, should never happen. } img_info.imageLayout = VK_IMAGE_LAYOUT_SHADER_READ_ONLY_OPTIMAL; @@ -5762,13 +5972,13 @@ RID RenderingDeviceVulkan::uniform_set_create(const Vector<Uniform> &p_uniforms, } if (texture->usage_flags & TEXTURE_USAGE_STORAGE_BIT) { - //can also be used as storage, add to mutable sampled + // Can also be used as storage, add to mutable sampled. mutable_sampled_textures.push_back(texture); } if (texture->owner.is_valid()) { texture = texture_owner.get_or_null(texture->owner); - ERR_FAIL_COND_V(!texture, RID()); //bug, should never happen + ERR_FAIL_COND_V(!texture, RID()); // Bug, should never happen. } img_info.imageLayout = VK_IMAGE_LAYOUT_SHADER_READ_ONLY_OPTIMAL; @@ -5810,13 +6020,13 @@ RID RenderingDeviceVulkan::uniform_set_create(const Vector<Uniform> &p_uniforms, img_info.imageView = texture->view; if (texture->usage_flags & TEXTURE_USAGE_SAMPLING_BIT) { - //can also be used as storage, add to mutable sampled + // Can also be used as storage, add to mutable sampled. mutable_storage_textures.push_back(texture); } if (texture->owner.is_valid()) { texture = texture_owner.get_or_null(texture->owner); - ERR_FAIL_COND_V(!texture, RID()); //bug, should never happen + ERR_FAIL_COND_V(!texture, RID()); // Bug, should never happen. } img_info.imageLayout = VK_IMAGE_LAYOUT_GENERAL; @@ -5906,7 +6116,7 @@ RID RenderingDeviceVulkan::uniform_set_create(const Vector<Uniform> &p_uniforms, type_size = uniform.get_id_count() / 2; } break; case UNIFORM_TYPE_IMAGE_BUFFER: { - //todo + // Todo. } break; case UNIFORM_TYPE_UNIFORM_BUFFER: { @@ -5942,7 +6152,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 it's 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) + ")."); @@ -5981,7 +6191,7 @@ RID RenderingDeviceVulkan::uniform_set_create(const Vector<Uniform> &p_uniforms, if (texture->owner.is_valid()) { texture = texture_owner.get_or_null(texture->owner); - ERR_FAIL_COND_V(!texture, RID()); //bug, should never happen + ERR_FAIL_COND_V(!texture, RID()); // Bug, should never happen. } img_info.imageLayout = VK_IMAGE_LAYOUT_SHADER_READ_ONLY_OPTIMAL; @@ -6009,7 +6219,7 @@ RID RenderingDeviceVulkan::uniform_set_create(const Vector<Uniform> &p_uniforms, pool_key.uniform_type[set_uniform.type] += type_size; } - //need a descriptor pool + // Need a descriptor pool. DescriptorPool *pool = _descriptor_pool_allocate(pool_key); ERR_FAIL_COND_V(!pool, RID()); @@ -6026,7 +6236,7 @@ RID RenderingDeviceVulkan::uniform_set_create(const Vector<Uniform> &p_uniforms, VkResult res = vkAllocateDescriptorSets(device, &descriptor_set_allocate_info, &descriptor_set); if (res) { - _descriptor_pool_free(pool_key, pool); // meh + _descriptor_pool_free(pool_key, pool); // Meh. ERR_FAIL_V_MSG(RID(), "Cannot allocate descriptor sets, error " + itos(res) + "."); } @@ -6042,7 +6252,10 @@ RID RenderingDeviceVulkan::uniform_set_create(const Vector<Uniform> &p_uniforms, uniform_set.shader_id = p_shader; RID id = uniform_set_owner.make_rid(uniform_set); - //add dependencies +#ifdef DEV_ENABLED + set_resource_name(id, "RID:" + itos(id.get_id())); +#endif + // Add dependencies. _add_dependency(id, p_shader); for (uint32_t i = 0; i < uniform_count; i++) { const Uniform &uniform = uniforms[i]; @@ -6052,7 +6265,7 @@ RID RenderingDeviceVulkan::uniform_set_create(const Vector<Uniform> &p_uniforms, } } - //write the contents + // Write the contents. if (writes.size()) { for (int i = 0; i < writes.size(); i++) { writes.write[i].dstSet = descriptor_set; @@ -6067,7 +6280,7 @@ 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) { +void RenderingDeviceVulkan::uniform_set_set_invalidation_callback(RID p_uniform_set, InvalidationCallback p_callback, void *p_userdata) { UniformSet *us = uniform_set_owner.get_or_null(p_uniform_set); ERR_FAIL_COND(!us); us->invalidated_callback = p_callback; @@ -6085,7 +6298,7 @@ Error RenderingDeviceVulkan::buffer_update(RID p_buffer, uint32_t p_offset, uint VkPipelineStageFlags dst_stage_mask = 0; VkAccessFlags dst_access = 0; if (p_post_barrier & BARRIER_MASK_TRANSFER) { - // Protect subsequent updates... + // Protect subsequent updates. dst_stage_mask = VK_PIPELINE_STAGE_TRANSFER_BIT; dst_access = VK_ACCESS_TRANSFER_WRITE_BIT; } @@ -6097,7 +6310,7 @@ Error RenderingDeviceVulkan::buffer_update(RID p_buffer, uint32_t p_offset, uint ERR_FAIL_COND_V_MSG(p_offset + p_size > buffer->size, ERR_INVALID_PARAMETER, "Attempted to write buffer (" + itos((p_offset + p_size) - buffer->size) + " bytes) past the end."); - // no barrier should be needed here + // No barrier should be needed here. // _buffer_memory_barrier(buffer->buffer, p_offset, p_size, dst_stage_mask, VK_PIPELINE_STAGE_TRANSFER_BIT, dst_access, VK_ACCESS_TRANSFER_WRITE_BIT, true); Error err = _buffer_update(buffer, p_offset, (uint8_t *)p_data, p_size, p_post_barrier); @@ -6133,7 +6346,7 @@ Error RenderingDeviceVulkan::buffer_clear(RID p_buffer, uint32_t p_offset, uint3 VkPipelineStageFlags dst_stage_mask = 0; VkAccessFlags dst_access = 0; if (p_post_barrier & BARRIER_MASK_TRANSFER) { - // Protect subsequent updates... + // Protect subsequent updates. dst_stage_mask = VK_PIPELINE_STAGE_TRANSFER_BIT; dst_access = VK_ACCESS_TRANSFER_WRITE_BIT; } @@ -6146,7 +6359,7 @@ Error RenderingDeviceVulkan::buffer_clear(RID p_buffer, uint32_t p_offset, uint3 ERR_FAIL_COND_V_MSG(p_offset + p_size > buffer->size, ERR_INVALID_PARAMETER, "Attempted to write buffer (" + itos((p_offset + p_size) - buffer->size) + " bytes) past the end."); - // should not be needed + // Should not be needed. // _buffer_memory_barrier(buffer->buffer, p_offset, p_size, dst_stage_mask, VK_PIPELINE_STAGE_TRANSFER_BIT, dst_access, VK_ACCESS_TRANSFER_WRITE_BIT, p_post_barrier); vkCmdFillBuffer(frames[frame].draw_command_buffer, buffer->buffer, p_offset, p_size, 0); @@ -6167,10 +6380,10 @@ Error RenderingDeviceVulkan::buffer_clear(RID p_buffer, uint32_t p_offset, uint3 Vector<uint8_t> RenderingDeviceVulkan::buffer_get_data(RID p_buffer) { _THREAD_SAFE_METHOD_ - // It could be this buffer was just created + // It could be this buffer was just created. VkPipelineShaderStageCreateFlags src_stage_mask = VK_PIPELINE_STAGE_TRANSFER_BIT; VkAccessFlags src_access_mask = VK_ACCESS_TRANSFER_WRITE_BIT; - // Get the vulkan buffer and the potential stage/access possible + // Get the vulkan buffer and the potential stage/access possible. Buffer *buffer = _get_buffer_from_owner(p_buffer, src_stage_mask, src_access_mask, BARRIER_MASK_ALL); if (!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."); @@ -6187,8 +6400,8 @@ Vector<uint8_t> RenderingDeviceVulkan::buffer_get_data(RID p_buffer) { region.srcOffset = 0; region.dstOffset = 0; region.size = buffer->size; - vkCmdCopyBuffer(command_buffer, buffer->buffer, tmp_buffer.buffer, 1, ®ion); //dst buffer is in CPU, but I wonder if src buffer needs a barrier for this.. - //flush everything so memory can be safely mapped + vkCmdCopyBuffer(command_buffer, buffer->buffer, tmp_buffer.buffer, 1, ®ion); // Dst buffer is in CPU, but I wonder if src buffer needs a barrier for this. + // Flush everything so memory can be safely mapped. _flush(true); void *buffer_mem; @@ -6216,7 +6429,7 @@ Vector<uint8_t> RenderingDeviceVulkan::buffer_get_data(RID p_buffer) { 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 + // Needs a shader. Shader *shader = shader_owner.get_or_null(p_shader); ERR_FAIL_COND_V(!shader, RID()); @@ -6224,13 +6437,13 @@ RID RenderingDeviceVulkan::render_pipeline_create(RID p_shader, FramebufferForma "Compute shaders can't be used in render pipelines"); if (p_framebuffer_format == INVALID_ID) { - //if nothing provided, use an empty one (no attachments) + // If nothing provided, use an empty one (no attachments). p_framebuffer_format = framebuffer_format_create(Vector<AttachmentFormat>()); } ERR_FAIL_COND_V(!framebuffer_formats.has(p_framebuffer_format), RID()); const FramebufferFormat &fb_format = framebuffer_formats[p_framebuffer_format]; - { //validate shader vs framebuffer + { // Validate shader vs framebuffer. 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]; @@ -6243,17 +6456,17 @@ RID RenderingDeviceVulkan::render_pipeline_create(RID p_shader, FramebufferForma 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 + // Vertex. VkPipelineVertexInputStateCreateInfo pipeline_vertex_input_state_create_info; if (p_vertex_format != INVALID_ID) { - //uses vertices, else it does not + // Uses vertices, else it does not. ERR_FAIL_COND_V(!vertex_formats.has(p_vertex_format), RID()); const VertexDescriptionCache &vd = vertex_formats[p_vertex_format]; pipeline_vertex_input_state_create_info = vd.create_info; - //validate with inputs + // Validate with inputs. for (uint32_t i = 0; i < 32; i++) { if (!(shader->vertex_input_mask & (1UL << i))) { continue; @@ -6270,7 +6483,7 @@ RID RenderingDeviceVulkan::render_pipeline_create(RID p_shader, FramebufferForma } } else { - //does not use vertices + // Does not use vertices. pipeline_vertex_input_state_create_info.sType = VK_STRUCTURE_TYPE_PIPELINE_VERTEX_INPUT_STATE_CREATE_INFO; pipeline_vertex_input_state_create_info.pNext = nullptr; pipeline_vertex_input_state_create_info.flags = 0; @@ -6282,7 +6495,7 @@ RID RenderingDeviceVulkan::render_pipeline_create(RID p_shader, FramebufferForma ERR_FAIL_COND_V_MSG(shader->vertex_input_mask != 0, RID(), "Shader contains vertex inputs, but no vertex input description was provided for pipeline creation."); } - //input assembly + // Input assembly. ERR_FAIL_INDEX_V(p_render_primitive, RENDER_PRIMITIVE_MAX, RID()); @@ -6308,7 +6521,7 @@ RID RenderingDeviceVulkan::render_pipeline_create(RID p_shader, FramebufferForma input_assembly_create_info.topology = topology_list[p_render_primitive]; input_assembly_create_info.primitiveRestartEnable = (p_render_primitive == RENDER_PRIMITIVE_TRIANGLE_STRIPS_WITH_RESTART_INDEX); - //tessellation + // Tessellation. VkPipelineTessellationStateCreateInfo tessellation_create_info; tessellation_create_info.sType = VK_STRUCTURE_TYPE_PIPELINE_TESSELLATION_STATE_CREATE_INFO; tessellation_create_info.pNext = nullptr; @@ -6320,12 +6533,12 @@ RID RenderingDeviceVulkan::render_pipeline_create(RID p_shader, FramebufferForma viewport_state_create_info.sType = VK_STRUCTURE_TYPE_PIPELINE_VIEWPORT_STATE_CREATE_INFO; viewport_state_create_info.pNext = nullptr; viewport_state_create_info.flags = 0; - viewport_state_create_info.viewportCount = 1; //if VR extensions are supported at some point, this will have to be customizable in the framebuffer format + viewport_state_create_info.viewportCount = 1; // If VR extensions are supported at some point, this will have to be customizable in the framebuffer format. viewport_state_create_info.pViewports = nullptr; viewport_state_create_info.scissorCount = 1; viewport_state_create_info.pScissors = nullptr; - //rasterization + // Rasterization. VkPipelineRasterizationStateCreateInfo rasterization_state_create_info; rasterization_state_create_info.sType = VK_STRUCTURE_TYPE_PIPELINE_RASTERIZATION_STATE_CREATE_INFO; rasterization_state_create_info.pNext = nullptr; @@ -6348,7 +6561,7 @@ RID RenderingDeviceVulkan::render_pipeline_create(RID p_shader, FramebufferForma rasterization_state_create_info.depthBiasSlopeFactor = p_rasterization_state.depth_bias_slope_factor; rasterization_state_create_info.lineWidth = p_rasterization_state.line_width; - //multisample + // Multisample. VkPipelineMultisampleStateCreateInfo multisample_state_create_info; multisample_state_create_info.sType = VK_STRUCTURE_TYPE_PIPELINE_MULTISAMPLE_STATE_CREATE_INFO; multisample_state_create_info.pNext = nullptr; @@ -6359,7 +6572,7 @@ RID RenderingDeviceVulkan::render_pipeline_create(RID p_shader, FramebufferForma multisample_state_create_info.minSampleShading = p_multisample_state.min_sample_shading; Vector<VkSampleMask> sample_mask; if (p_multisample_state.sample_mask.size()) { - //use sample mask + // Use sample mask. const int rasterization_sample_mask_expected_size[TEXTURE_SAMPLES_MAX] = { 1, 2, 4, 8, 16, 32, 64 }; @@ -6377,7 +6590,7 @@ RID RenderingDeviceVulkan::render_pipeline_create(RID p_shader, FramebufferForma multisample_state_create_info.alphaToCoverageEnable = p_multisample_state.enable_alpha_to_coverage; multisample_state_create_info.alphaToOneEnable = p_multisample_state.enable_alpha_to_one; - //depth stencil + // Depth stencil. VkPipelineDepthStencilStateCreateInfo depth_stencil_state_create_info; depth_stencil_state_create_info.sType = VK_STRUCTURE_TYPE_PIPELINE_DEPTH_STENCIL_STATE_CREATE_INFO; @@ -6417,7 +6630,7 @@ RID RenderingDeviceVulkan::render_pipeline_create(RID p_shader, FramebufferForma depth_stencil_state_create_info.minDepthBounds = p_depth_stencil_state.depth_range_min; depth_stencil_state_create_info.maxDepthBounds = p_depth_stencil_state.depth_range_max; - //blend state + // Blend state. VkPipelineColorBlendStateCreateInfo color_blend_state_create_info; color_blend_state_create_info.sType = VK_STRUCTURE_TYPE_PIPELINE_COLOR_BLEND_STATE_CREATE_INFO; color_blend_state_create_info.pNext = nullptr; @@ -6488,15 +6701,15 @@ RID RenderingDeviceVulkan::render_pipeline_create(RID p_shader, FramebufferForma color_blend_state_create_info.blendConstants[2] = p_blend_state.blend_constant.b; color_blend_state_create_info.blendConstants[3] = p_blend_state.blend_constant.a; - //dynamic state + // Dynamic state. VkPipelineDynamicStateCreateInfo dynamic_state_create_info; dynamic_state_create_info.sType = VK_STRUCTURE_TYPE_PIPELINE_DYNAMIC_STATE_CREATE_INFO; dynamic_state_create_info.pNext = nullptr; dynamic_state_create_info.flags = 0; - Vector<VkDynamicState> dynamic_states; //vulkan is weird.. + Vector<VkDynamicState> dynamic_states; // Vulkan is weird. - dynamic_states.push_back(VK_DYNAMIC_STATE_VIEWPORT); //viewport and scissor are always dynamic + dynamic_states.push_back(VK_DYNAMIC_STATE_VIEWPORT); // Viewport and scissor are always dynamic. dynamic_states.push_back(VK_DYNAMIC_STATE_SCISSOR); if (p_dynamic_state_flags & DYNAMIC_STATE_LINE_WIDTH) { @@ -6530,11 +6743,28 @@ RID RenderingDeviceVulkan::render_pipeline_create(RID p_shader, FramebufferForma dynamic_state_create_info.dynamicStateCount = dynamic_states.size(); dynamic_state_create_info.pDynamicStates = dynamic_states.ptr(); - //finally, pipeline create info + void *graphics_pipeline_nextptr = nullptr; + + VkPipelineFragmentShadingRateStateCreateInfoKHR vrs_create_info; + if (context->get_vrs_capabilities().attachment_vrs_supported) { + // If VRS is used, this defines how the different VRS types are combined. + // combinerOps[0] decides how we use the output of pipeline and primitive (drawcall) VRS. + // combinerOps[1] decides how we use the output of combinerOps[0] and our attachment VRS. + + vrs_create_info.sType = VK_STRUCTURE_TYPE_PIPELINE_FRAGMENT_SHADING_RATE_STATE_CREATE_INFO_KHR; + vrs_create_info.pNext = nullptr; + vrs_create_info.fragmentSize = { 4, 4 }; + vrs_create_info.combinerOps[0] = VK_FRAGMENT_SHADING_RATE_COMBINER_OP_KEEP_KHR; // We don't use pipeline/primitive VRS so this really doesn't matter. + vrs_create_info.combinerOps[1] = VK_FRAGMENT_SHADING_RATE_COMBINER_OP_REPLACE_KHR; // Always use the outcome of attachment VRS if enabled. + + graphics_pipeline_nextptr = &vrs_create_info; + } + + // Finally, pipeline create info. VkGraphicsPipelineCreateInfo graphics_pipeline_create_info; graphics_pipeline_create_info.sType = VK_STRUCTURE_TYPE_GRAPHICS_PIPELINE_CREATE_INFO; - graphics_pipeline_create_info.pNext = nullptr; + graphics_pipeline_create_info.pNext = graphics_pipeline_nextptr; graphics_pipeline_create_info.flags = 0; Vector<VkPipelineShaderStageCreateInfo> pipeline_stages = shader->pipeline_stages; @@ -6548,9 +6778,9 @@ RID RenderingDeviceVulkan::render_pipeline_create(RID p_shader, FramebufferForma 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 overridden + // See if overridden. const Shader::SpecializationConstant &sc = shader->specialization_constants[i]; - data_ptr[i] = sc.constant.int_value; //just copy the 32 bits + 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]; @@ -6645,9 +6875,12 @@ RID RenderingDeviceVulkan::render_pipeline_create(RID p_shader, FramebufferForma }; pipeline.validation.primitive_minimum = primitive_minimum[p_render_primitive]; #endif - //create ID to associate with this pipeline + // Create ID to associate with this pipeline. RID id = render_pipeline_owner.make_rid(pipeline); - //now add all the dependencies +#ifdef DEV_ENABLED + set_resource_name(id, "RID:" + itos(id.get_id())); +#endif + // Now add all the dependencies. _add_dependency(id, p_shader); return id; } @@ -6664,14 +6897,14 @@ bool RenderingDeviceVulkan::render_pipeline_is_valid(RID p_pipeline) { RID RenderingDeviceVulkan::compute_pipeline_create(RID p_shader, const Vector<PipelineSpecializationConstant> &p_specialization_constants) { _THREAD_SAFE_METHOD_ - //needs a shader + // Needs a shader. Shader *shader = shader_owner.get_or_null(p_shader); ERR_FAIL_COND_V(!shader, RID()); ERR_FAIL_COND_V_MSG(!shader->is_compute, RID(), "Non-compute shaders can't be used in compute pipelines"); - //finally, pipeline create info + // Finally, pipeline create info. VkComputePipelineCreateInfo compute_pipeline_create_info; compute_pipeline_create_info.sType = VK_STRUCTURE_TYPE_COMPUTE_PIPELINE_CREATE_INFO; @@ -6691,15 +6924,15 @@ RID RenderingDeviceVulkan::compute_pipeline_create(RID p_shader, const Vector<Pi 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 overridden + // See if overridden. const Shader::SpecializationConstant &sc = shader->specialization_constants[i]; - data_ptr[i] = sc.constant.int_value; //just copy the 32 bits + 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; + data_ptr[i] = psc.int_value; break; } } @@ -6734,9 +6967,12 @@ RID RenderingDeviceVulkan::compute_pipeline_create(RID p_shader, const Vector<Pi pipeline.local_group_size[1] = shader->compute_local_size[1]; pipeline.local_group_size[2] = shader->compute_local_size[2]; - //create ID to associate with this pipeline + // Create ID to associate with this pipeline. RID id = compute_pipeline_owner.make_rid(pipeline); - //now add all the dependencies +#ifdef DEV_ENABLED + set_resource_name(id, "RID:" + itos(id.get_id())); +#endif + // Now add all the dependencies. _add_dependency(id, p_shader); return id; } @@ -6766,7 +7002,7 @@ RenderingDevice::FramebufferFormatID RenderingDeviceVulkan::screen_get_framebuff _THREAD_SAFE_METHOD_ ERR_FAIL_COND_V_MSG(local_device.is_valid(), INVALID_ID, "Local devices have no screen"); - //very hacky, but not used often per frame so I guess ok + // Very hacky, but not used often per frame so I guess ok. VkFormat vkformat = context->get_screen_format(); DataFormat format = DATA_FORMAT_MAX; for (int i = 0; i < DATA_FORMAT_MAX; i++) { @@ -6868,7 +7104,7 @@ Error RenderingDeviceVulkan::_draw_list_setup_framebuffer(Framebuffer *p_framebu vk.view_count = p_framebuffer->view_count; if (!p_framebuffer->framebuffers.has(vk)) { - //need to create this version + // Need to create this version. Framebuffer::Version version; 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); @@ -6883,8 +7119,10 @@ Error RenderingDeviceVulkan::_draw_list_setup_framebuffer(Framebuffer *p_framebu Texture *texture = texture_owner.get_or_null(p_framebuffer->texture_ids[i]); if (texture) { attachments.push_back(texture->view); - ERR_FAIL_COND_V(texture->width != p_framebuffer->size.width, ERR_BUG); - ERR_FAIL_COND_V(texture->height != p_framebuffer->size.height, ERR_BUG); + if (!(texture->usage_flags & TEXTURE_USAGE_VRS_ATTACHMENT_BIT)) { // VRS attachment will be a different size. + ERR_FAIL_COND_V(texture->width != p_framebuffer->size.width, ERR_BUG); + ERR_FAIL_COND_V(texture->height != p_framebuffer->size.height, ERR_BUG); + } } } framebuffer_create_info.attachmentCount = attachments.size(); @@ -6942,7 +7180,7 @@ Error RenderingDeviceVulkan::_draw_list_render_pass_begin(Framebuffer *framebuff } if (color_index < p_clear_colors.size() && texture->usage_flags & TEXTURE_USAGE_COLOR_ATTACHMENT_BIT) { - ERR_FAIL_INDEX_V(color_index, p_clear_colors.size(), ERR_BUG); //a bug + ERR_FAIL_INDEX_V(color_index, p_clear_colors.size(), ERR_BUG); // A bug. Color clear_color = p_clear_colors[color_index]; clear_value.color.float32[0] = clear_color.r; clear_value.color.float32[1] = clear_color.g; @@ -6973,7 +7211,7 @@ Error RenderingDeviceVulkan::_draw_list_render_pass_begin(Framebuffer *framebuff ERR_CONTINUE_MSG(!(texture->usage_flags & TEXTURE_USAGE_STORAGE_BIT), "Supplied storage texture " + itos(i) + " for draw list is not set to be used for storage."); if (texture->usage_flags & TEXTURE_USAGE_SAMPLING_BIT) { - //must change layout to general + // Must change layout to general. VkImageMemoryBarrier image_memory_barrier; image_memory_barrier.sType = VK_STRUCTURE_TYPE_IMAGE_MEMORY_BARRIER; image_memory_barrier.pNext = nullptr; @@ -7001,7 +7239,7 @@ Error RenderingDeviceVulkan::_draw_list_render_pass_begin(Framebuffer *framebuff vkCmdBeginRenderPass(command_buffer, &render_pass_begin, subpass_contents); - //mark textures as bound + // Mark textures as bound. draw_list_bound_textures.clear(); draw_list_unbind_color_textures = p_final_color_action != FINAL_ACTION_CONTINUE; draw_list_unbind_depth_textures = p_final_depth_action != FINAL_ACTION_CONTINUE; @@ -7019,7 +7257,6 @@ Error RenderingDeviceVulkan::_draw_list_render_pass_begin(Framebuffer *framebuff } void RenderingDeviceVulkan::_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) { - ERR_FAIL_COND_MSG(p_clear_color && p_clear_colors.size() != framebuffer->texture_ids.size(), "Clear color values supplied (" + itos(p_clear_colors.size()) + ") differ from the amount required for framebuffer color attachments (" + itos(framebuffer->texture_ids.size()) + ")."); Vector<VkClearAttachment> clear_attachments; int color_index = 0; int texture_index = 0; @@ -7079,7 +7316,7 @@ RenderingDevice::DrawListID RenderingDeviceVulkan::draw_list_begin(RID p_framebu bool needs_clear_color = false; bool needs_clear_depth = false; - if (p_region != Rect2() && p_region != Rect2(Vector2(), viewport_size)) { //check custom region + if (p_region != Rect2() && p_region != Rect2(Vector2(), viewport_size)) { // Check custom region. Rect2i viewport(viewport_offset, viewport_size); Rect2i regioni = p_region; if (!(regioni.position.x >= viewport.position.x) && (regioni.position.y >= viewport.position.y) && @@ -7108,11 +7345,14 @@ RenderingDevice::DrawListID RenderingDeviceVulkan::draw_list_begin(RID p_framebu } } - if (p_initial_color_action == INITIAL_ACTION_CLEAR) { //check clear values + if (p_initial_color_action == INITIAL_ACTION_CLEAR || needs_clear_color) { // Check clear values. int color_count = 0; for (int i = 0; i < framebuffer->texture_ids.size(); i++) { Texture *texture = texture_owner.get_or_null(framebuffer->texture_ids[i]); - if (!texture || !(texture->usage_flags & TEXTURE_USAGE_DEPTH_STENCIL_ATTACHMENT_BIT)) { + // We only check for our VRS usage bit if this is not the first texture id. + // If it is the first we're likely populating our VRS texture. + // Bit dirty but... + if (!texture || (!(texture->usage_flags & TEXTURE_USAGE_DEPTH_STENCIL_ATTACHMENT_BIT) && !(i != 0 && texture->usage_flags & TEXTURE_USAGE_VRS_ATTACHMENT_BIT))) { color_count++; } } @@ -7169,6 +7409,9 @@ RenderingDevice::DrawListID RenderingDeviceVulkan::draw_list_begin(RID p_framebu Error RenderingDeviceVulkan::draw_list_begin_split(RID p_framebuffer, uint32_t p_splits, DrawListID *r_split_ids, 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_color_values, float p_clear_depth, uint32_t p_clear_stencil, const Rect2 &p_region, const Vector<RID> &p_storage_textures) { _THREAD_SAFE_METHOD_ + ERR_FAIL_COND_V_MSG(draw_list != nullptr, ERR_BUSY, "Only one draw list can be active at the same time."); + ERR_FAIL_COND_V_MSG(compute_list != nullptr && !compute_list->state.allow_draw_overlap, ERR_BUSY, "Only one draw/compute list can be active at the same time."); + ERR_FAIL_COND_V(p_splits < 1, ERR_INVALID_DECLARATION); Framebuffer *framebuffer = framebuffer_owner.get_or_null(p_framebuffer); @@ -7180,7 +7423,7 @@ Error RenderingDeviceVulkan::draw_list_begin_split(RID p_framebuffer, uint32_t p bool needs_clear_color = false; bool needs_clear_depth = false; - if (p_region != Rect2() && p_region != Rect2(Vector2(), viewport_size)) { //check custom region + if (p_region != Rect2() && p_region != Rect2(Vector2(), viewport_size)) { // Check custom region. Rect2i viewport(viewport_offset, viewport_size); Rect2i regioni = p_region; if (!(regioni.position.x >= viewport.position.x) && (regioni.position.y >= viewport.position.y) && @@ -7202,7 +7445,7 @@ Error RenderingDeviceVulkan::draw_list_begin_split(RID p_framebuffer, uint32_t p } } - if (p_initial_color_action == INITIAL_ACTION_CLEAR) { //check clear values + if (p_initial_color_action == INITIAL_ACTION_CLEAR || needs_clear_color) { // Check clear values. int color_count = 0; for (int i = 0; i < framebuffer->texture_ids.size(); i++) { @@ -7288,7 +7531,7 @@ RenderingDeviceVulkan::DrawList *RenderingDeviceVulkan::_get_draw_list_ptr(DrawL return nullptr; } - uint64_t index = p_id & ((DrawListID(1) << DrawListID(ID_BASE_SHIFT)) - 1); //mask + uint64_t index = p_id & ((DrawListID(1) << DrawListID(ID_BASE_SHIFT)) - 1); // Mask. if (index >= draw_list_count) { return nullptr; @@ -7314,7 +7557,7 @@ void RenderingDeviceVulkan::draw_list_bind_render_pipeline(DrawListID p_list, RI #endif if (p_render_pipeline == dl->state.pipeline) { - return; //redundant state, return. + return; // Redundant state, return. } dl->state.pipeline = p_render_pipeline; @@ -7323,17 +7566,17 @@ void RenderingDeviceVulkan::draw_list_bind_render_pipeline(DrawListID p_list, RI vkCmdBindPipeline(dl->command_buffer, VK_PIPELINE_BIND_POINT_GRAPHICS, pipeline->pipeline); if (dl->state.pipeline_shader != pipeline->shader) { - // shader changed, so descriptor sets may become incompatible. + // Shader changed, so descriptor sets may become incompatible. - //go through ALL sets, and unbind them (and all those above) if the format is different + // Go through ALL sets, and unbind them (and all those above) if the format is different. - uint32_t pcount = pipeline->set_formats.size(); //formats count in this pipeline + uint32_t pcount = pipeline->set_formats.size(); // Formats count in this pipeline. dl->state.set_count = MAX(dl->state.set_count, pcount); - const uint32_t *pformats = pipeline->set_formats.ptr(); //pipeline set formats + const uint32_t *pformats = pipeline->set_formats.ptr(); // Pipeline set formats. - bool sets_valid = true; //once invalid, all above become invalid + bool sets_valid = true; // Once invalid, all above become invalid. for (uint32_t i = 0; i < pcount; i++) { - //if a part of the format is different, invalidate it (and the rest) + // If a part of the format is different, invalidate it (and the rest). if (!sets_valid || dl->state.sets[i].pipeline_expected_format != pformats[i]) { dl->state.sets[i].bound = false; dl->state.sets[i].pipeline_expected_format = pformats[i]; @@ -7342,11 +7585,11 @@ void RenderingDeviceVulkan::draw_list_bind_render_pipeline(DrawListID p_list, RI } for (uint32_t i = pcount; i < dl->state.set_count; i++) { - //unbind the ones above (not used) if exist + // Unbind the ones above (not used) if exist. dl->state.sets[i].bound = false; } - dl->state.set_count = pcount; //update set count + dl->state.set_count = pcount; // Update set count. if (pipeline->push_constant_size) { dl->state.pipeline_push_constant_stages = pipeline->push_constant_stages; @@ -7359,7 +7602,7 @@ void RenderingDeviceVulkan::draw_list_bind_render_pipeline(DrawListID p_list, RI } #ifdef DEBUG_ENABLED - //update render pass pipeline info + // Update render pass pipeline info. dl->validation.pipeline_active = true; dl->validation.pipeline_dynamic_state = pipeline->validation.dynamic_state; dl->validation.pipeline_vertex_format = pipeline->validation.vertex_format; @@ -7389,8 +7632,8 @@ void RenderingDeviceVulkan::draw_list_bind_uniform_set(DrawListID p_list, RID p_ dl->state.set_count = p_index; } - dl->state.sets[p_index].descriptor_set = uniform_set->descriptor_set; //update set pointer - dl->state.sets[p_index].bound = false; //needs rebind + dl->state.sets[p_index].descriptor_set = uniform_set->descriptor_set; // Update set pointer. + dl->state.sets[p_index].bound = false; // Needs rebind. dl->state.sets[p_index].uniform_set_format = uniform_set->format; dl->state.sets[p_index].uniform_set = p_uniform_set; @@ -7408,7 +7651,7 @@ void RenderingDeviceVulkan::draw_list_bind_uniform_set(DrawListID p_list, RID p_ } #ifdef DEBUG_ENABLED - { //validate that textures bound are not attached as framebuffer bindings + { // Validate that textures bound are not attached as framebuffer bindings. uint32_t attachable_count = uniform_set->attachable_textures.size(); const UniformSet::AttachableTexture *attachable_ptr = uniform_set->attachable_textures.ptr(); uint32_t bound_count = draw_list_bound_textures.size(); @@ -7434,7 +7677,7 @@ void RenderingDeviceVulkan::draw_list_bind_vertex_array(DrawListID p_list, RID p ERR_FAIL_COND(!vertex_array); if (dl->state.vertex_array == p_vertex_array) { - return; //already set + return; // Already set. } dl->state.vertex_array = p_vertex_array; @@ -7458,7 +7701,7 @@ void RenderingDeviceVulkan::draw_list_bind_index_array(DrawListID p_list, RID p_ ERR_FAIL_COND(!index_array); if (dl->state.index_array == p_index_array) { - return; //already set + return; // Already set. } dl->state.index_array = p_index_array; @@ -7510,30 +7753,30 @@ void RenderingDeviceVulkan::draw_list_draw(DrawListID p_list, bool p_use_indices ERR_FAIL_COND_MSG(!dl->validation.pipeline_active, "No render pipeline was set before attempting to draw."); if (dl->validation.pipeline_vertex_format != INVALID_ID) { - //pipeline uses vertices, validate format + // Pipeline uses vertices, validate format. ERR_FAIL_COND_MSG(dl->validation.vertex_format == INVALID_ID, "No vertex array was bound, and render pipeline expects vertices."); - //make sure format is right + // Make sure format is right. ERR_FAIL_COND_MSG(dl->validation.pipeline_vertex_format != dl->validation.vertex_format, "The vertex format used to create the pipeline does not match the vertex format bound."); - //make sure number of instances is valid + // Make sure number of instances is valid. ERR_FAIL_COND_MSG(p_instances > dl->validation.vertex_max_instances_allowed, "Number of instances requested (" + itos(p_instances) + " is larger than the maximum number supported by the bound vertex array (" + itos(dl->validation.vertex_max_instances_allowed) + ")."); } if (dl->validation.pipeline_push_constant_size > 0) { - //using push constants, check that they were supplied + // Using push constants, check that they were supplied. ERR_FAIL_COND_MSG(!dl->validation.pipeline_push_constant_supplied, "The shader in this pipeline requires a push constant to be set before drawing, but it's not present."); } #endif - //Bind descriptor sets + // Bind descriptor sets. for (uint32_t i = 0; i < dl->state.set_count; i++) { if (dl->state.sets[i].pipeline_expected_format == 0) { - continue; //nothing expected by this pipeline + continue; // Nothing expected by this pipeline. } #ifdef DEBUG_ENABLED if (dl->state.sets[i].pipeline_expected_format != dl->state.sets[i].uniform_set_format) { @@ -7548,7 +7791,7 @@ void RenderingDeviceVulkan::draw_list_draw(DrawListID p_list, bool p_use_indices } #endif if (!dl->state.sets[i].bound) { - //All good, see if this requires re-binding + // All good, see if this requires re-binding. vkCmdBindDescriptorSets(dl->command_buffer, VK_PIPELINE_BIND_POINT_GRAPHICS, dl->state.pipeline_layout, i, 1, &dl->state.sets[i].descriptor_set, 0, nullptr); dl->state.sets[i].bound = true; } @@ -7562,12 +7805,6 @@ void RenderingDeviceVulkan::draw_list_draw(DrawListID p_list, bool p_use_indices ERR_FAIL_COND_MSG(!dl->validation.index_array_size, "Draw command requested indices, but no index buffer was set."); - if (dl->validation.pipeline_vertex_format != INVALID_ID) { - //uses vertices, do some vertex validations - ERR_FAIL_COND_MSG(dl->validation.vertex_array_size < dl->validation.index_array_max_index, - "Index array references (max index: " + itos(dl->validation.index_array_max_index) + ") indices beyond the vertex array size (" + itos(dl->validation.vertex_array_size) + ")."); - } - ERR_FAIL_COND_MSG(dl->validation.pipeline_uses_restart_indices != dl->validation.index_buffer_uses_restart_indices, "The usage of restart indices in index buffer does not match the render primitive in the pipeline."); #endif @@ -7689,7 +7926,7 @@ Error RenderingDeviceVulkan::draw_list_switch_to_next_pass_split(uint32_t p_spli } Error RenderingDeviceVulkan::_draw_list_allocate(const Rect2i &p_viewport, uint32_t p_splits, uint32_t p_subpass) { - // Lock while draw_list is active + // Lock while draw_list is active. _THREAD_SAFE_LOCK_ if (p_splits == 0) { @@ -7716,7 +7953,7 @@ Error RenderingDeviceVulkan::_draw_list_allocate(const Rect2i &p_viewport, uint3 VkCommandBuffer command_buffer; VkCommandBufferAllocateInfo cmdbuf; - //no command buffer exists, create it. + // 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; @@ -7735,7 +7972,7 @@ Error RenderingDeviceVulkan::_draw_list_allocate(const Rect2i &p_viewport, uint3 draw_list_split = true; for (uint32_t i = 0; i < p_splits; i++) { - //take a command buffer and initialize it + // Take a command buffer and initialize it. VkCommandBuffer command_buffer = split_draw_list_allocators[i].command_buffers[frame]; VkCommandBufferInheritanceInfo inheritance_info; @@ -7745,7 +7982,7 @@ Error RenderingDeviceVulkan::_draw_list_allocate(const Rect2i &p_viewport, uint3 inheritance_info.subpass = p_subpass; inheritance_info.framebuffer = draw_list_vkframebuffer; inheritance_info.occlusionQueryEnable = false; - inheritance_info.queryFlags = 0; //? + inheritance_info.queryFlags = 0; // ? inheritance_info.pipelineStatistics = 0; VkCommandBufferBeginInfo cmdbuf_begin; @@ -7778,7 +8015,7 @@ Error RenderingDeviceVulkan::_draw_list_allocate(const Rect2i &p_viewport, uint3 void RenderingDeviceVulkan::_draw_list_free(Rect2i *r_last_viewport) { if (draw_list_split) { - //send all command buffers + // 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); @@ -7798,12 +8035,12 @@ void RenderingDeviceVulkan::_draw_list_free(Rect2i *r_last_viewport) { if (r_last_viewport) { *r_last_viewport = draw_list->viewport; } - //just end the list + // Just end the list. memdelete(draw_list); draw_list = nullptr; } - // draw_list is no longer active + // Draw_list is no longer active. _THREAD_SAFE_UNLOCK_ } @@ -7818,7 +8055,7 @@ void RenderingDeviceVulkan::draw_list_end(uint32_t p_post_barrier) { for (int i = 0; i < draw_list_bound_textures.size(); i++) { Texture *texture = texture_owner.get_or_null(draw_list_bound_textures[i]); - ERR_CONTINUE(!texture); //wtf + ERR_CONTINUE(!texture); // Wtf. if (draw_list_unbind_color_textures && (texture->usage_flags & TEXTURE_USAGE_COLOR_ATTACHMENT_BIT)) { texture->bound = false; } @@ -7890,12 +8127,8 @@ void RenderingDeviceVulkan::draw_list_end(uint32_t p_post_barrier) { draw_list_storage_textures.clear(); // 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) - -#ifdef FORCE_FULL_BARRIER - _full_barrier(true); -#else + // * Some buffer is copied. + // * Another render pass happens (since we may be done). VkMemoryBarrier mem_barrier; mem_barrier.sType = VK_STRUCTURE_TYPE_MEMORY_BARRIER; @@ -7907,6 +8140,8 @@ void RenderingDeviceVulkan::draw_list_end(uint32_t p_post_barrier) { vkCmdPipelineBarrier(frames[frame].draw_command_buffer, src_stage, barrier_flags, 0, 1, &mem_barrier, 0, nullptr, image_barrier_count, image_barriers); } +#ifdef FORCE_FULL_BARRIER + _full_barrier(true); #endif } @@ -7918,7 +8153,7 @@ RenderingDevice::ComputeListID RenderingDeviceVulkan::compute_list_begin(bool p_ ERR_FAIL_COND_V_MSG(!p_allow_draw_overlap && draw_list != nullptr, INVALID_ID, "Only one draw list can be active at the same time."); ERR_FAIL_COND_V_MSG(compute_list != nullptr, INVALID_ID, "Only one draw/compute list can be active at the same time."); - // Lock while compute_list is active + // Lock while compute_list is active. _THREAD_SAFE_LOCK_ compute_list = memnew(ComputeList); @@ -7938,7 +8173,7 @@ void RenderingDeviceVulkan::compute_list_bind_compute_pipeline(ComputeListID p_l ERR_FAIL_COND(!pipeline); if (p_compute_pipeline == cl->state.pipeline) { - return; //redundant state, return. + return; // Redundant state, return. } cl->state.pipeline = p_compute_pipeline; @@ -7947,17 +8182,17 @@ void RenderingDeviceVulkan::compute_list_bind_compute_pipeline(ComputeListID p_l vkCmdBindPipeline(cl->command_buffer, VK_PIPELINE_BIND_POINT_COMPUTE, pipeline->pipeline); if (cl->state.pipeline_shader != pipeline->shader) { - // shader changed, so descriptor sets may become incompatible. + // Shader changed, so descriptor sets may become incompatible. - //go through ALL sets, and unbind them (and all those above) if the format is different + // Go through ALL sets, and unbind them (and all those above) if the format is different. - uint32_t pcount = pipeline->set_formats.size(); //formats count in this pipeline + uint32_t pcount = pipeline->set_formats.size(); // Formats count in this pipeline. cl->state.set_count = MAX(cl->state.set_count, pcount); - const uint32_t *pformats = pipeline->set_formats.ptr(); //pipeline set formats + const uint32_t *pformats = pipeline->set_formats.ptr(); // Pipeline set formats. - bool sets_valid = true; //once invalid, all above become invalid + bool sets_valid = true; // Once invalid, all above become invalid. for (uint32_t i = 0; i < pcount; i++) { - //if a part of the format is different, invalidate it (and the rest) + // If a part of the format is different, invalidate it (and the rest). if (!sets_valid || cl->state.sets[i].pipeline_expected_format != pformats[i]) { cl->state.sets[i].bound = false; cl->state.sets[i].pipeline_expected_format = pformats[i]; @@ -7966,11 +8201,11 @@ void RenderingDeviceVulkan::compute_list_bind_compute_pipeline(ComputeListID p_l } for (uint32_t i = pcount; i < cl->state.set_count; i++) { - //unbind the ones above (not used) if exist + // Unbind the ones above (not used) if exist. cl->state.sets[i].bound = false; } - cl->state.set_count = pcount; //update set count + cl->state.set_count = pcount; // Update set count. if (pipeline->push_constant_size) { cl->state.pipeline_push_constant_stages = pipeline->push_constant_stages; @@ -7986,7 +8221,7 @@ void RenderingDeviceVulkan::compute_list_bind_compute_pipeline(ComputeListID p_l } #ifdef DEBUG_ENABLED - //update compute pass pipeline info + // Update compute pass pipeline info. cl->validation.pipeline_active = true; cl->validation.pipeline_push_constant_size = pipeline->push_constant_size; #endif @@ -8014,8 +8249,8 @@ void RenderingDeviceVulkan::compute_list_bind_uniform_set(ComputeListID p_list, cl->state.set_count = p_index; } - cl->state.sets[p_index].descriptor_set = uniform_set->descriptor_set; //update set pointer - cl->state.sets[p_index].bound = false; //needs rebind + cl->state.sets[p_index].descriptor_set = uniform_set->descriptor_set; // Update set pointer. + cl->state.sets[p_index].bound = false; // Needs rebind. cl->state.sets[p_index].uniform_set_format = uniform_set->format; cl->state.sets[p_index].uniform_set = p_uniform_set; @@ -8118,7 +8353,7 @@ void RenderingDeviceVulkan::compute_list_bind_uniform_set(ComputeListID p_list, textures_to_storage[i]->layout = VK_IMAGE_LAYOUT_GENERAL; - cl->state.textures_to_sampled_layout.insert(textures_to_storage[i]); //needs to go back to sampled layout afterwards + cl->state.textures_to_sampled_layout.insert(textures_to_storage[i]); // Needs to go back to sampled layout afterwards. } } @@ -8131,7 +8366,7 @@ void RenderingDeviceVulkan::compute_list_bind_uniform_set(ComputeListID p_list, } #if 0 - { //validate that textures bound are not attached as framebuffer bindings + { // Validate that textures bound are not attached as framebuffer bindings. uint32_t attachable_count = uniform_set->attachable_textures.size(); const RID *attachable_ptr = uniform_set->attachable_textures.ptr(); uint32_t bound_count = draw_list_bound_textures.size(); @@ -8191,18 +8426,18 @@ void RenderingDeviceVulkan::compute_list_dispatch(ComputeListID p_list, uint32_t ERR_FAIL_COND_MSG(!cl->validation.pipeline_active, "No compute pipeline was set before attempting to draw."); if (cl->validation.pipeline_push_constant_size > 0) { - //using push constants, check that they were supplied + // Using push constants, check that they were supplied. ERR_FAIL_COND_MSG(!cl->validation.pipeline_push_constant_supplied, "The shader in this pipeline requires a push constant to be set before drawing, but it's not present."); } #endif - //Bind descriptor sets + // Bind descriptor sets. for (uint32_t i = 0; i < cl->state.set_count; i++) { if (cl->state.sets[i].pipeline_expected_format == 0) { - continue; //nothing expected by this pipeline + continue; // Nothing expected by this pipeline. } #ifdef DEBUG_ENABLED if (cl->state.sets[i].pipeline_expected_format != cl->state.sets[i].uniform_set_format) { @@ -8217,7 +8452,7 @@ void RenderingDeviceVulkan::compute_list_dispatch(ComputeListID p_list, uint32_t } #endif if (!cl->state.sets[i].bound) { - //All good, see if this requires re-binding + // All good, see if this requires re-binding. vkCmdBindDescriptorSets(cl->command_buffer, VK_PIPELINE_BIND_POINT_COMPUTE, cl->state.pipeline_layout, i, 1, &cl->state.sets[i].descriptor_set, 0, nullptr); cl->state.sets[i].bound = true; } @@ -8243,7 +8478,7 @@ void RenderingDeviceVulkan::compute_list_dispatch_threads(ComputeListID p_list, ERR_FAIL_COND_MSG(!cl->validation.pipeline_active, "No compute pipeline was set before attempting to draw."); if (cl->validation.pipeline_push_constant_size > 0) { - //using push constants, check that they were supplied + // Using push constants, check that they were supplied. ERR_FAIL_COND_MSG(!cl->validation.pipeline_push_constant_supplied, "The shader in this pipeline requires a push constant to be set before drawing, but it's not present."); } @@ -8261,7 +8496,7 @@ void RenderingDeviceVulkan::compute_list_dispatch_indirect(ComputeListID p_list, Buffer *buffer = storage_buffer_owner.get_or_null(p_buffer); ERR_FAIL_COND(!buffer); - ERR_FAIL_COND_MSG(!(buffer->usage & STORAGE_BUFFER_USAGE_DISPATCH_INDIRECT), "Buffer provided was not created to do indirect dispatch."); + ERR_FAIL_COND_MSG(!(buffer->usage & VK_BUFFER_USAGE_INDIRECT_BUFFER_BIT), "Buffer provided was not created to do indirect dispatch."); ERR_FAIL_COND_MSG(p_offset + 12 > buffer->size, "Offset provided (+12) is past the end of buffer."); @@ -8274,18 +8509,18 @@ void RenderingDeviceVulkan::compute_list_dispatch_indirect(ComputeListID p_list, ERR_FAIL_COND_MSG(!cl->validation.pipeline_active, "No compute pipeline was set before attempting to draw."); if (cl->validation.pipeline_push_constant_size > 0) { - //using push constants, check that they were supplied + // Using push constants, check that they were supplied. ERR_FAIL_COND_MSG(!cl->validation.pipeline_push_constant_supplied, "The shader in this pipeline requires a push constant to be set before drawing, but it's not present."); } #endif - //Bind descriptor sets + // Bind descriptor sets. for (uint32_t i = 0; i < cl->state.set_count; i++) { if (cl->state.sets[i].pipeline_expected_format == 0) { - continue; //nothing expected by this pipeline + continue; // Nothing expected by this pipeline. } #ifdef DEBUG_ENABLED if (cl->state.sets[i].pipeline_expected_format != cl->state.sets[i].uniform_set_format) { @@ -8300,7 +8535,7 @@ void RenderingDeviceVulkan::compute_list_dispatch_indirect(ComputeListID p_list, } #endif if (!cl->state.sets[i].bound) { - //All good, see if this requires re-binding + // All good, see if this requires re-binding. vkCmdBindDescriptorSets(cl->command_buffer, VK_PIPELINE_BIND_POINT_COMPUTE, cl->state.pipeline_layout, i, 1, &cl->state.sets[i].descriptor_set, 0, nullptr); cl->state.sets[i].bound = true; } @@ -8349,37 +8584,34 @@ void RenderingDeviceVulkan::compute_list_end(uint32_t p_post_barrier) { uint32_t barrier_idx = 0; - for (Set<Texture *>::Element *E = compute_list->state.textures_to_sampled_layout.front(); E; E = E->next()) { + for (Texture *E : compute_list->state.textures_to_sampled_layout) { VkImageMemoryBarrier &image_memory_barrier = image_barriers[barrier_idx++]; image_memory_barrier.sType = VK_STRUCTURE_TYPE_IMAGE_MEMORY_BARRIER; image_memory_barrier.pNext = nullptr; image_memory_barrier.srcAccessMask = VK_ACCESS_SHADER_READ_BIT | VK_ACCESS_SHADER_WRITE_BIT; image_memory_barrier.dstAccessMask = access_flags; - image_memory_barrier.oldLayout = E->get()->layout; + image_memory_barrier.oldLayout = E->layout; image_memory_barrier.newLayout = VK_IMAGE_LAYOUT_SHADER_READ_ONLY_OPTIMAL; image_memory_barrier.srcQueueFamilyIndex = VK_QUEUE_FAMILY_IGNORED; image_memory_barrier.dstQueueFamilyIndex = VK_QUEUE_FAMILY_IGNORED; - image_memory_barrier.image = E->get()->image; - image_memory_barrier.subresourceRange.aspectMask = E->get()->read_aspect_mask; - image_memory_barrier.subresourceRange.baseMipLevel = E->get()->base_mipmap; - image_memory_barrier.subresourceRange.levelCount = E->get()->mipmaps; - image_memory_barrier.subresourceRange.baseArrayLayer = E->get()->base_layer; - image_memory_barrier.subresourceRange.layerCount = E->get()->layers; + image_memory_barrier.image = E->image; + image_memory_barrier.subresourceRange.aspectMask = E->read_aspect_mask; + image_memory_barrier.subresourceRange.baseMipLevel = E->base_mipmap; + image_memory_barrier.subresourceRange.levelCount = E->mipmaps; + image_memory_barrier.subresourceRange.baseArrayLayer = E->base_layer; + image_memory_barrier.subresourceRange.layerCount = E->layers; - E->get()->layout = VK_IMAGE_LAYOUT_SHADER_READ_ONLY_OPTIMAL; + E->layout = VK_IMAGE_LAYOUT_SHADER_READ_ONLY_OPTIMAL; - if (E->get()->used_in_frame != frames_drawn) { - E->get()->used_in_transfer = false; - E->get()->used_in_raster = false; - E->get()->used_in_compute = false; - E->get()->used_in_frame = frames_drawn; + if (E->used_in_frame != frames_drawn) { + E->used_in_transfer = false; + E->used_in_raster = false; + E->used_in_compute = false; + E->used_in_frame = frames_drawn; } } -#ifdef FORCE_FULL_BARRIER - _full_barrier(true); -#else VkMemoryBarrier mem_barrier; mem_barrier.sType = VK_STRUCTURE_TYPE_MEMORY_BARRIER; mem_barrier.pNext = nullptr; @@ -8390,12 +8622,14 @@ void RenderingDeviceVulkan::compute_list_end(uint32_t p_post_barrier) { vkCmdPipelineBarrier(compute_list->command_buffer, VK_PIPELINE_STAGE_COMPUTE_SHADER_BIT, barrier_flags, 0, 1, &mem_barrier, 0, nullptr, image_barrier_count, image_barriers); } +#ifdef FORCE_FULL_BARRIER + _full_barrier(true); #endif memdelete(compute_list); compute_list = nullptr; - // compute_list is no longer active + // Compute_list is no longer active. _THREAD_SAFE_UNLOCK_ } @@ -8488,7 +8722,7 @@ void RenderingDeviceVulkan::draw_list_render_secondary_to_framebuffer(ID p_frame "Draw list index (" + itos(i) + ") is created with a framebuffer format incompatible with this render pass."); if (dl->validation.active) { - //needs to be closed, so close it. + // Needs to be closed, so close it. vkEndCommandBuffer(dl->command_buffer); dl->validation.active = false; } @@ -8505,7 +8739,15 @@ void RenderingDeviceVulkan::draw_list_render_secondary_to_framebuffer(ID p_frame #endif void RenderingDeviceVulkan::_free_internal(RID p_id) { - //push everything so it's disposed of next time this frame index is processed (means, it's safe to do it) +#ifdef DEV_ENABLED + String resource_name; + if (resource_names.has(p_id)) { + resource_name = resource_names[p_id]; + resource_names.erase(p_id); + } +#endif + + // Push everything so it's disposed of next time this frame index is processed (means, it's safe to do it). if (texture_owner.owns(p_id)) { Texture *texture = texture_owner.get_or_null(p_id); frames[frame].textures_to_dispose_of.push_back(*texture); @@ -8513,6 +8755,11 @@ void RenderingDeviceVulkan::_free_internal(RID p_id) { } else if (framebuffer_owner.owns(p_id)) { Framebuffer *framebuffer = framebuffer_owner.get_or_null(p_id); frames[frame].framebuffers_to_dispose_of.push_back(*framebuffer); + + if (framebuffer->invalidated_callback != nullptr) { + framebuffer->invalidated_callback(framebuffer->invalidated_callback_userdata); + } + framebuffer_owner.free(p_id); } else if (sampler_owner.owns(p_id)) { VkSampler *sampler = sampler_owner.get_or_null(p_id); @@ -8569,30 +8816,34 @@ void RenderingDeviceVulkan::_free_internal(RID p_id) { frames[frame].compute_pipelines_to_dispose_of.push_back(*pipeline); compute_pipeline_owner.free(p_id); } else { +#ifdef DEV_ENABLED + ERR_PRINT("Attempted to free invalid ID: " + itos(p_id.get_id()) + " " + resource_name); +#else ERR_PRINT("Attempted to free invalid ID: " + itos(p_id.get_id())); +#endif } } void RenderingDeviceVulkan::free(RID p_id) { _THREAD_SAFE_METHOD_ - _free_dependencies(p_id); //recursively erase dependencies first, to avoid potential API problems + _free_dependencies(p_id); // Recursively erase dependencies first, to avoid potential API problems. _free_internal(p_id); } -// The full list of resources that can be named is in the VkObjectType enum +// The full list of resources that can be named is in the VkObjectType enum. // We just expose the resources that are owned and can be accessed easily. void RenderingDeviceVulkan::set_resource_name(RID p_id, const String p_name) { if (texture_owner.owns(p_id)) { Texture *texture = texture_owner.get_or_null(p_id); if (texture->owner.is_null()) { - // Don't set the source texture's name when calling on a texture view + // Don't set the source texture's name when calling on a texture view. context->set_object_name(VK_OBJECT_TYPE_IMAGE, uint64_t(texture->image), p_name); } context->set_object_name(VK_OBJECT_TYPE_IMAGE_VIEW, uint64_t(texture->view), p_name + " View"); } else if (framebuffer_owner.owns(p_id)) { //Framebuffer *framebuffer = framebuffer_owner.get_or_null(p_id); - // Not implemented for now as the relationship between Framebuffer and RenderPass is very complex + // Not implemented for now as the relationship between Framebuffer and RenderPass is very complex. } else if (sampler_owner.owns(p_id)) { VkSampler *sampler = sampler_owner.get_or_null(p_id); context->set_object_name(VK_OBJECT_TYPE_SAMPLER, uint64_t(*sampler), p_name); @@ -8631,7 +8882,11 @@ void RenderingDeviceVulkan::set_resource_name(RID p_id, const String p_name) { context->set_object_name(VK_OBJECT_TYPE_PIPELINE_LAYOUT, uint64_t(pipeline->pipeline_layout), p_name + " Layout"); } else { ERR_PRINT("Attempted to name invalid ID: " + itos(p_id.get_id())); + return; } +#ifdef DEV_ENABLED + resource_names[p_id] = p_name; +#endif } void RenderingDeviceVulkan::draw_command_begin_label(String p_label_name, const Color p_color) { @@ -8649,6 +8904,7 @@ void RenderingDeviceVulkan::draw_command_end_label() { String RenderingDeviceVulkan::get_device_vendor_name() const { return context->get_device_vendor_name(); } + String RenderingDeviceVulkan::get_device_name() const { return context->get_device_name(); } @@ -8657,6 +8913,10 @@ RenderingDevice::DeviceType RenderingDeviceVulkan::get_device_type() const { return context->get_device_type(); } +String RenderingDeviceVulkan::get_device_api_version() const { + return context->get_device_api_version(); +} + String RenderingDeviceVulkan::get_device_pipeline_cache_uuid() const { return context->get_device_pipeline_cache_uuid(); } @@ -8670,17 +8930,17 @@ void RenderingDeviceVulkan::_finalize_command_bufers() { ERR_PRINT("Found open compute list at the end of the frame, this should never happen (further compute will likely not work)."); } - { //complete the setup buffer (that needs to be processed before anything else) + { // Complete the setup buffer (that needs to be processed before anything else). vkEndCommandBuffer(frames[frame].setup_command_buffer); vkEndCommandBuffer(frames[frame].draw_command_buffer); } } void RenderingDeviceVulkan::_begin_frame() { - //erase pending resources + // Erase pending resources. _free_pending_resources(frame); - //create setup command buffer and set as the setup buffer + // Create setup command buffer and set as the setup buffer. { VkCommandBufferBeginInfo cmdbuf_begin; @@ -8699,20 +8959,20 @@ void RenderingDeviceVulkan::_begin_frame() { if (local_device.is_null()) { context->append_command_buffer(frames[frame].draw_command_buffer); - context->set_setup_buffer(frames[frame].setup_command_buffer); //append now so it's added before everything else + context->set_setup_buffer(frames[frame].setup_command_buffer); // Append now so it's added before everything else. } } - //advance current frame + // Advance current frame. frames_drawn++; - //advance staging buffer if used + // Advance staging buffer if used. if (staging_buffer_used) { staging_buffer_current = (staging_buffer_current + 1) % staging_buffer_blocks.size(); staging_buffer_used = false; } if (frames[frame].timestamp_count) { - vkGetQueryPoolResults(device, frames[frame].timestamp_pool, 0, frames[frame].timestamp_count, sizeof(uint64_t) * max_timestamp_query_elements, frames[frame].timestamp_result_values, sizeof(uint64_t), VK_QUERY_RESULT_64_BIT); + vkGetQueryPoolResults(device, frames[frame].timestamp_pool, 0, frames[frame].timestamp_count, sizeof(uint64_t) * max_timestamp_query_elements, frames[frame].timestamp_result_values.ptr(), sizeof(uint64_t), VK_QUERY_RESULT_64_BIT); vkCmdResetQueryPool(frames[frame].setup_command_buffer, frames[frame].timestamp_pool, 0, frames[frame].timestamp_count); SWAP(frames[frame].timestamp_names, frames[frame].timestamp_result_names); SWAP(frames[frame].timestamp_cpu_values, frames[frame].timestamp_cpu_result_values); @@ -8730,7 +8990,7 @@ void RenderingDeviceVulkan::swap_buffers() { _finalize_command_bufers(); screen_prepared = false; - //swap buffers + // Swap buffers. context->swap_buffers(); frame = (frame + 1) % frame_count; @@ -8776,15 +9036,15 @@ VmaPool RenderingDeviceVulkan::_find_or_create_small_allocs_pool(uint32_t p_mem_ pci.pMemoryAllocateNext = nullptr; VmaPool pool = VK_NULL_HANDLE; VkResult res = vmaCreatePool(allocator, &pci, &pool); - small_allocs_pools[p_mem_type_index] = pool; // Don't try to create it again if failed the first time + small_allocs_pools[p_mem_type_index] = pool; // Don't try to create it again if failed the first time. ERR_FAIL_COND_V_MSG(res, pool, "vmaCreatePool failed with error " + itos(res) + "."); return pool; } void RenderingDeviceVulkan::_free_pending_resources(int p_frame) { - //free in dependency usage order, so nothing weird happens - //pipelines + // Free in dependency usage order, so nothing weird happens. + // Pipelines. while (frames[p_frame].render_pipelines_to_dispose_of.front()) { RenderPipeline *pipeline = &frames[p_frame].render_pipelines_to_dispose_of.front()->get(); @@ -8801,7 +9061,7 @@ void RenderingDeviceVulkan::_free_pending_resources(int p_frame) { frames[p_frame].compute_pipelines_to_dispose_of.pop_front(); } - //uniform sets + // Uniform sets. while (frames[p_frame].uniform_sets_to_dispose_of.front()) { UniformSet *uniform_set = &frames[p_frame].uniform_sets_to_dispose_of.front()->get(); @@ -8811,7 +9071,7 @@ void RenderingDeviceVulkan::_free_pending_resources(int p_frame) { frames[p_frame].uniform_sets_to_dispose_of.pop_front(); } - //buffer views + // Buffer views. while (frames[p_frame].buffer_views_to_dispose_of.front()) { VkBufferView buffer_view = frames[p_frame].buffer_views_to_dispose_of.front()->get(); @@ -8820,19 +9080,19 @@ void RenderingDeviceVulkan::_free_pending_resources(int p_frame) { frames[p_frame].buffer_views_to_dispose_of.pop_front(); } - //shaders + // Shaders. while (frames[p_frame].shaders_to_dispose_of.front()) { Shader *shader = &frames[p_frame].shaders_to_dispose_of.front()->get(); - //descriptor set layout for each set + // Descriptor set layout for each set. for (int i = 0; i < shader->sets.size(); i++) { vkDestroyDescriptorSetLayout(device, shader->sets[i].descriptor_set_layout, nullptr); } - //pipeline layout + // Pipeline layout. vkDestroyPipelineLayout(device, shader->pipeline_layout, nullptr); - //shaders themselves + // Shaders themselves. for (int i = 0; i < shader->pipeline_stages.size(); i++) { vkDestroyShaderModule(device, shader->pipeline_stages[i].module, nullptr); } @@ -8840,7 +9100,7 @@ void RenderingDeviceVulkan::_free_pending_resources(int p_frame) { frames[p_frame].shaders_to_dispose_of.pop_front(); } - //samplers + // Samplers. while (frames[p_frame].samplers_to_dispose_of.front()) { VkSampler sampler = frames[p_frame].samplers_to_dispose_of.front()->get(); @@ -8849,12 +9109,12 @@ void RenderingDeviceVulkan::_free_pending_resources(int p_frame) { frames[p_frame].samplers_to_dispose_of.pop_front(); } - //framebuffers + // Framebuffers. while (frames[p_frame].framebuffers_to_dispose_of.front()) { Framebuffer *framebuffer = &frames[p_frame].framebuffers_to_dispose_of.front()->get(); for (const KeyValue<Framebuffer::VersionKey, Framebuffer::Version> &E : framebuffer->framebuffers) { - //first framebuffer, then render pass because it depends on it + // First framebuffer, then render pass because it depends on it. vkDestroyFramebuffer(device, E.value.framebuffer, nullptr); vkDestroyRenderPass(device, E.value.render_pass, nullptr); } @@ -8862,7 +9122,7 @@ void RenderingDeviceVulkan::_free_pending_resources(int p_frame) { frames[p_frame].framebuffers_to_dispose_of.pop_front(); } - //textures + // Textures. while (frames[p_frame].textures_to_dispose_of.front()) { Texture *texture = &frames[p_frame].textures_to_dispose_of.front()->get(); @@ -8871,14 +9131,14 @@ 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 + // 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(); } - //buffers + // Buffers. while (frames[p_frame].buffers_to_dispose_of.front()) { _buffer_free(&frames[p_frame].buffers_to_dispose_of.front()->get()); @@ -8910,9 +9170,9 @@ uint64_t RenderingDeviceVulkan::get_memory_usage(MemoryType p_type) const { void RenderingDeviceVulkan::_flush(bool p_current_frame) { if (local_device.is_valid() && !p_current_frame) { - return; //flushing 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) + // Not doing this crashes RADV (undefined behavior). if (p_current_frame) { vkEndCommandBuffer(frames[frame].setup_command_buffer); vkEndCommandBuffer(frames[frame].draw_command_buffer); @@ -8936,7 +9196,7 @@ void RenderingDeviceVulkan::_flush(bool p_current_frame) { } else { context->flush(p_current_frame, p_current_frame); - //re-create the setup command + // Re-create the setup command. if (p_current_frame) { VkCommandBufferBeginInfo cmdbuf_begin; cmdbuf_begin.sType = VK_STRUCTURE_TYPE_COMMAND_BUFFER_BEGIN_INFO; @@ -8946,7 +9206,7 @@ void RenderingDeviceVulkan::_flush(bool p_current_frame) { VkResult err = vkBeginCommandBuffer(frames[frame].setup_command_buffer, &cmdbuf_begin); ERR_FAIL_COND_MSG(err, "vkBeginCommandBuffer failed with error " + itos(err) + "."); - context->set_setup_buffer(frames[frame].setup_command_buffer); //append now so it's added before everything else + context->set_setup_buffer(frames[frame].setup_command_buffer); // Append now so it's added before everything else. } if (p_current_frame) { @@ -8964,21 +9224,10 @@ void RenderingDeviceVulkan::_flush(bool p_current_frame) { } void RenderingDeviceVulkan::initialize(VulkanContext *p_context, bool p_local_device) { - // get our device capabilities + // 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; - device_capabilities.supports_fsr_half_float = p_context->get_shader_capabilities().shader_float16_is_supported && p_context->get_storage_buffer_capabilities().storage_buffer_16_bit_access_is_supported; } context = p_context; @@ -8988,12 +9237,12 @@ void RenderingDeviceVulkan::initialize(VulkanContext *p_context, bool p_local_de local_device = p_context->local_device_create(); device = p_context->local_device_get_vk_device(local_device); } else { - frame_count = p_context->get_swapchain_image_count() + 1; //always need one extra to ensure it's unused at any time, without having to use a fence for this. + frame_count = p_context->get_swapchain_image_count() + 1; // Always need one extra to ensure it's unused at any time, without having to use a fence for this. } limits = p_context->get_device_limits(); max_timestamp_query_elements = 256; - { //initialize allocator + { // Initialize allocator. VmaAllocatorCreateInfo allocatorInfo; memset(&allocatorInfo, 0, sizeof(VmaAllocatorCreateInfo)); @@ -9003,13 +9252,13 @@ void RenderingDeviceVulkan::initialize(VulkanContext *p_context, bool p_local_de vmaCreateAllocator(&allocatorInfo, &allocator); } - frames = memnew_arr(Frame, frame_count); + frames.resize(frame_count); frame = 0; - //create setup and frame buffers + // Create setup and frame buffers. for (int i = 0; i < frame_count; i++) { frames[i].index = 0; - { //create command pool, one per frame is recommended + { // Create command pool, one per frame is recommended. VkCommandPoolCreateInfo cmd_pool_info; cmd_pool_info.sType = VK_STRUCTURE_TYPE_COMMAND_POOL_CREATE_INFO; cmd_pool_info.pNext = nullptr; @@ -9020,10 +9269,10 @@ void RenderingDeviceVulkan::initialize(VulkanContext *p_context, bool p_local_de ERR_FAIL_COND_MSG(res, "vkCreateCommandPool failed with error " + itos(res) + "."); } - { //create command buffers + { // Create command buffers. VkCommandBufferAllocateInfo cmdbuf; - //no command buffer exists, create it. + // No command buffer exists, create it. cmdbuf.sType = VK_STRUCTURE_TYPE_COMMAND_BUFFER_ALLOCATE_INFO; cmdbuf.pNext = nullptr; cmdbuf.commandPool = frames[i].command_pool; @@ -9038,7 +9287,7 @@ void RenderingDeviceVulkan::initialize(VulkanContext *p_context, bool p_local_de } { - //create query pool + // Create query pool. VkQueryPoolCreateInfo query_pool_create_info; query_pool_create_info.sType = VK_STRUCTURE_TYPE_QUERY_POOL_CREATE_INFO; query_pool_create_info.flags = 0; @@ -9049,19 +9298,19 @@ void RenderingDeviceVulkan::initialize(VulkanContext *p_context, bool p_local_de vkCreateQueryPool(device, &query_pool_create_info, nullptr, &frames[i].timestamp_pool); - frames[i].timestamp_names = memnew_arr(String, max_timestamp_query_elements); - frames[i].timestamp_cpu_values = memnew_arr(uint64_t, max_timestamp_query_elements); + frames[i].timestamp_names.resize(max_timestamp_query_elements); + frames[i].timestamp_cpu_values.resize(max_timestamp_query_elements); frames[i].timestamp_count = 0; - frames[i].timestamp_result_names = memnew_arr(String, max_timestamp_query_elements); - frames[i].timestamp_cpu_result_values = memnew_arr(uint64_t, max_timestamp_query_elements); - frames[i].timestamp_result_values = memnew_arr(uint64_t, max_timestamp_query_elements); + frames[i].timestamp_result_names.resize(max_timestamp_query_elements); + frames[i].timestamp_cpu_result_values.resize(max_timestamp_query_elements); + frames[i].timestamp_result_values.resize(max_timestamp_query_elements); frames[i].timestamp_result_count = 0; } } { - //begin the first command buffer for the first frame, so - //setting up things can be done in the meantime until swap_buffers(), which is called before advance. + // Begin the first command buffer for the first frame, so + // setting up things can be done in the meantime until swap_buffers(), which is called before advance. VkCommandBufferBeginInfo cmdbuf_begin; cmdbuf_begin.sType = VK_STRUCTURE_TYPE_COMMAND_BUFFER_BEGIN_INFO; cmdbuf_begin.pNext = nullptr; @@ -9074,42 +9323,42 @@ void RenderingDeviceVulkan::initialize(VulkanContext *p_context, bool p_local_de err = vkBeginCommandBuffer(frames[0].draw_command_buffer, &cmdbuf_begin); ERR_FAIL_COND_MSG(err, "vkBeginCommandBuffer failed with error " + itos(err) + "."); if (local_device.is_null()) { - context->set_setup_buffer(frames[0].setup_command_buffer); //append now so it's added before everything else + context->set_setup_buffer(frames[0].setup_command_buffer); // Append now so it's added before everything else. context->append_command_buffer(frames[0].draw_command_buffer); } } - // Note: If adding new project settings here, also duplicate their definition in + // NOTE: If adding new project settings here, also duplicate their definition in // rendering_server.cpp for headless doctool. staging_buffer_block_size = GLOBAL_DEF("rendering/vulkan/staging_buffer/block_size_kb", 256); staging_buffer_block_size = MAX(4u, staging_buffer_block_size); - staging_buffer_block_size *= 1024; //kb -> bytes + staging_buffer_block_size *= 1024; // Kb -> bytes. staging_buffer_max_size = GLOBAL_DEF("rendering/vulkan/staging_buffer/max_size_mb", 128); staging_buffer_max_size = MAX(1u, staging_buffer_max_size); staging_buffer_max_size *= 1024 * 1024; if (staging_buffer_max_size < staging_buffer_block_size * 4) { - //validate enough blocks + // Validate enough blocks. staging_buffer_max_size = staging_buffer_block_size * 4; } texture_upload_region_size_px = GLOBAL_DEF("rendering/vulkan/staging_buffer/texture_upload_region_size_px", 64); texture_upload_region_size_px = nearest_power_of_2_templated(texture_upload_region_size_px); - frames_drawn = frame_count; //start from frame count, so everything else is immediately old + frames_drawn = frame_count; // Start from frame count, so everything else is immediately old. - //ensure current staging block is valid and at least one per frame exists + // Ensure current staging block is valid and at least one per frame exists. staging_buffer_current = 0; staging_buffer_used = false; for (int i = 0; i < frame_count; i++) { - //staging was never used, create a block + // Staging was never used, create a block. Error err = _insert_staging_block(); ERR_CONTINUE(err != OK); } max_descriptors_per_pool = GLOBAL_DEF("rendering/vulkan/descriptor_pools/max_descriptors_per_pool", 64); - //check to make sure DescriptorPoolKey is good + // Check to make sure DescriptorPoolKey is good. static_assert(sizeof(uint64_t) * 3 >= UNIFORM_TYPE_MAX * sizeof(uint16_t)); draw_list = nullptr; @@ -9130,6 +9379,11 @@ void RenderingDeviceVulkan::_free_rids(T &p_owner, const char *p_type) { WARN_PRINT(vformat("%d RIDs of type \"%s\" were leaked.", owned.size(), p_type)); } for (const RID &E : owned) { +#ifdef DEV_ENABLED + if (resource_names.has(E)) { + print_line(String(" - ") + resource_names[E]); + } +#endif free(E); } } @@ -9139,7 +9393,7 @@ void RenderingDeviceVulkan::capture_timestamp(const String &p_name) { ERR_FAIL_COND_MSG(draw_list != nullptr, "Capturing timestamps during draw list creation is not allowed. Offending timestamp was: " + p_name); ERR_FAIL_COND(frames[frame].timestamp_count >= max_timestamp_query_elements); - //this should be optional for profiling, else it will slow things down + // This should be optional for profiling, else it will slow things down. { VkMemoryBarrier memoryBarrier; @@ -9265,7 +9519,7 @@ uint64_t RenderingDeviceVulkan::get_driver_resource(DriverResource p_resource, R return uint64_t(render_pipeline->pipeline); } break; default: { - // not supported for this driver + // Not supported for this driver. return 0; } break; } @@ -9302,9 +9556,9 @@ static void mult64to128(uint64_t u, uint64_t v, uint64_t &h, uint64_t &l) { uint64_t RenderingDeviceVulkan::get_captured_timestamp_gpu_time(uint32_t p_index) const { ERR_FAIL_UNSIGNED_INDEX_V(p_index, frames[frame].timestamp_result_count, 0); - // this sucks because timestampPeriod multiplier is a float, while the timestamp is 64 bits nanosecs. - // so, in cases like nvidia which give you enormous numbers and 1 as multiplier, multiplying is next to impossible - // need to do 128 bits fixed point multiplication to get the right value + // This sucks because timestampPeriod multiplier is a float, while the timestamp is 64 bits nanosecs. + // So, in cases like nvidia which give you enormous numbers and 1 as multiplier, multiplying is next to impossible. + // Need to do 128 bits fixed point multiplication to get the right value. uint64_t shift_bits = 16; @@ -9327,7 +9581,7 @@ String RenderingDeviceVulkan::get_captured_timestamp_name(uint32_t p_index) cons return frames[frame].timestamp_result_names[p_index]; } -uint64_t RenderingDeviceVulkan::limit_get(Limit p_limit) { +uint64_t RenderingDeviceVulkan::limit_get(Limit p_limit) const { switch (p_limit) { case LIMIT_MAX_BOUND_UNIFORM_SETS: return limits.maxBoundDescriptorSets; @@ -9397,7 +9651,18 @@ uint64_t RenderingDeviceVulkan::limit_get(Limit p_limit) { return limits.maxComputeWorkGroupSize[1]; case LIMIT_MAX_COMPUTE_WORKGROUP_SIZE_Z: return limits.maxComputeWorkGroupSize[2]; - + case LIMIT_SUBGROUP_SIZE: { + VulkanContext::SubgroupCapabilities subgroup_capabilities = context->get_subgroup_capabilities(); + return subgroup_capabilities.size; + } + case LIMIT_SUBGROUP_IN_SHADERS: { + VulkanContext::SubgroupCapabilities subgroup_capabilities = context->get_subgroup_capabilities(); + return subgroup_capabilities.supported_stages_flags_rd(); + } + case LIMIT_SUBGROUP_OPERATIONS: { + VulkanContext::SubgroupCapabilities subgroup_capabilities = context->get_subgroup_capabilities(); + return subgroup_capabilities.supported_operations_flags_rd(); + } default: ERR_FAIL_V(0); } @@ -9406,7 +9671,7 @@ uint64_t RenderingDeviceVulkan::limit_get(Limit p_limit) { } void RenderingDeviceVulkan::finalize() { - //free all resources + // Free all resources. _flush(false); @@ -9424,7 +9689,7 @@ void RenderingDeviceVulkan::finalize() { _free_rids(framebuffer_owner, "Framebuffer"); _free_rids(sampler_owner, "Sampler"); { - //for textures it's a bit more difficult because they may be shared + // For textures it's a bit more difficult because they may be shared. List<RID> owned; texture_owner.get_owned_list(&owned); if (owned.size()) { @@ -9433,64 +9698,69 @@ void RenderingDeviceVulkan::finalize() { } else { WARN_PRINT(vformat("%d RIDs of type \"Texture\" were leaked.", owned.size())); } - //free shared first + // Free shared first. for (List<RID>::Element *E = owned.front(); E;) { List<RID>::Element *N = E->next(); if (texture_is_shared(E->get())) { +#ifdef DEV_ENABLED + if (resource_names.has(E->get())) { + print_line(String(" - ") + resource_names[E->get()]); + } +#endif free(E->get()); owned.erase(E); } E = N; } - //free non shared second, this will avoid an error trying to free unexisting textures due to dependencies. + // Free non shared second, this will avoid an error trying to free unexisting textures due to dependencies. for (const RID &E : owned) { +#ifdef DEV_ENABLED + if (resource_names.has(E)) { + print_line(String(" - ") + resource_names[E]); + } +#endif free(E); } } } - //free everything pending + // Free everything pending. for (int i = 0; i < frame_count; i++) { int f = (frame + i) % frame_count; _free_pending_resources(f); vkDestroyCommandPool(device, frames[i].command_pool, nullptr); vkDestroyQueryPool(device, frames[i].timestamp_pool, nullptr); - memdelete_arr(frames[i].timestamp_names); - memdelete_arr(frames[i].timestamp_cpu_values); - memdelete_arr(frames[i].timestamp_result_names); - memdelete_arr(frames[i].timestamp_result_values); - memdelete_arr(frames[i].timestamp_cpu_result_values); } for (int i = 0; i < split_draw_list_allocators.size(); i++) { vkDestroyCommandPool(device, split_draw_list_allocators[i].command_pool, nullptr); } - memdelete_arr(frames); + frames.clear(); for (int i = 0; i < staging_buffer_blocks.size(); i++) { vmaDestroyBuffer(allocator, staging_buffer_blocks[i].buffer, staging_buffer_blocks[i].allocation); } while (small_allocs_pools.size()) { - Map<uint32_t, VmaPool>::Element *E = small_allocs_pools.front(); - vmaDestroyPool(allocator, E->get()); - small_allocs_pools.erase(E); + HashMap<uint32_t, VmaPool>::Iterator E = small_allocs_pools.begin(); + vmaDestroyPool(allocator, E->value); + small_allocs_pools.remove(E); } vmaDestroyAllocator(allocator); while (vertex_formats.size()) { - Map<VertexFormatID, VertexDescriptionCache>::Element *temp = vertex_formats.front(); - memdelete_arr(temp->get().bindings); - memdelete_arr(temp->get().attributes); - vertex_formats.erase(temp); + HashMap<VertexFormatID, VertexDescriptionCache>::Iterator temp = vertex_formats.begin(); + memdelete_arr(temp->value.bindings); + memdelete_arr(temp->value.attributes); + vertex_formats.remove(temp); } - for (int i = 0; i < framebuffer_formats.size(); i++) { - vkDestroyRenderPass(device, framebuffer_formats[i].render_pass, nullptr); + for (KeyValue<FramebufferFormatID, FramebufferFormat> &E : framebuffer_formats) { + vkDestroyRenderPass(device, E.value.render_pass, nullptr); } framebuffer_formats.clear(); - //all these should be clear at this point + // All these should be clear at this point. ERR_FAIL_COND(descriptor_pools.size()); ERR_FAIL_COND(dependency_map.size()); ERR_FAIL_COND(reverse_dependency_map.size()); @@ -9502,6 +9772,25 @@ RenderingDevice *RenderingDeviceVulkan::create_local_device() { return rd; } +bool RenderingDeviceVulkan::has_feature(const Features p_feature) const { + switch (p_feature) { + case SUPPORTS_MULTIVIEW: { + VulkanContext::MultiviewCapabilities multiview_capabilies = context->get_multiview_capabilities(); + return multiview_capabilies.is_supported && multiview_capabilies.max_view_count > 1; + } break; + case SUPPORTS_FSR_HALF_FLOAT: { + return context->get_shader_capabilities().shader_float16_is_supported && context->get_storage_buffer_capabilities().storage_buffer_16_bit_access_is_supported; + } break; + case SUPPORTS_ATTACHMENT_VRS: { + VulkanContext::VRSCapabilities vrs_capabilities = context->get_vrs_capabilities(); + return vrs_capabilities.attachment_vrs_supported; + } break; + default: { + return false; + } + } +} + RenderingDeviceVulkan::RenderingDeviceVulkan() { device_capabilities.device_family = DEVICE_VULKAN; } diff --git a/drivers/vulkan/rendering_device_vulkan.h b/drivers/vulkan/rendering_device_vulkan.h index 7d9bd19309..6572de7c52 100644 --- a/drivers/vulkan/rendering_device_vulkan.h +++ b/drivers/vulkan/rendering_device_vulkan.h @@ -96,13 +96,13 @@ class RenderingDeviceVulkan : public RenderingDevice { ID_TYPE_SPLIT_DRAW_LIST, ID_TYPE_COMPUTE_LIST, ID_TYPE_MAX, - ID_BASE_SHIFT = 58 //5 bits for ID types + ID_BASE_SHIFT = 58 // 5 bits for ID types. }; VkDevice device = VK_NULL_HANDLE; - Map<RID, Set<RID>> dependency_map; //IDs to IDs that depend on it - Map<RID, Set<RID>> reverse_dependency_map; //same as above, but in reverse + HashMap<RID, HashSet<RID>> dependency_map; // IDs to IDs that depend on it. + HashMap<RID, HashSet<RID>> reverse_dependency_map; // Same as above, but in reverse. void _add_dependency(RID p_id, RID p_depends_on); void _free_dependencies(RID p_id); @@ -152,7 +152,7 @@ class RenderingDeviceVulkan : public RenderingDevice { uint32_t read_aspect_mask = 0; uint32_t barrier_aspect_mask = 0; - bool bound = false; //bound to framebffer + bool bound = false; // Bound to framebffer. RID owner; }; @@ -206,7 +206,7 @@ class RenderingDeviceVulkan : public RenderingDevice { uint64_t staging_buffer_max_size = 0; bool staging_buffer_used = false; - Error _staging_buffer_allocate(uint32_t p_amount, uint32_t p_required_align, uint32_t &r_alloc_offset, uint32_t &r_alloc_size, bool p_can_segment = true, bool p_on_draw_command_buffer = false); + Error _staging_buffer_allocate(uint32_t p_amount, uint32_t p_required_align, uint32_t &r_alloc_offset, uint32_t &r_alloc_size, bool p_can_segment = true); Error _insert_staging_block(); struct Buffer { @@ -214,7 +214,7 @@ class RenderingDeviceVulkan : public RenderingDevice { uint32_t usage = 0; VkBuffer buffer = VK_NULL_HANDLE; VmaAllocation allocation = nullptr; - VkDescriptorBufferInfo buffer_info; //used for binding + VkDescriptorBufferInfo buffer_info; // Used for binding. Buffer() { } }; @@ -241,6 +241,7 @@ class RenderingDeviceVulkan : public RenderingDevice { 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; @@ -255,7 +256,7 @@ class RenderingDeviceVulkan : public RenderingDevice { const FramebufferPass *key_pass_ptr = p_key.passes.ptr(); for (uint32_t i = 0; i < pass_size; i++) { - { //compare color attachments + { // 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) { @@ -270,7 +271,7 @@ class RenderingDeviceVulkan : public RenderingDevice { } } } - { //compare input attachments + { // 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) { @@ -285,7 +286,7 @@ class RenderingDeviceVulkan : public RenderingDevice { } } } - { //compare resolve attachments + { // 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) { @@ -300,7 +301,7 @@ class RenderingDeviceVulkan : public RenderingDevice { } } } - { //compare preserve attachments + { // 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) { @@ -342,22 +343,22 @@ class RenderingDeviceVulkan : public RenderingDevice { } } - return false; //equal + return false; // Equal. } }; 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; + RBMap<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) + const RBMap<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). Vector<TextureSamples> pass_samples; - uint32_t view_count = 1; // number of views + uint32_t view_count = 1; // Number of views. }; - Map<FramebufferFormatID, FramebufferFormat> framebuffer_formats; + HashMap<FramebufferFormatID, FramebufferFormat> framebuffer_formats; struct Framebuffer { FramebufferFormatID format_id = 0; @@ -391,14 +392,16 @@ class RenderingDeviceVulkan : public RenderingDevice { uint32_t storage_mask = 0; Vector<RID> texture_ids; + InvalidationCallback invalidated_callback = nullptr; + void *invalidated_callback_userdata = nullptr; struct Version { VkFramebuffer framebuffer = VK_NULL_HANDLE; - VkRenderPass render_pass = VK_NULL_HANDLE; //this one is owned + VkRenderPass render_pass = VK_NULL_HANDLE; // This one is owned. uint32_t subpass_count = 1; }; - Map<VersionKey, Version> framebuffers; + RBMap<VersionKey, Version> framebuffers; Size2 size; uint32_t view_count; }; @@ -451,23 +454,23 @@ class RenderingDeviceVulkan : public RenderingDevice { return false; } } - return true; //they are equal + return true; // They are equal. } } uint32_t hash() const { int vdc = vertex_formats.size(); - uint32_t h = hash_djb2_one_32(vdc); + uint32_t h = hash_murmur3_one_32(vdc); const VertexAttribute *ptr = vertex_formats.ptr(); for (int i = 0; i < vdc; i++) { const VertexAttribute &vd = ptr[i]; - h = hash_djb2_one_32(vd.location, h); - h = hash_djb2_one_32(vd.offset, h); - h = hash_djb2_one_32(vd.format, h); - h = hash_djb2_one_32(vd.stride, h); - h = hash_djb2_one_32(vd.frequency, h); + h = hash_murmur3_one_32(vd.location, h); + h = hash_murmur3_one_32(vd.offset, h); + h = hash_murmur3_one_32(vd.format, h); + h = hash_murmur3_one_32(vd.stride, h); + h = hash_murmur3_one_32(vd.frequency, h); } - return h; + return hash_fmix32(h); } }; @@ -488,7 +491,7 @@ class RenderingDeviceVulkan : public RenderingDevice { VkPipelineVertexInputStateCreateInfo create_info; }; - Map<VertexFormatID, VertexDescriptionCache> vertex_formats; + HashMap<VertexFormatID, VertexDescriptionCache> vertex_formats; struct VertexArray { RID buffer; @@ -496,14 +499,14 @@ class RenderingDeviceVulkan : public RenderingDevice { int vertex_count = 0; uint32_t max_instances_allowed = 0; - Vector<VkBuffer> buffers; //not owned, just referenced + Vector<VkBuffer> buffers; // Not owned, just referenced. Vector<VkDeviceSize> offsets; }; RID_Owner<VertexArray, true> vertex_array_owner; struct IndexBuffer : public Buffer { - uint32_t max_index = 0; //used for validation + uint32_t max_index = 0; // Used for validation. uint32_t index_count = 0; VkIndexType index_type = VK_INDEX_TYPE_NONE_NV; bool supports_restart_indices = false; @@ -512,8 +515,8 @@ class RenderingDeviceVulkan : public RenderingDevice { RID_Owner<IndexBuffer, true> index_buffer_owner; struct IndexArray { - uint32_t max_index = 0; //remember the maximum index here too, for validation - VkBuffer buffer; //not owned, inherited from index buffer + uint32_t max_index = 0; // Remember the maximum index here too, for validation. + VkBuffer buffer; // Not owned, inherited from index buffer. uint32_t offset = 0; uint32_t indices = 0; VkIndexType index_type = VK_INDEX_TYPE_NONE_NV; @@ -544,12 +547,13 @@ class RenderingDeviceVulkan : public RenderingDevice { struct UniformInfo { UniformType type = UniformType::UNIFORM_TYPE_MAX; + bool writable = false; int binding = 0; uint32_t stages = 0; - int length = 0; //size of arrays (in total elements), or ubos (in bytes * total elements) + int length = 0; // Size of arrays (in total elements), or ubos (in bytes * total elements). bool operator!=(const UniformInfo &p_info) const { - return (binding != p_info.binding || type != p_info.type || stages != p_info.stages || length != p_info.length); + return (binding != p_info.binding || type != p_info.type || writable != p_info.writable || stages != p_info.stages || length != p_info.length); } bool operator<(const UniformInfo &p_info) const { @@ -559,6 +563,9 @@ class RenderingDeviceVulkan : public RenderingDevice { if (type != p_info.type) { return type < p_info.type; } + if (writable != p_info.writable) { + return writable < p_info.writable; + } if (stages != p_info.stages) { return stages < p_info.stages; } @@ -592,7 +599,7 @@ class RenderingDeviceVulkan : public RenderingDevice { // Always grows, never shrinks, ensuring unique IDs, but we assume // the amount of formats will never be a problem, as the amount of shaders // in a game is limited. - Map<UniformSetFormat, uint32_t> uniform_set_format_cache; + RBMap<UniformSetFormat, uint32_t> uniform_set_format_cache; // Shaders in Vulkan are just pretty much // precompiled blocks of SPIR-V bytecode. They @@ -615,7 +622,7 @@ class RenderingDeviceVulkan : public RenderingDevice { VkDescriptorSetLayout descriptor_set_layout = VK_NULL_HANDLE; }; - uint32_t vertex_input_mask = 0; //inputs used, this is mostly for validation + uint32_t vertex_input_mask = 0; // Inputs used, this is mostly for validation. uint32_t fragment_output_mask = 0; struct PushConstant { @@ -633,13 +640,12 @@ class RenderingDeviceVulkan : public RenderingDevice { }; 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; - String name; //used for debug + String name; // Used for debug. }; String _shader_uniform_debug(RID p_shader, int p_set = -1); @@ -702,7 +708,7 @@ class RenderingDeviceVulkan : public RenderingDevice { uint32_t usage; }; - Map<DescriptorPoolKey, Set<DescriptorPool *>> descriptor_pools; + RBMap<DescriptorPoolKey, HashSet<DescriptorPool *>> descriptor_pools; uint32_t max_descriptors_per_pool = 0; DescriptorPool *_descriptor_pool_allocate(const DescriptorPoolKey &p_key); @@ -711,7 +717,7 @@ class RenderingDeviceVulkan : public RenderingDevice { RID_Owner<Buffer, true> uniform_buffer_owner; RID_Owner<Buffer, true> storage_buffer_owner; - //texture buffer needs a view + // Texture buffer needs a view. struct TextureBuffer { Buffer buffer; VkBufferView view = VK_NULL_HANDLE; @@ -734,16 +740,16 @@ class RenderingDeviceVulkan : public RenderingDevice { DescriptorPool *pool = nullptr; DescriptorPoolKey pool_key; VkDescriptorSet descriptor_set = VK_NULL_HANDLE; - //VkPipelineLayout pipeline_layout; //not owned, inherited from shader + //VkPipelineLayout pipeline_layout; // Not owned, inherited from shader. struct AttachableTexture { uint32_t bind; RID texture; }; - 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; + 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. + InvalidationCallback invalidated_callback = nullptr; void *invalidated_callback_userdata = nullptr; }; @@ -765,7 +771,7 @@ class RenderingDeviceVulkan : public RenderingDevice { // was not supplied as intended. struct RenderPipeline { - //Cached values for validation + // Cached values for validation. #ifdef DEBUG_ENABLED struct Validation { FramebufferFormatID framebuffer_format = 0; @@ -777,10 +783,10 @@ class RenderingDeviceVulkan : public RenderingDevice { uint32_t primitive_divisor = 0; } validation; #endif - //Actual pipeline + // Actual pipeline. RID shader; Vector<uint32_t> set_formats; - VkPipelineLayout pipeline_layout = VK_NULL_HANDLE; // not owned, needed for push constants + VkPipelineLayout pipeline_layout = VK_NULL_HANDLE; // Not owned, needed for push constants. VkPipeline pipeline = VK_NULL_HANDLE; uint32_t push_constant_size = 0; uint32_t push_constant_stages = 0; @@ -791,7 +797,7 @@ class RenderingDeviceVulkan : public RenderingDevice { struct ComputePipeline { RID shader; Vector<uint32_t> set_formats; - VkPipelineLayout pipeline_layout = VK_NULL_HANDLE; // not owned, needed for push constants + VkPipelineLayout pipeline_layout = VK_NULL_HANDLE; // Not owned, needed for push constants. VkPipeline pipeline = VK_NULL_HANDLE; uint32_t push_constant_size = 0; uint32_t push_constant_stages = 0; @@ -817,7 +823,7 @@ class RenderingDeviceVulkan : public RenderingDevice { struct SplitDrawListAllocator { VkCommandPool command_pool = VK_NULL_HANDLE; - Vector<VkCommandBuffer> command_buffers; //one for each frame + Vector<VkCommandBuffer> command_buffers; // One for each frame. }; Vector<SplitDrawListAllocator> split_draw_list_allocators; @@ -866,11 +872,9 @@ class RenderingDeviceVulkan : public RenderingDevice { uint32_t pipeline_dynamic_state = 0; VertexFormatID pipeline_vertex_format = INVALID_ID; RID pipeline_shader; - uint32_t invalid_set_from = 0; bool pipeline_uses_restart_indices = false; uint32_t pipeline_primitive_divisor = 0; uint32_t pipeline_primitive_minimum = 0; - Vector<uint32_t> pipeline_set_formats; uint32_t pipeline_push_constant_size = 0; bool pipeline_push_constant_supplied = false; } validation; @@ -886,8 +890,8 @@ 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; + VkRenderPass draw_list_render_pass = VK_NULL_HANDLE; + VkFramebuffer draw_list_vkframebuffer = VK_NULL_HANDLE; #ifdef DEBUG_ENABLED FramebufferFormatID draw_list_framebuffer_format = INVALID_ID; #endif @@ -923,7 +927,7 @@ class RenderingDeviceVulkan : public RenderingDevice { }; struct State { - Set<Texture *> textures_to_sampled_layout; + HashSet<Texture *> textures_to_sampled_layout; SetState sets[MAX_UNIFORM_SETS]; uint32_t set_count = 0; RID pipeline; @@ -944,7 +948,6 @@ class RenderingDeviceVulkan : public RenderingDevice { bool pipeline_active = false; RID pipeline_shader; uint32_t invalid_set_from = 0; - Vector<uint32_t> pipeline_set_formats; uint32_t pipeline_push_constant_size = 0; bool pipeline_push_constant_supplied = false; } validation; @@ -972,7 +975,7 @@ class RenderingDeviceVulkan : public RenderingDevice { // when the frame is cycled. struct Frame { - //list in usage order, from last to free to first to free + // List in usage order, from last to free to first to free. List<Buffer> buffers_to_dispose_of; List<Texture> textures_to_dispose_of; List<Framebuffer> framebuffers_to_dispose_of; @@ -984,8 +987,8 @@ class RenderingDeviceVulkan : public RenderingDevice { List<ComputePipeline> compute_pipelines_to_dispose_of; VkCommandPool command_pool = VK_NULL_HANDLE; - VkCommandBuffer setup_command_buffer = VK_NULL_HANDLE; //used at the beginning of every frame for set-up - VkCommandBuffer draw_command_buffer = VK_NULL_HANDLE; //used at the beginning of every frame for set-up + VkCommandBuffer setup_command_buffer = VK_NULL_HANDLE; // Used at the beginning of every frame for set-up. + VkCommandBuffer draw_command_buffer = VK_NULL_HANDLE; // Used at the beginning of every frame for set-up. struct Timestamp { String description; @@ -994,21 +997,21 @@ class RenderingDeviceVulkan : public RenderingDevice { VkQueryPool timestamp_pool; - String *timestamp_names = nullptr; - uint64_t *timestamp_cpu_values = nullptr; + TightLocalVector<String> timestamp_names; + TightLocalVector<uint64_t> timestamp_cpu_values; uint32_t timestamp_count = 0; - String *timestamp_result_names = nullptr; - uint64_t *timestamp_cpu_result_values = nullptr; - uint64_t *timestamp_result_values = nullptr; + TightLocalVector<String> timestamp_result_names; + TightLocalVector<uint64_t> timestamp_cpu_result_values; + TightLocalVector<uint64_t> timestamp_result_values; uint32_t timestamp_result_count = 0; uint64_t index = 0; }; uint32_t max_timestamp_query_elements = 0; - Frame *frames = nullptr; //frames available, for main device they are cycled (usually 3), for local devices only 1 - int frame = 0; //current frame - int frame_count = 0; //total amount of frames + TightLocalVector<Frame> frames; // Frames available, for main device they are cycled (usually 3), for local devices only 1. + int frame = 0; // Current frame. + int frame_count = 0; // Total amount of frames. uint64_t frames_drawn = 0; RID local_device; bool local_device_processing = false; @@ -1016,7 +1019,7 @@ class RenderingDeviceVulkan : public RenderingDevice { void _free_pending_resources(int p_frame); VmaAllocator allocator = nullptr; - Map<uint32_t, VmaPool> small_allocs_pools; + HashMap<uint32_t, VmaPool> small_allocs_pools; VmaPool _find_or_create_small_allocs_pool(uint32_t p_mem_type_index); VulkanContext *context = nullptr; @@ -1035,6 +1038,10 @@ class RenderingDeviceVulkan : public RenderingDevice { void _finalize_command_bufers(); void _begin_frame(); +#ifdef DEV_ENABLED + HashMap<RID, String> resource_names; +#endif + public: virtual RID texture_create(const TextureFormat &p_format, const TextureView &p_view, const Vector<Vector<uint8_t>> &p_data = Vector<Vector<uint8_t>>()); virtual RID texture_create_shared(const TextureView &p_view, RID p_with_texture); @@ -1058,13 +1065,15 @@ public: /*********************/ 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_multipass(const Vector<AttachmentFormat> &p_attachments, const 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, uint32_t p_pass = 0); 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_multipass(const Vector<RID> &p_texture_attachments, const 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 bool framebuffer_is_valid(RID p_framebuffer) const; + virtual void framebuffer_set_invalidation_callback(RID p_framebuffer, InvalidationCallback p_callback, void *p_userdata); virtual FramebufferFormatID framebuffer_get_format(RID p_framebuffer); @@ -1080,7 +1089,7 @@ public: virtual RID vertex_buffer_create(uint32_t p_size_bytes, const Vector<uint8_t> &p_data = Vector<uint8_t>(), bool p_use_as_storage = false); - // Internally reference counted, this ID is warranted to be unique for the same description, but needs to be freed as many times as it was allocated + // Internally reference counted, this ID is warranted to be unique for the same description, but needs to be freed as many times as it was allocated. virtual VertexFormatID vertex_format_create(const Vector<VertexAttribute> &p_vertex_formats); virtual RID vertex_array_create(uint32_t p_vertex_count, VertexFormatID p_vertex_format, const Vector<RID> &p_src_buffers); @@ -1109,9 +1118,9 @@ 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 void uniform_set_set_invalidation_callback(RID p_uniform_set, InvalidationCallback 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_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); virtual Vector<uint8_t> buffer_get_data(RID p_buffer); @@ -1203,16 +1212,16 @@ public: /**** Limits ****/ /****************/ - virtual uint64_t limit_get(Limit p_limit); + virtual uint64_t limit_get(Limit p_limit) const; virtual void prepare_screen_for_drawing(); void initialize(VulkanContext *p_context, bool p_local_device = false); void finalize(); - virtual void swap_buffers(); //for main device + virtual void swap_buffers(); // For main device. - virtual void submit(); //for local device - virtual void sync(); //for local device + virtual void submit(); // For local device. + virtual void sync(); // For local device. virtual uint32_t get_frame_delay() const; @@ -1229,10 +1238,13 @@ public: virtual String get_device_vendor_name() const; virtual String get_device_name() const; virtual RenderingDevice::DeviceType get_device_type() const; + virtual String get_device_api_version() const; virtual String get_device_pipeline_cache_uuid() const; virtual uint64_t get_driver_resource(DriverResource p_resource, RID p_rid = RID(), uint64_t p_index = 0); + virtual bool has_feature(const Features p_feature) const; + RenderingDeviceVulkan(); ~RenderingDeviceVulkan(); }; diff --git a/drivers/vulkan/vulkan_context.cpp b/drivers/vulkan/vulkan_context.cpp index a09a757842..afc3e78372 100644 --- a/drivers/vulkan/vulkan_context.cpp +++ b/drivers/vulkan/vulkan_context.cpp @@ -48,6 +48,18 @@ VulkanHooks *VulkanContext::vulkan_hooks = nullptr; +VkResult VulkanContext::vkCreateRenderPass2KHR(VkDevice device, const VkRenderPassCreateInfo2 *pCreateInfo, const VkAllocationCallbacks *pAllocator, VkRenderPass *pRenderPass) { + if (fpCreateRenderPass2KHR == nullptr) { + fpCreateRenderPass2KHR = (PFN_vkCreateRenderPass2KHR)vkGetInstanceProcAddr(inst, "vkCreateRenderPass2KHR"); + } + + if (fpCreateRenderPass2KHR == nullptr) { + return VK_ERROR_EXTENSION_NOT_PRESENT; + } else { + return (fpCreateRenderPass2KHR)(device, pCreateInfo, pAllocator, pRenderPass); + } +} + VKAPI_ATTR VkBool32 VKAPI_CALL VulkanContext::_debug_messenger_callback( VkDebugUtilsMessageSeverityFlagBitsEXT messageSeverity, VkDebugUtilsMessageTypeFlagsEXT messageType, @@ -72,20 +84,20 @@ VKAPI_ATTR VkBool32 VKAPI_CALL VulkanContext::_debug_messenger_callback( strstr(pCallbackData->pMessage, "must be a memory object") != nullptr) { return VK_FALSE; } - /* - // This is a valid warning because its illegal in Vulkan, but in practice it should work according to VK_KHR_maintenance2 - if (strstr(pCallbackData->pMessage, "VK_FORMAT_E5B9G9R9_UFLOAT_PACK32 with tiling VK_IMAGE_TILING_OPTIMAL does not support usage that includes VK_IMAGE_USAGE_STORAGE_BIT") != nullptr) { - return VK_FALSE; - } - if (strstr(pCallbackData->pMessage, "VK_FORMAT_R4G4B4A4_UNORM_PACK16 with tiling VK_IMAGE_TILING_OPTIMAL does not support usage that includes VK_IMAGE_USAGE_STORAGE_BIT") != nullptr) { - return VK_FALSE; - } -*/ // Workaround for Vulkan-Loader usability bug: https://github.com/KhronosGroup/Vulkan-Loader/issues/262. if (strstr(pCallbackData->pMessage, "wrong ELF class: ELFCLASS32") != nullptr) { return VK_FALSE; } + +#ifdef WINDOWS_ENABLED + // Some software installs Vulkan overlays in Windows registry and never cleans them up on uninstall. + // So we get spammy error level messages from the loader about those - make them verbose instead. + if (strstr(pCallbackData->pMessage, "loader_get_json: Failed to open JSON file") != nullptr) { + messageSeverity = VK_DEBUG_UTILS_MESSAGE_SEVERITY_VERBOSE_BIT_EXT; + } +#endif + if (pCallbackData->pMessageIdName && strstr(pCallbackData->pMessageIdName, "UNASSIGNED-CoreValidation-DrawState-ClearCmdBeforeDraw") != nullptr) { return VK_FALSE; } @@ -215,17 +227,17 @@ VkBool32 VulkanContext::_check_layers(uint32_t check_count, const char *const *c Error VulkanContext::_get_preferred_validation_layers(uint32_t *count, const char *const **names) { static const LocalVector<LocalVector<const char *>> instance_validation_layers_alt{ - // Preferred set of validation layers + // Preferred set of validation layers. { "VK_LAYER_KHRONOS_validation" }, - // Alternative (deprecated, removed in SDK 1.1.126.0) set of validation layers + // 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 + // 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 + // Clear out-arguments. *count = 0; if (names != nullptr) { *names = nullptr; @@ -269,7 +281,7 @@ 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. + // 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; @@ -279,15 +291,15 @@ Error VulkanContext::_obtain_vulkan_version() { vulkan_minor = VK_API_VERSION_MINOR(api_version); vulkan_patch = VK_API_VERSION_PATCH(api_version); } 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 + // 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 { print_line("vkEnumerateInstanceVersion not available, assuming Vulkan 1.0."); } - // we don't go above 1.2 + // We don't go above 1.2. if ((vulkan_major > 1) || (vulkan_major == 1 && vulkan_minor > 2)) { vulkan_major = 1; vulkan_minor = 2; @@ -303,7 +315,7 @@ Error VulkanContext::_initialize_extensions() { enabled_extension_count = 0; enabled_debug_utils = false; enabled_debug_report = false; - /* Look for instance extensions */ + // Look for instance extensions. VkBool32 surfaceExtFound = 0; VkBool32 platformSurfaceExtFound = 0; memset(extension_names, 0, sizeof(extension_names)); @@ -403,7 +415,7 @@ String VulkanContext::SubgroupCapabilities::supported_stages_desc() const { res += ", STAGE_COMPUTE"; } - /* these are not defined on Android GRMBL */ + // These are not defined on Android GRMBL. if (supportedStages & 0x00000100 /* VK_SHADER_STAGE_RAYGEN_BIT_KHR */) { res += ", STAGE_RAYGEN_KHR"; } @@ -429,7 +441,7 @@ String VulkanContext::SubgroupCapabilities::supported_stages_desc() const { res += ", STAGE_MESH_NV"; } - return res.substr(2); // remove first ", " + return res.substr(2); // Remove first ", ". } uint32_t VulkanContext::SubgroupCapabilities::supported_operations_flags_rd() const { @@ -494,19 +506,22 @@ String VulkanContext::SubgroupCapabilities::supported_operations_desc() const { res += ", FEATURE_PARTITIONED_NV"; } - return res.substr(2); // remove first ", " + 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. + // 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) + // 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 + // Assume not supported until proven otherwise. + vrs_capabilities.pipeline_vrs_supported = false; + vrs_capabilities.primitive_vrs_supported = false; + vrs_capabilities.attachment_vrs_supported = false; multiview_capabilities.is_supported = false; multiview_capabilities.geometry_shader_is_supported = false; multiview_capabilities.tessellation_shader_is_supported = false; @@ -523,17 +538,25 @@ Error VulkanContext::_check_capabilities() { storage_buffer_capabilities.storage_push_constant_16_is_supported = false; storage_buffer_capabilities.storage_input_output_16 = false; - // check for extended features + // Check for extended features. PFN_vkGetPhysicalDeviceFeatures2 vkGetPhysicalDeviceFeatures2_func = (PFN_vkGetPhysicalDeviceFeatures2)vkGetInstanceProcAddr(inst, "vkGetPhysicalDeviceFeatures2"); if (vkGetPhysicalDeviceFeatures2_func == nullptr) { - // In Vulkan 1.0 might be accessible under its original extension name + // In Vulkan 1.0 might be accessible under its original extension name. vkGetPhysicalDeviceFeatures2_func = (PFN_vkGetPhysicalDeviceFeatures2)vkGetInstanceProcAddr(inst, "vkGetPhysicalDeviceFeatures2KHR"); } if (vkGetPhysicalDeviceFeatures2_func != nullptr) { - // check our extended features + // Check our extended features. + VkPhysicalDeviceFragmentShadingRateFeaturesKHR vrs_features = { + /*sType*/ VK_STRUCTURE_TYPE_PHYSICAL_DEVICE_FRAGMENT_SHADING_RATE_FEATURES_KHR, + /*pNext*/ nullptr, + /*pipelineFragmentShadingRate*/ false, + /*primitiveFragmentShadingRate*/ false, + /*attachmentFragmentShadingRate*/ false, + }; + VkPhysicalDeviceShaderFloat16Int8FeaturesKHR shader_features = { /*sType*/ VK_STRUCTURE_TYPE_PHYSICAL_DEVICE_SHADER_FLOAT16_INT8_FEATURES_KHR, - /*pNext*/ nullptr, + /*pNext*/ &vrs_features, /*shaderFloat16*/ false, /*shaderInt8*/ false, }; @@ -561,6 +584,10 @@ Error VulkanContext::_check_capabilities() { vkGetPhysicalDeviceFeatures2_func(gpu, &device_features); + vrs_capabilities.pipeline_vrs_supported = vrs_features.pipelineFragmentShadingRate; + vrs_capabilities.primitive_vrs_supported = vrs_features.primitiveFragmentShadingRate; + vrs_capabilities.attachment_vrs_supported = vrs_features.attachmentFragmentShadingRate; + 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; @@ -574,41 +601,72 @@ Error VulkanContext::_check_capabilities() { storage_buffer_capabilities.storage_input_output_16 = storage_feature.storageInputOutput16; } - // check extended properties + // 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 + // 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) { + VkPhysicalDeviceFragmentShadingRatePropertiesKHR vrsProperties; VkPhysicalDeviceMultiviewProperties multiviewProperties; VkPhysicalDeviceSubgroupProperties subgroupProperties; VkPhysicalDeviceProperties2 physicalDeviceProperties; + void *nextptr = nullptr; subgroupProperties.sType = VK_STRUCTURE_TYPE_PHYSICAL_DEVICE_SUBGROUP_PROPERTIES; - subgroupProperties.pNext = nullptr; - - physicalDeviceProperties.sType = VK_STRUCTURE_TYPE_PHYSICAL_DEVICE_PROPERTIES_2; + subgroupProperties.pNext = nextptr; + nextptr = &subgroupProperties; if (multiview_capabilities.is_supported) { multiviewProperties.sType = VK_STRUCTURE_TYPE_PHYSICAL_DEVICE_MULTIVIEW_PROPERTIES; - multiviewProperties.pNext = &subgroupProperties; + multiviewProperties.pNext = nextptr; - physicalDeviceProperties.pNext = &multiviewProperties; - } else { - physicalDeviceProperties.pNext = &subgroupProperties; + nextptr = &multiviewProperties; + } + + if (vrs_capabilities.attachment_vrs_supported) { + vrsProperties.sType = VK_STRUCTURE_TYPE_PHYSICAL_DEVICE_FRAGMENT_SHADING_RATE_PROPERTIES_KHR; + vrsProperties.pNext = nextptr; + + nextptr = &vrsProperties; } + physicalDeviceProperties.sType = VK_STRUCTURE_TYPE_PHYSICAL_DEVICE_PROPERTIES_2; + physicalDeviceProperties.pNext = nextptr; + 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 + // - 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 (vrs_capabilities.pipeline_vrs_supported || vrs_capabilities.primitive_vrs_supported || vrs_capabilities.attachment_vrs_supported) { + print_verbose("- Vulkan Variable Rate Shading supported:"); + if (vrs_capabilities.pipeline_vrs_supported) { + print_verbose(" Pipeline fragment shading rate"); + } + if (vrs_capabilities.primitive_vrs_supported) { + print_verbose(" Primitive fragment shading rate"); + } + if (vrs_capabilities.attachment_vrs_supported) { + // TODO expose these somehow to the end user. + vrs_capabilities.min_texel_size.x = vrsProperties.minFragmentShadingRateAttachmentTexelSize.width; + vrs_capabilities.min_texel_size.y = vrsProperties.minFragmentShadingRateAttachmentTexelSize.height; + vrs_capabilities.max_texel_size.x = vrsProperties.maxFragmentShadingRateAttachmentTexelSize.width; + vrs_capabilities.max_texel_size.y = vrsProperties.maxFragmentShadingRateAttachmentTexelSize.height; + + print_verbose(String(" Attachment fragment shading rate") + String(", min texel size: (") + itos(vrs_capabilities.min_texel_size.x) + String(", ") + itos(vrs_capabilities.min_texel_size.y) + String(")") + String(", max texel size: (") + itos(vrs_capabilities.max_texel_size.x) + String(", ") + itos(vrs_capabilities.max_texel_size.y) + String(")")); + } + + } else { + print_verbose("- Vulkan Variable Rate Shading not supported"); + } + if (multiview_capabilities.is_supported) { multiview_capabilities.max_view_count = multiviewProperties.maxMultiviewViewCount; multiview_capabilities.max_instance_count = multiviewProperties.maxMultiviewInstanceIndex; @@ -635,10 +693,10 @@ Error VulkanContext::_check_capabilities() { } Error VulkanContext::_create_instance() { - /* obtain version */ + // Obtain Vulkan version. _obtain_vulkan_version(); - /* initialise extensions */ + // Initialize extensions. { Error err = _initialize_extensions(); if (err != OK) { @@ -673,7 +731,7 @@ Error VulkanContext::_create_instance() { VkDebugUtilsMessengerCreateInfoEXT dbg_messenger_create_info; VkDebugReportCallbackCreateInfoEXT dbg_report_callback_create_info{}; if (enabled_debug_utils) { - // VK_EXT_debug_utils style + // VK_EXT_debug_utils style. dbg_messenger_create_info.sType = VK_STRUCTURE_TYPE_DEBUG_UTILS_MESSENGER_CREATE_INFO_EXT; dbg_messenger_create_info.pNext = nullptr; dbg_messenger_create_info.flags = 0; @@ -726,8 +784,7 @@ Error VulkanContext::_create_instance() { #endif if (enabled_debug_utils) { - // Setup VK_EXT_debug_utils function pointers always (we use them for - // debug labels and names). + // Setup VK_EXT_debug_utils function pointers always (we use them for debug labels and names). CreateDebugUtilsMessengerEXT = (PFN_vkCreateDebugUtilsMessengerEXT)vkGetInstanceProcAddr(inst, "vkCreateDebugUtilsMessengerEXT"); DestroyDebugUtilsMessengerEXT = @@ -800,7 +857,7 @@ Error VulkanContext::_create_instance() { } Error VulkanContext::_create_physical_device(VkSurfaceKHR p_surface) { - /* Make initial call to query gpu_count, then second call for gpu info*/ + // Make initial call to query gpu_count, then second call for gpu info. uint32_t gpu_count = 0; VkResult err = vkEnumeratePhysicalDevices(inst, &gpu_count, nullptr); ERR_FAIL_COND_V(err, ERR_CANT_CREATE); @@ -836,7 +893,7 @@ Error VulkanContext::_create_physical_device(VkSurfaceKHR p_surface) { return ERR_CANT_CREATE; } - // not really needed but nice to print the correct entry + // Not really needed but nice to print the correct entry. for (uint32_t i = 0; i < gpu_count; ++i) { if (physical_devices[i] == gpu) { device_index = i; @@ -845,8 +902,8 @@ Error VulkanContext::_create_physical_device(VkSurfaceKHR p_surface) { } } else { // 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. + // The device should really be a preference, but for now choosing a discrete GPU over the + // integrated one is better than the default. int type_selected = -1; print_verbose("Vulkan devices:"); @@ -948,13 +1005,13 @@ Error VulkanContext::_create_physical_device(VkSurfaceKHR p_surface) { free(physical_devices); - /* Look for device extensions */ + // Look for device extensions. uint32_t device_extension_count = 0; VkBool32 swapchainExtFound = 0; enabled_extension_count = 0; memset(extension_names, 0, sizeof(extension_names)); - /* Get identifier properties */ + // Get identifier properties. vkGetPhysicalDeviceProperties(gpu, &gpu_props); device_name = gpu_props.deviceName; @@ -996,9 +1053,16 @@ Error VulkanContext::_create_physical_device(VkSurfaceKHR p_surface) { 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 + // If multiview is supported, enable it. extension_names[enabled_extension_count++] = VK_KHR_MULTIVIEW_EXTENSION_NAME; } + if (!strcmp(VK_KHR_FRAGMENT_SHADING_RATE_EXTENSION_NAME, device_extensions[i].extensionName)) { + // if shading rate image is supported, enable it + extension_names[enabled_extension_count++] = VK_KHR_FRAGMENT_SHADING_RATE_EXTENSION_NAME; + } + if (!strcmp(VK_KHR_CREATE_RENDERPASS_2_EXTENSION_NAME, device_extensions[i].extensionName)) { + extension_names[enabled_extension_count++] = VK_KHR_CREATE_RENDERPASS_2_EXTENSION_NAME; + } if (enabled_extension_count >= MAX_EXTENSIONS) { free(device_extensions); ERR_FAIL_V_MSG(ERR_BUG, "Enabled extension count reaches MAX_EXTENSIONS, BUG"); @@ -1049,19 +1113,18 @@ Error VulkanContext::_create_physical_device(VkSurfaceKHR p_surface) { " extension.\n\nDo you have a compatible Vulkan installable client driver (ICD) installed?\n" "vkCreateInstance Failure"); - /* Call with nullptr 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); queue_props = (VkQueueFamilyProperties *)malloc(queue_family_count * sizeof(VkQueueFamilyProperties)); vkGetPhysicalDeviceQueueFamilyProperties(gpu, &queue_family_count, queue_props); - // Query fine-grained feature support for this device. // If app has specific feature requirements it should check supported // features based on this query vkGetPhysicalDeviceFeatures(gpu, &physical_device_features); - physical_device_features.robustBufferAccess = false; //turn off robust buffer access, which can hamper performance on some hardware + physical_device_features.robustBufferAccess = false; // Turn off robust buffer access, which can hamper performance on some hardware. #define GET_INSTANCE_PROC_ADDR(inst, entrypoint) \ { \ @@ -1076,7 +1139,7 @@ Error VulkanContext::_create_physical_device(VkSurfaceKHR p_surface) { GET_INSTANCE_PROC_ADDR(inst, GetPhysicalDeviceSurfacePresentModesKHR); GET_INSTANCE_PROC_ADDR(inst, GetSwapchainImagesKHR); - // get info about what our vulkan driver is capable off + // Gets capability info for current Vulkan driver. { Error res = _check_capabilities(); if (res != OK) { @@ -1110,11 +1173,23 @@ Error VulkanContext::_create_device() { }; nextptr = &shader_features; + VkPhysicalDeviceFragmentShadingRateFeaturesKHR vrs_features; + if (vrs_capabilities.pipeline_vrs_supported || vrs_capabilities.primitive_vrs_supported || vrs_capabilities.attachment_vrs_supported) { + // Insert into our chain to enable these features if they are available. + vrs_features.sType = VK_STRUCTURE_TYPE_PHYSICAL_DEVICE_FRAGMENT_SHADING_RATE_FEATURES_KHR; + vrs_features.pNext = nextptr; + vrs_features.pipelineFragmentShadingRate = vrs_capabilities.pipeline_vrs_supported; + vrs_features.primitiveFragmentShadingRate = vrs_capabilities.primitive_vrs_supported; + vrs_features.attachmentFragmentShadingRate = vrs_capabilities.attachment_vrs_supported; + + nextptr = &vrs_features; + } + VkPhysicalDeviceVulkan11Features vulkan11features; VkPhysicalDevice16BitStorageFeaturesKHR storage_feature; VkPhysicalDeviceMultiviewFeatures multiview_features; if (vulkan_major > 1 || vulkan_minor >= 2) { - // In Vulkan 1.2 and newer we use a newer struct to enable various features + // In Vulkan 1.2 and newer we use a newer struct to enable various features. vulkan11features.sType = VK_STRUCTURE_TYPE_PHYSICAL_DEVICE_VULKAN_1_1_FEATURES; vulkan11features.pNext = nextptr; @@ -1132,7 +1207,7 @@ Error VulkanContext::_create_device() { vulkan11features.shaderDrawParameters = 0; nextptr = &vulkan11features; } else { - // On Vulkan 1.0 and 1.1 we use our older structs to initialise these features + // On Vulkan 1.0 and 1.1 we use our older structs to initialise these features. storage_feature.sType = VK_STRUCTURE_TYPE_PHYSICAL_DEVICE_16BIT_STORAGE_FEATURES_KHR; storage_feature.pNext = nextptr; storage_feature.storageBuffer16BitAccess = storage_buffer_capabilities.storage_buffer_16_bit_access_is_supported; @@ -1161,7 +1236,7 @@ Error VulkanContext::_create_device() { /*ppEnabledLayerNames*/ nullptr, /*enabledExtensionCount*/ enabled_extension_count, /*ppEnabledExtensionNames*/ (const char *const *)extension_names, - /*pEnabledFeatures*/ &physical_device_features, // If specific features are required, pass them in here + /*pEnabledFeatures*/ &physical_device_features, // If specific features are required, pass them in here. }; if (separate_present_queue) { queues[1].sType = VK_STRUCTURE_TYPE_DEVICE_QUEUE_CREATE_INFO; @@ -1193,7 +1268,7 @@ Error VulkanContext::_initialize_queues(VkSurfaceKHR p_surface) { } // Search for a graphics and a present queue in the array of queue - // families, try to find one that supports both + // families, try to find one that supports both. uint32_t graphicsQueueFamilyIndex = UINT32_MAX; uint32_t presentQueueFamilyIndex = UINT32_MAX; for (uint32_t i = 0; i < queue_family_count; i++) { @@ -1223,7 +1298,7 @@ Error VulkanContext::_initialize_queues(VkSurfaceKHR p_surface) { free(supportsPresent); - // Generate error if could not find both a graphics and a present queue + // Generate error if could not find both a graphics and a present queue. ERR_FAIL_COND_V_MSG(graphicsQueueFamilyIndex == UINT32_MAX || presentQueueFamilyIndex == UINT32_MAX, ERR_CANT_CREATE, "Could not find both graphics and present queues\n"); @@ -1279,7 +1354,7 @@ Error VulkanContext::_initialize_queues(VkSurfaceKHR p_surface) { 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 + // we want a 32bit RGBA unsigned normalised buffer or similar. const VkFormat allowed_formats[] = { VK_FORMAT_B8G8R8A8_UNORM, VK_FORMAT_R8G8B8A8_UNORM @@ -1291,7 +1366,7 @@ Error VulkanContext::_initialize_queues(VkSurfaceKHR p_surface) { ERR_FAIL_V_MSG(ERR_CANT_CREATE, "formatCount less than 1"); } - // Find the first format that we support + // 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++) { @@ -1323,7 +1398,7 @@ Error VulkanContext::_create_semaphores() { VkResult err; // Create semaphores to synchronize acquiring presentable buffers before - // rendering and waiting for drawing to be complete before presenting + // rendering and waiting for drawing to be complete before presenting. VkSemaphoreCreateInfo semaphoreCreateInfo = { /*sType*/ VK_STRUCTURE_TYPE_SEMAPHORE_CREATE_INFO, /*pNext*/ nullptr, @@ -1331,7 +1406,7 @@ Error VulkanContext::_create_semaphores() { }; // Create fences that we can use to throttle if we get too far - // ahead of the image presents + // ahead of the image presents. VkFenceCreateInfo fence_ci = { /*sType*/ VK_STRUCTURE_TYPE_FENCE_CREATE_INFO, /*pNext*/ nullptr, @@ -1351,7 +1426,7 @@ Error VulkanContext::_create_semaphores() { } frame_index = 0; - // Get Memory information and properties + // Get Memory information and properties. vkGetPhysicalDeviceMemoryProperties(gpu, &memory_properties); return OK; @@ -1426,7 +1501,7 @@ bool VulkanContext::window_is_valid_swapchain(DisplayServer::WindowID p_window) VkRenderPass VulkanContext::window_get_render_pass(DisplayServer::WindowID p_window) { ERR_FAIL_COND_V(!windows.has(p_window), VK_NULL_HANDLE); Window *w = &windows[p_window]; - //vulkan use of currentbuffer + // Vulkan use of currentbuffer. return w->render_pass; } @@ -1434,7 +1509,7 @@ VkFramebuffer VulkanContext::window_get_framebuffer(DisplayServer::WindowID p_wi ERR_FAIL_COND_V(!windows.has(p_window), VK_NULL_HANDLE); ERR_FAIL_COND_V(!buffers_prepared, VK_NULL_HANDLE); Window *w = &windows[p_window]; - //vulkan use of currentbuffer + // Vulkan use of currentbuffer. if (w->swapchain_image_resources != VK_NULL_HANDLE) { return w->swapchain_image_resources[w->current_buffer].framebuffer; } else { @@ -1459,7 +1534,7 @@ Error VulkanContext::_clean_up_swap_chain(Window *window) { } vkDeviceWaitIdle(device); - //this destroys images associated it seems + // This destroys images associated it seems. fpDestroySwapchainKHR(device, window->swapchain, nullptr); window->swapchain = VK_NULL_HANDLE; vkDestroyRenderPass(device, window->render_pass, nullptr); @@ -1485,7 +1560,7 @@ Error VulkanContext::_update_swap_chain(Window *window) { _clean_up_swap_chain(window); } - // Check the surface capabilities and formats + // Check the surface capabilities and formats. VkSurfaceCapabilitiesKHR surfCapabilities; err = fpGetPhysicalDeviceSurfaceCapabilitiesKHR(gpu, window->surface, &surfCapabilities); ERR_FAIL_COND_V(err, ERR_CANT_CREATE); @@ -1502,7 +1577,7 @@ Error VulkanContext::_update_swap_chain(Window *window) { } VkExtent2D swapchainExtent; - // width and height are either both 0xFFFFFFFF, or both not 0xFFFFFFFF. + // Width and height are either both 0xFFFFFFFF, or both not 0xFFFFFFFF. if (surfCapabilities.currentExtent.width == 0xFFFFFFFF) { // If the surface size is undefined, the size is set to the size // of the images requested, which must fit within the minimum and @@ -1522,7 +1597,7 @@ Error VulkanContext::_update_swap_chain(Window *window) { swapchainExtent.height = surfCapabilities.maxImageExtent.height; } } else { - // If the surface size is defined, the swap chain size must match + // If the surface size is defined, the swap chain size must match. swapchainExtent = surfCapabilities.currentExtent; window->width = surfCapabilities.currentExtent.width; window->height = surfCapabilities.currentExtent.height; @@ -1530,23 +1605,23 @@ Error VulkanContext::_update_swap_chain(Window *window) { if (window->width == 0 || window->height == 0) { free(presentModes); - //likely window minimized, no swapchain created + // Likely window minimized, no swapchain created. return OK; } // 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. - // 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 - // reasons you may wish to use them. + // 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 + // reasons you may wish to use them. // // It should be noted that Vulkan 1.0 doesn't provide a method for - // synchronizing rendering with the presentation engine's display. There + // synchronizing rendering with the presentation engine's display. There // is a method provided for throttling rendering with the display, but // there are some presentation engines for which this method will not work. // If an application doesn't throttle its rendering, and if it renders much // faster than the refresh rate of the display, this can waste power on - // mobile devices. That is because power is being spent rendering images + // mobile devices. That is because power is being spent rendering images // that may never be seen. // VK_PRESENT_MODE_IMMEDIATE_KHR is for applications that don't care about @@ -1591,8 +1666,23 @@ Error VulkanContext::_update_swap_chain(Window *window) { 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 + String present_mode_string; + switch (window->vsync_mode) { + case DisplayServer::VSYNC_MAILBOX: + present_mode_string = "Mailbox"; + break; + case DisplayServer::VSYNC_ADAPTIVE: + present_mode_string = "Adaptive"; + break; + case DisplayServer::VSYNC_ENABLED: + present_mode_string = "Enabled"; + break; + case DisplayServer::VSYNC_DISABLED: + present_mode_string = "Disabled"; + break; + } + WARN_PRINT(vformat("The requested V-Sync mode %s is not available. Falling back to V-Sync mode Enabled.", present_mode_string)); + window->vsync_mode = DisplayServer::VSYNC_ENABLED; // Set to default. } print_verbose("Using present mode: " + String(string_VkPresentModeKHR(window->presentMode))); @@ -1601,15 +1691,15 @@ Error VulkanContext::_update_swap_chain(Window *window) { // Determine the number of VkImages to use in the swap chain. // Application desires to acquire 3 images at a time for triple - // buffering + // buffering. uint32_t desiredNumOfSwapchainImages = 3; if (desiredNumOfSwapchainImages < surfCapabilities.minImageCount) { desiredNumOfSwapchainImages = surfCapabilities.minImageCount; } // If maxImageCount is 0, we can ask for as many images as we want; - // otherwise we're limited to maxImageCount + // otherwise we're limited to maxImageCount. if ((surfCapabilities.maxImageCount > 0) && (desiredNumOfSwapchainImages > surfCapabilities.maxImageCount)) { - // Application must settle for fewer images than desired: + // Application must settle for fewer images than desired. desiredNumOfSwapchainImages = surfCapabilities.maxImageCount; } @@ -1620,7 +1710,7 @@ Error VulkanContext::_update_swap_chain(Window *window) { preTransform = surfCapabilities.currentTransform; } - // Find a supported composite alpha mode - one of these is guaranteed to be set + // Find a supported composite alpha mode - one of these is guaranteed to be set. VkCompositeAlphaFlagBitsKHR compositeAlpha = VK_COMPOSITE_ALPHA_OPAQUE_BIT_KHR; VkCompositeAlphaFlagBitsKHR compositeAlphaFlags[4] = { VK_COMPOSITE_ALPHA_OPAQUE_BIT_KHR, @@ -1667,7 +1757,7 @@ Error VulkanContext::_update_swap_chain(Window *window) { ERR_FAIL_COND_V(err, ERR_CANT_CREATE); if (swapchainImageCount == 0) { - //assign here for the first time. + // Assign here for the first time. swapchainImageCount = sp_image_count; } else { ERR_FAIL_COND_V(swapchainImageCount != sp_image_count, ERR_BUG); @@ -1725,7 +1815,9 @@ Error VulkanContext::_update_swap_chain(Window *window) { /******** FRAMEBUFFER ************/ { - const VkAttachmentDescription attachment = { + const VkAttachmentDescription2KHR attachment = { + /*sType*/ VK_STRUCTURE_TYPE_ATTACHMENT_DESCRIPTION_2_KHR, + /*pNext*/ nullptr, /*flags*/ 0, /*format*/ format, /*samples*/ VK_SAMPLE_COUNT_1_BIT, @@ -1737,14 +1829,20 @@ Error VulkanContext::_update_swap_chain(Window *window) { /*finalLayout*/ VK_IMAGE_LAYOUT_PRESENT_SRC_KHR, }; - const VkAttachmentReference color_reference = { + const VkAttachmentReference2KHR color_reference = { + /*sType*/ VK_STRUCTURE_TYPE_ATTACHMENT_REFERENCE_2_KHR, + /*pNext*/ nullptr, /*attachment*/ 0, /*layout*/ VK_IMAGE_LAYOUT_COLOR_ATTACHMENT_OPTIMAL, + /*aspectMask*/ 0, }; - const VkSubpassDescription subpass = { + const VkSubpassDescription2KHR subpass = { + /*sType*/ VK_STRUCTURE_TYPE_SUBPASS_DESCRIPTION_2_KHR, + /*pNext*/ nullptr, /*flags*/ 0, /*pipelineBindPoint*/ VK_PIPELINE_BIND_POINT_GRAPHICS, + /*viewMask*/ 0, /*inputAttachmentCount*/ 0, /*pInputAttachments*/ nullptr, /*colorAttachmentCount*/ 1, @@ -1754,8 +1852,9 @@ Error VulkanContext::_update_swap_chain(Window *window) { /*preserveAttachmentCount*/ 0, /*pPreserveAttachments*/ nullptr, }; - const VkRenderPassCreateInfo rp_info = { - /*sTyp*/ VK_STRUCTURE_TYPE_RENDER_PASS_CREATE_INFO, + + const VkRenderPassCreateInfo2KHR rp_info = { + /*sType*/ VK_STRUCTURE_TYPE_RENDER_PASS_CREATE_INFO_2_KHR, /*pNext*/ nullptr, /*flags*/ 0, /*attachmentCount*/ 1, @@ -1764,9 +1863,11 @@ Error VulkanContext::_update_swap_chain(Window *window) { /*pSubpasses*/ &subpass, /*dependencyCount*/ 0, /*pDependencies*/ nullptr, + /*correlatedViewMaskCount*/ 0, + /*pCorrelatedViewMasks*/ nullptr, }; - err = vkCreateRenderPass(device, &rp_info, nullptr, &window->render_pass); + err = vkCreateRenderPass2KHR(device, &rp_info, nullptr, &window->render_pass); ERR_FAIL_COND_V(err, ERR_CANT_CREATE); for (uint32_t i = 0; i < swapchainImageCount; i++) { @@ -1839,7 +1940,7 @@ Error VulkanContext::_update_swap_chain(Window *window) { } } - //reset current buffer + // Reset current buffer. window->current_buffer = 0; return OK; @@ -1860,27 +1961,30 @@ Error VulkanContext::initialize() { return OK; } -void VulkanContext::set_setup_buffer(const VkCommandBuffer &pCommandBuffer) { - command_buffer_queue.write[0] = pCommandBuffer; +void VulkanContext::set_setup_buffer(VkCommandBuffer p_command_buffer) { + command_buffer_queue.write[0] = p_command_buffer; } -void VulkanContext::append_command_buffer(const VkCommandBuffer &pCommandBuffer) { +void VulkanContext::append_command_buffer(VkCommandBuffer p_command_buffer) { if (command_buffer_queue.size() <= command_buffer_count) { command_buffer_queue.resize(command_buffer_count + 1); } - command_buffer_queue.write[command_buffer_count] = pCommandBuffer; + command_buffer_queue.write[command_buffer_count] = p_command_buffer; command_buffer_count++; } void VulkanContext::flush(bool p_flush_setup, bool p_flush_pending) { - // ensure everything else pending is executed + // Ensure everything else pending is executed. vkDeviceWaitIdle(device); - //flush the pending setup buffer + // Flush the pending setup buffer. - if (p_flush_setup && command_buffer_queue[0]) { - //use a fence to wait for everything done + bool setup_flushable = p_flush_setup && command_buffer_queue[0]; + bool pending_flushable = p_flush_pending && command_buffer_count > 1; + + if (setup_flushable) { + // Use a fence to wait for everything done. VkSubmitInfo submit_info; submit_info.sType = VK_STRUCTURE_TYPE_SUBMIT_INFO; submit_info.pNext = nullptr; @@ -1889,33 +1993,33 @@ void VulkanContext::flush(bool p_flush_setup, bool p_flush_pending) { submit_info.pWaitSemaphores = nullptr; submit_info.commandBufferCount = 1; submit_info.pCommandBuffers = command_buffer_queue.ptr(); - submit_info.signalSemaphoreCount = 0; - submit_info.pSignalSemaphores = nullptr; + submit_info.signalSemaphoreCount = pending_flushable ? 1 : 0; + submit_info.pSignalSemaphores = pending_flushable ? &draw_complete_semaphores[frame_index] : nullptr; VkResult err = vkQueueSubmit(graphics_queue, 1, &submit_info, VK_NULL_HANDLE); command_buffer_queue.write[0] = nullptr; ERR_FAIL_COND(err); - vkDeviceWaitIdle(device); } - if (p_flush_pending && command_buffer_count > 1) { - //use a fence to wait for everything done + if (pending_flushable) { + // Use a fence to wait for everything to finish. VkSubmitInfo submit_info; submit_info.sType = VK_STRUCTURE_TYPE_SUBMIT_INFO; submit_info.pNext = nullptr; - submit_info.pWaitDstStageMask = nullptr; - submit_info.waitSemaphoreCount = 0; - submit_info.pWaitSemaphores = nullptr; + VkPipelineStageFlags wait_stage_mask = VK_PIPELINE_STAGE_TOP_OF_PIPE_BIT; + submit_info.pWaitDstStageMask = setup_flushable ? &wait_stage_mask : nullptr; + submit_info.waitSemaphoreCount = setup_flushable ? 1 : 0; + submit_info.pWaitSemaphores = setup_flushable ? &draw_complete_semaphores[frame_index] : nullptr; submit_info.commandBufferCount = command_buffer_count - 1; submit_info.pCommandBuffers = command_buffer_queue.ptr() + 1; submit_info.signalSemaphoreCount = 0; submit_info.pSignalSemaphores = nullptr; VkResult err = vkQueueSubmit(graphics_queue, 1, &submit_info, VK_NULL_HANDLE); - ERR_FAIL_COND(err); - vkDeviceWaitIdle(device); - command_buffer_count = 1; + ERR_FAIL_COND(err); } + + vkDeviceWaitIdle(device); } Error VulkanContext::prepare_buffers() { @@ -1925,7 +2029,7 @@ Error VulkanContext::prepare_buffers() { VkResult err; - // Ensure no more than FRAME_LAG renderings are outstanding + // Ensure no more than FRAME_LAG renderings are outstanding. vkWaitForFences(device, 1, &fences[frame_index], VK_TRUE, UINT64_MAX); vkResetFences(device, 1, &fences[frame_index]); @@ -1939,19 +2043,19 @@ Error VulkanContext::prepare_buffers() { } do { - // Get the index of the next available swapchain image: + // Get the index of the next available swapchain image. err = fpAcquireNextImageKHR(device, w->swapchain, UINT64_MAX, 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: + // Swapchain is out of date (e.g. the window was resized) and + // must be recreated. print_verbose("Vulkan: Early out of date swapchain, recreating."); - //resize_notify(); + // resize_notify(); _update_swap_chain(w); } else if (err == VK_SUBOPTIMAL_KHR) { - // swapchain is not as optimal as it could be, but the platform's + // 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; @@ -1979,7 +2083,7 @@ Error VulkanContext::swap_buffers() { #if 0 if (VK_GOOGLE_display_timing_enabled) { // Look at what happened to previous presents, and make appropriate - // adjustments in timing: + // adjustments in timing. DemoUpdateTargetIPD(demo); // Note: a real application would position its geometry to that it's in @@ -1998,7 +2102,7 @@ Error VulkanContext::swap_buffers() { uint32_t commands_to_submit = 0; if (command_buffer_queue[0] == nullptr) { - //no setup command, but commands to submit, submit from the first and skip command + // No setup command, but commands to submit, submit from the first and skip command. if (command_buffer_count > 1) { commands_ptr = command_buffer_queue.ptr() + 1; commands_to_submit = command_buffer_count - 1; @@ -2041,7 +2145,7 @@ Error VulkanContext::swap_buffers() { if (separate_present_queue) { // If we are using separate queues, change image ownership to the // present queue before presenting, waiting for the draw complete - // semaphore and signalling the ownership released semaphore when finished + // semaphore and signalling the ownership released semaphore when finished. VkFence nullFence = VK_NULL_HANDLE; pipe_stage_flags[0] = VK_PIPELINE_STAGE_COLOR_ATTACHMENT_OUTPUT_BIT; submit_info.waitSemaphoreCount = 1; @@ -2068,7 +2172,7 @@ Error VulkanContext::swap_buffers() { } // If we are using separate queues, we have to wait for image ownership, - // otherwise wait for draw complete + // otherwise wait for draw complete. VkPresentInfoKHR present = { /*sType*/ VK_STRUCTURE_TYPE_PRESENT_INFO_KHR, /*pNext*/ nullptr, @@ -2142,7 +2246,7 @@ Error VulkanContext::swap_buffers() { uint64_t curtime = getTimeInNanoseconds(); if (curtime == 0) { // Since we didn't find out the current time, don't give a - // desiredPresentTime: + // desiredPresentTime. ptime.desiredPresentTime = 0; } else { ptime.desiredPresentTime = curtime + (target_IPD >> 1); @@ -2173,12 +2277,12 @@ Error VulkanContext::swap_buffers() { frame_index %= FRAME_LAG; if (err == VK_ERROR_OUT_OF_DATE_KHR) { - // swapchain is out of date (e.g. the window was resized) and - // must be recreated: + // Swapchain is out of date (e.g. the window was resized) and + // must be recreated. 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 + // Swapchain is not as optimal as it could be, but the platform's // presentation engine will still present the image correctly. print_verbose("Vulkan: Swapchain is suboptimal."); } else { @@ -2223,7 +2327,7 @@ VkPhysicalDeviceLimits VulkanContext::get_device_limits() const { RID VulkanContext::local_device_create() { LocalDevice ld; - { //create device + { // Create device. VkResult err; float queue_priorities[1] = { 0.0 }; VkDeviceQueueCreateInfo queues[2]; @@ -2244,13 +2348,13 @@ RID VulkanContext::local_device_create() { /*ppEnabledLayerNames */ nullptr, /*enabledExtensionCount */ enabled_extension_count, /*ppEnabledExtensionNames */ (const char *const *)extension_names, - /*pEnabledFeatures */ &physical_device_features, // If specific features are required, pass them in here + /*pEnabledFeatures */ &physical_device_features, // If specific features are required, pass them in here. }; err = vkCreateDevice(gpu, &sdevice, nullptr, &ld.device); ERR_FAIL_COND_V(err, RID()); } - { //create graphics queue + { // Create graphics queue. vkGetDeviceQueue(ld.device, graphics_queue_family_index, 0, &ld.queue); } @@ -2312,7 +2416,7 @@ void VulkanContext::command_begin_label(VkCommandBuffer p_command_buffer, String return; } - CharString cs = p_label_name.utf8().get_data(); + CharString cs = p_label_name.utf8(); VkDebugUtilsLabelEXT label; label.sType = VK_STRUCTURE_TYPE_DEBUG_UTILS_LABEL_EXT; label.pNext = nullptr; @@ -2328,7 +2432,7 @@ void VulkanContext::command_insert_label(VkCommandBuffer p_command_buffer, Strin if (!enabled_debug_utils) { return; } - CharString cs = p_label_name.utf8().get_data(); + CharString cs = p_label_name.utf8(); VkDebugUtilsLabelEXT label; label.sType = VK_STRUCTURE_TYPE_DEBUG_UTILS_LABEL_EXT; label.pNext = nullptr; @@ -2364,6 +2468,7 @@ void VulkanContext::set_object_name(VkObjectType p_object_type, uint64_t p_objec String VulkanContext::get_device_vendor_name() const { return device_vendor; } + String VulkanContext::get_device_name() const { return device_name; } @@ -2372,17 +2477,21 @@ RenderingDevice::DeviceType VulkanContext::get_device_type() const { return RenderingDevice::DeviceType(device_type); } +String VulkanContext::get_device_api_version() const { + return vformat("%d.%d.%d", vulkan_major, vulkan_minor, vulkan_patch); +} + String VulkanContext::get_device_pipeline_cache_uuid() const { return pipeline_cache_id; } 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."); + ERR_FAIL_COND_V_MSG(!windows.has(p_window), DisplayServer::VSYNC_ENABLED, "Could not get V-Sync 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."); + ERR_FAIL_COND_MSG(!windows.has(p_window), "Could not set V-Sync 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]); } diff --git a/drivers/vulkan/vulkan_context.h b/drivers/vulkan/vulkan_context.h index 8c0111714c..5cc3b515d9 100644 --- a/drivers/vulkan/vulkan_context.h +++ b/drivers/vulkan/vulkan_context.h @@ -34,7 +34,7 @@ #include "core/error/error_list.h" #include "core/os/mutex.h" #include "core/string/ustring.h" -#include "core/templates/map.h" +#include "core/templates/rb_map.h" #include "core/templates/rid_owner.h" #include "servers/display_server.h" #include "servers/rendering/rendering_device.h" @@ -69,6 +69,15 @@ public: uint32_t max_instance_count; }; + struct VRSCapabilities { + bool pipeline_vrs_supported; // We can specify our fragment rate on a pipeline level. + bool primitive_vrs_supported; // We can specify our fragment rate on each drawcall. + bool attachment_vrs_supported; // We can provide a density map attachment on our framebuffer. + + Size2i min_texel_size; + Size2i max_texel_size; + }; + struct ShaderCapabilities { bool shader_float16_is_supported; bool shader_int8_is_supported; @@ -98,12 +107,13 @@ private: 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 + // 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; uint32_t vulkan_patch = 0; SubgroupCapabilities subgroup_capabilities; MultiviewCapabilities multiview_capabilities; + VRSCapabilities vrs_capabilities; ShaderCapabilities shader_capabilities; StorageBufferCapabilities storage_buffer_capabilities; @@ -117,8 +127,8 @@ private: // Present queue. bool queues_initialized = false; - uint32_t graphics_queue_family_index = 0; - uint32_t present_queue_family_index = 0; + uint32_t graphics_queue_family_index = UINT32_MAX; + uint32_t present_queue_family_index = UINT32_MAX; bool separate_present_queue = false; VkQueue graphics_queue = VK_NULL_HANDLE; VkQueue present_queue = VK_NULL_HANDLE; @@ -161,7 +171,7 @@ private: RID_Owner<LocalDevice, true> local_device_owner; - Map<DisplayServer::WindowID, Window> windows; + HashMap<DisplayServer::WindowID, Window> windows; uint32_t swapchainImageCount = 0; // Commands. @@ -185,27 +195,28 @@ private: */ bool enabled_debug_report = false; - PFN_vkCreateDebugUtilsMessengerEXT CreateDebugUtilsMessengerEXT; - PFN_vkDestroyDebugUtilsMessengerEXT DestroyDebugUtilsMessengerEXT; - PFN_vkSubmitDebugUtilsMessageEXT SubmitDebugUtilsMessageEXT; - PFN_vkCmdBeginDebugUtilsLabelEXT CmdBeginDebugUtilsLabelEXT; - 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; - PFN_vkGetPhysicalDeviceSurfacePresentModesKHR fpGetPhysicalDeviceSurfacePresentModesKHR; - PFN_vkCreateSwapchainKHR fpCreateSwapchainKHR; - PFN_vkDestroySwapchainKHR fpDestroySwapchainKHR; - PFN_vkGetSwapchainImagesKHR fpGetSwapchainImagesKHR; - PFN_vkAcquireNextImageKHR fpAcquireNextImageKHR; - PFN_vkQueuePresentKHR fpQueuePresentKHR; - PFN_vkGetRefreshCycleDurationGOOGLE fpGetRefreshCycleDurationGOOGLE; - PFN_vkGetPastPresentationTimingGOOGLE fpGetPastPresentationTimingGOOGLE; + PFN_vkCreateDebugUtilsMessengerEXT CreateDebugUtilsMessengerEXT = nullptr; + PFN_vkDestroyDebugUtilsMessengerEXT DestroyDebugUtilsMessengerEXT = nullptr; + PFN_vkSubmitDebugUtilsMessageEXT SubmitDebugUtilsMessageEXT = nullptr; + PFN_vkCmdBeginDebugUtilsLabelEXT CmdBeginDebugUtilsLabelEXT = nullptr; + PFN_vkCmdEndDebugUtilsLabelEXT CmdEndDebugUtilsLabelEXT = nullptr; + PFN_vkCmdInsertDebugUtilsLabelEXT CmdInsertDebugUtilsLabelEXT = nullptr; + PFN_vkSetDebugUtilsObjectNameEXT SetDebugUtilsObjectNameEXT = nullptr; + PFN_vkCreateDebugReportCallbackEXT CreateDebugReportCallbackEXT = nullptr; + PFN_vkDebugReportMessageEXT DebugReportMessageEXT = nullptr; + PFN_vkDestroyDebugReportCallbackEXT DestroyDebugReportCallbackEXT = nullptr; + PFN_vkGetPhysicalDeviceSurfaceSupportKHR fpGetPhysicalDeviceSurfaceSupportKHR = nullptr; + PFN_vkGetPhysicalDeviceSurfaceCapabilitiesKHR fpGetPhysicalDeviceSurfaceCapabilitiesKHR = nullptr; + PFN_vkGetPhysicalDeviceSurfaceFormatsKHR fpGetPhysicalDeviceSurfaceFormatsKHR = nullptr; + PFN_vkGetPhysicalDeviceSurfacePresentModesKHR fpGetPhysicalDeviceSurfacePresentModesKHR = nullptr; + PFN_vkCreateSwapchainKHR fpCreateSwapchainKHR = nullptr; + PFN_vkDestroySwapchainKHR fpDestroySwapchainKHR = nullptr; + PFN_vkGetSwapchainImagesKHR fpGetSwapchainImagesKHR = nullptr; + PFN_vkAcquireNextImageKHR fpAcquireNextImageKHR = nullptr; + PFN_vkQueuePresentKHR fpQueuePresentKHR = nullptr; + PFN_vkGetRefreshCycleDurationGOOGLE fpGetRefreshCycleDurationGOOGLE = nullptr; + PFN_vkGetPastPresentationTimingGOOGLE fpGetPastPresentationTimingGOOGLE = nullptr; + PFN_vkCreateRenderPass2KHR fpCreateRenderPass2KHR = nullptr; VkDebugUtilsMessengerEXT dbg_messenger = VK_NULL_HANDLE; VkDebugReportCallbackEXT dbg_debug_report = VK_NULL_HANDLE; @@ -256,12 +267,16 @@ protected: Error _get_preferred_validation_layers(uint32_t *count, const char *const **names); public: + // Extension calls. + VkResult vkCreateRenderPass2KHR(VkDevice device, const VkRenderPassCreateInfo2 *pCreateInfo, const VkAllocationCallbacks *pAllocator, VkRenderPass *pRenderPass); + 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; }; - ShaderCapabilities get_shader_capabilities() const { return shader_capabilities; }; - StorageBufferCapabilities get_storage_buffer_capabilities() const { return storage_buffer_capabilities; }; + const SubgroupCapabilities &get_subgroup_capabilities() const { return subgroup_capabilities; }; + const MultiviewCapabilities &get_multiview_capabilities() const { return multiview_capabilities; }; + const VRSCapabilities &get_vrs_capabilities() const { return vrs_capabilities; }; + const ShaderCapabilities &get_shader_capabilities() const { return shader_capabilities; }; + const StorageBufferCapabilities &get_storage_buffer_capabilities() const { return storage_buffer_capabilities; }; VkDevice get_device(); VkPhysicalDevice get_physical_device(); @@ -289,8 +304,8 @@ public: VkFormat get_screen_format() const; VkPhysicalDeviceLimits get_device_limits() const; - void set_setup_buffer(const VkCommandBuffer &pCommandBuffer); - void append_command_buffer(const VkCommandBuffer &pCommandBuffer); + void set_setup_buffer(VkCommandBuffer p_command_buffer); + void append_command_buffer(VkCommandBuffer p_command_buffer); void resize_notify(); void flush(bool p_flush_setup = false, bool p_flush_pending = false); Error prepare_buffers(); @@ -305,6 +320,7 @@ public: String get_device_vendor_name() const; String get_device_name() const; RenderingDevice::DeviceType get_device_type() const; + String get_device_api_version() const; String get_device_pipeline_cache_uuid() const; void set_vsync_mode(DisplayServer::WindowID p_window, DisplayServer::VSyncMode p_mode); @@ -314,4 +330,4 @@ public: virtual ~VulkanContext(); }; -#endif // VULKAN_DEVICE_H +#endif // VULKAN_CONTEXT_H diff --git a/drivers/vulkan/vulkan_hooks.h b/drivers/vulkan/vulkan_hooks.h index 3f244b4d45..dec2042d0d 100644 --- a/drivers/vulkan/vulkan_hooks.h +++ b/drivers/vulkan/vulkan_hooks.h @@ -45,4 +45,4 @@ public: virtual ~VulkanHooks(){}; }; -#endif +#endif // VULKAN_HOOKS_H |