diff options
Diffstat (limited to 'drivers/vulkan/rendering_device_vulkan.cpp')
-rw-r--r-- | drivers/vulkan/rendering_device_vulkan.cpp | 7071 |
1 files changed, 7071 insertions, 0 deletions
diff --git a/drivers/vulkan/rendering_device_vulkan.cpp b/drivers/vulkan/rendering_device_vulkan.cpp new file mode 100644 index 0000000000..4582f1c2c1 --- /dev/null +++ b/drivers/vulkan/rendering_device_vulkan.cpp @@ -0,0 +1,7071 @@ +/*************************************************************************/ +/* rendering_device_vulkan.cpp */ +/*************************************************************************/ +/* This file is part of: */ +/* GODOT ENGINE */ +/* https://godotengine.org */ +/*************************************************************************/ +/* Copyright (c) 2007-2020 Juan Linietsky, Ariel Manzur. */ +/* Copyright (c) 2014-2020 Godot Engine contributors (cf. AUTHORS.md). */ +/* */ +/* Permission is hereby granted, free of charge, to any person obtaining */ +/* a copy of this software and associated documentation files (the */ +/* "Software"), to deal in the Software without restriction, including */ +/* without limitation the rights to use, copy, modify, merge, publish, */ +/* distribute, sublicense, and/or sell copies of the Software, and to */ +/* permit persons to whom the Software is furnished to do so, subject to */ +/* the following conditions: */ +/* */ +/* The above copyright notice and this permission notice shall be */ +/* included in all copies or substantial portions of the Software. */ +/* */ +/* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, */ +/* EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF */ +/* MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.*/ +/* IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY */ +/* CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, */ +/* TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE */ +/* SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. */ +/*************************************************************************/ + +#include "rendering_device_vulkan.h" +#include "core/hashfuncs.h" +#include "core/os/file_access.h" +#include "core/os/os.h" +#include "core/project_settings.h" +#include "drivers/vulkan/vulkan_context.h" +#include "thirdparty/spirv-reflect/spirv_reflect.h" + +//#define FORCE_FULL_BARRIER + +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].insert(p_id); + + if (!reverse_dependency_map.has(p_id)) { + reverse_dependency_map[p_id] = Set<RID>(); + } + + reverse_dependency_map[p_id].insert(p_depends_on); +} + +void RenderingDeviceVulkan::_free_dependencies(RID p_id) { + + //direct dependencies must be freed + + Map<RID, Set<RID> >::Element *E = dependency_map.find(p_id); + if (E) { + + while (E->get().size()) { + free(E->get().front()->get()); + } + dependency_map.erase(E); + } + + //reverse depenencies 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()); + ERR_CONTINUE(!G); + ERR_CONTINUE(!G->get().has(p_id)); + G->get().erase(p_id); + } + + reverse_dependency_map.erase(E); + } +} + +const VkFormat RenderingDeviceVulkan::vulkan_formats[RenderingDevice::DATA_FORMAT_MAX] = { + VK_FORMAT_R4G4_UNORM_PACK8, + VK_FORMAT_R4G4B4A4_UNORM_PACK16, + VK_FORMAT_B4G4R4A4_UNORM_PACK16, + VK_FORMAT_R5G6B5_UNORM_PACK16, + VK_FORMAT_B5G6R5_UNORM_PACK16, + VK_FORMAT_R5G5B5A1_UNORM_PACK16, + VK_FORMAT_B5G5R5A1_UNORM_PACK16, + VK_FORMAT_A1R5G5B5_UNORM_PACK16, + VK_FORMAT_R8_UNORM, + VK_FORMAT_R8_SNORM, + VK_FORMAT_R8_USCALED, + VK_FORMAT_R8_SSCALED, + VK_FORMAT_R8_UINT, + VK_FORMAT_R8_SINT, + VK_FORMAT_R8_SRGB, + VK_FORMAT_R8G8_UNORM, + VK_FORMAT_R8G8_SNORM, + VK_FORMAT_R8G8_USCALED, + VK_FORMAT_R8G8_SSCALED, + VK_FORMAT_R8G8_UINT, + VK_FORMAT_R8G8_SINT, + VK_FORMAT_R8G8_SRGB, + VK_FORMAT_R8G8B8_UNORM, + VK_FORMAT_R8G8B8_SNORM, + VK_FORMAT_R8G8B8_USCALED, + VK_FORMAT_R8G8B8_SSCALED, + VK_FORMAT_R8G8B8_UINT, + VK_FORMAT_R8G8B8_SINT, + VK_FORMAT_R8G8B8_SRGB, + VK_FORMAT_B8G8R8_UNORM, + VK_FORMAT_B8G8R8_SNORM, + VK_FORMAT_B8G8R8_USCALED, + VK_FORMAT_B8G8R8_SSCALED, + VK_FORMAT_B8G8R8_UINT, + VK_FORMAT_B8G8R8_SINT, + VK_FORMAT_B8G8R8_SRGB, + VK_FORMAT_R8G8B8A8_UNORM, + VK_FORMAT_R8G8B8A8_SNORM, + VK_FORMAT_R8G8B8A8_USCALED, + VK_FORMAT_R8G8B8A8_SSCALED, + VK_FORMAT_R8G8B8A8_UINT, + VK_FORMAT_R8G8B8A8_SINT, + VK_FORMAT_R8G8B8A8_SRGB, + VK_FORMAT_B8G8R8A8_UNORM, + VK_FORMAT_B8G8R8A8_SNORM, + VK_FORMAT_B8G8R8A8_USCALED, + VK_FORMAT_B8G8R8A8_SSCALED, + VK_FORMAT_B8G8R8A8_UINT, + VK_FORMAT_B8G8R8A8_SINT, + VK_FORMAT_B8G8R8A8_SRGB, + VK_FORMAT_A8B8G8R8_UNORM_PACK32, + VK_FORMAT_A8B8G8R8_SNORM_PACK32, + VK_FORMAT_A8B8G8R8_USCALED_PACK32, + VK_FORMAT_A8B8G8R8_SSCALED_PACK32, + VK_FORMAT_A8B8G8R8_UINT_PACK32, + VK_FORMAT_A8B8G8R8_SINT_PACK32, + VK_FORMAT_A8B8G8R8_SRGB_PACK32, + VK_FORMAT_A2R10G10B10_UNORM_PACK32, + VK_FORMAT_A2R10G10B10_SNORM_PACK32, + VK_FORMAT_A2R10G10B10_USCALED_PACK32, + VK_FORMAT_A2R10G10B10_SSCALED_PACK32, + VK_FORMAT_A2R10G10B10_UINT_PACK32, + VK_FORMAT_A2R10G10B10_SINT_PACK32, + VK_FORMAT_A2B10G10R10_UNORM_PACK32, + VK_FORMAT_A2B10G10R10_SNORM_PACK32, + VK_FORMAT_A2B10G10R10_USCALED_PACK32, + VK_FORMAT_A2B10G10R10_SSCALED_PACK32, + VK_FORMAT_A2B10G10R10_UINT_PACK32, + VK_FORMAT_A2B10G10R10_SINT_PACK32, + VK_FORMAT_R16_UNORM, + VK_FORMAT_R16_SNORM, + VK_FORMAT_R16_USCALED, + VK_FORMAT_R16_SSCALED, + VK_FORMAT_R16_UINT, + VK_FORMAT_R16_SINT, + VK_FORMAT_R16_SFLOAT, + VK_FORMAT_R16G16_UNORM, + VK_FORMAT_R16G16_SNORM, + VK_FORMAT_R16G16_USCALED, + VK_FORMAT_R16G16_SSCALED, + VK_FORMAT_R16G16_UINT, + VK_FORMAT_R16G16_SINT, + VK_FORMAT_R16G16_SFLOAT, + VK_FORMAT_R16G16B16_UNORM, + VK_FORMAT_R16G16B16_SNORM, + VK_FORMAT_R16G16B16_USCALED, + VK_FORMAT_R16G16B16_SSCALED, + VK_FORMAT_R16G16B16_UINT, + VK_FORMAT_R16G16B16_SINT, + VK_FORMAT_R16G16B16_SFLOAT, + VK_FORMAT_R16G16B16A16_UNORM, + VK_FORMAT_R16G16B16A16_SNORM, + VK_FORMAT_R16G16B16A16_USCALED, + VK_FORMAT_R16G16B16A16_SSCALED, + VK_FORMAT_R16G16B16A16_UINT, + VK_FORMAT_R16G16B16A16_SINT, + VK_FORMAT_R16G16B16A16_SFLOAT, + VK_FORMAT_R32_UINT, + VK_FORMAT_R32_SINT, + VK_FORMAT_R32_SFLOAT, + VK_FORMAT_R32G32_UINT, + VK_FORMAT_R32G32_SINT, + VK_FORMAT_R32G32_SFLOAT, + VK_FORMAT_R32G32B32_UINT, + VK_FORMAT_R32G32B32_SINT, + VK_FORMAT_R32G32B32_SFLOAT, + VK_FORMAT_R32G32B32A32_UINT, + VK_FORMAT_R32G32B32A32_SINT, + VK_FORMAT_R32G32B32A32_SFLOAT, + VK_FORMAT_R64_UINT, + VK_FORMAT_R64_SINT, + VK_FORMAT_R64_SFLOAT, + VK_FORMAT_R64G64_UINT, + VK_FORMAT_R64G64_SINT, + VK_FORMAT_R64G64_SFLOAT, + VK_FORMAT_R64G64B64_UINT, + VK_FORMAT_R64G64B64_SINT, + VK_FORMAT_R64G64B64_SFLOAT, + VK_FORMAT_R64G64B64A64_UINT, + VK_FORMAT_R64G64B64A64_SINT, + VK_FORMAT_R64G64B64A64_SFLOAT, + VK_FORMAT_B10G11R11_UFLOAT_PACK32, + VK_FORMAT_E5B9G9R9_UFLOAT_PACK32, + VK_FORMAT_D16_UNORM, + VK_FORMAT_X8_D24_UNORM_PACK32, + VK_FORMAT_D32_SFLOAT, + VK_FORMAT_S8_UINT, + VK_FORMAT_D16_UNORM_S8_UINT, + VK_FORMAT_D24_UNORM_S8_UINT, + VK_FORMAT_D32_SFLOAT_S8_UINT, + VK_FORMAT_BC1_RGB_UNORM_BLOCK, + VK_FORMAT_BC1_RGB_SRGB_BLOCK, + VK_FORMAT_BC1_RGBA_UNORM_BLOCK, + VK_FORMAT_BC1_RGBA_SRGB_BLOCK, + VK_FORMAT_BC2_UNORM_BLOCK, + VK_FORMAT_BC2_SRGB_BLOCK, + VK_FORMAT_BC3_UNORM_BLOCK, + VK_FORMAT_BC3_SRGB_BLOCK, + VK_FORMAT_BC4_UNORM_BLOCK, + VK_FORMAT_BC4_SNORM_BLOCK, + VK_FORMAT_BC5_UNORM_BLOCK, + VK_FORMAT_BC5_SNORM_BLOCK, + VK_FORMAT_BC6H_UFLOAT_BLOCK, + VK_FORMAT_BC6H_SFLOAT_BLOCK, + VK_FORMAT_BC7_UNORM_BLOCK, + VK_FORMAT_BC7_SRGB_BLOCK, + VK_FORMAT_ETC2_R8G8B8_UNORM_BLOCK, + VK_FORMAT_ETC2_R8G8B8_SRGB_BLOCK, + VK_FORMAT_ETC2_R8G8B8A1_UNORM_BLOCK, + VK_FORMAT_ETC2_R8G8B8A1_SRGB_BLOCK, + VK_FORMAT_ETC2_R8G8B8A8_UNORM_BLOCK, + VK_FORMAT_ETC2_R8G8B8A8_SRGB_BLOCK, + VK_FORMAT_EAC_R11_UNORM_BLOCK, + VK_FORMAT_EAC_R11_SNORM_BLOCK, + VK_FORMAT_EAC_R11G11_UNORM_BLOCK, + VK_FORMAT_EAC_R11G11_SNORM_BLOCK, + VK_FORMAT_ASTC_4x4_UNORM_BLOCK, + VK_FORMAT_ASTC_4x4_SRGB_BLOCK, + VK_FORMAT_ASTC_5x4_UNORM_BLOCK, + VK_FORMAT_ASTC_5x4_SRGB_BLOCK, + VK_FORMAT_ASTC_5x5_UNORM_BLOCK, + VK_FORMAT_ASTC_5x5_SRGB_BLOCK, + VK_FORMAT_ASTC_6x5_UNORM_BLOCK, + VK_FORMAT_ASTC_6x5_SRGB_BLOCK, + VK_FORMAT_ASTC_6x6_UNORM_BLOCK, + VK_FORMAT_ASTC_6x6_SRGB_BLOCK, + VK_FORMAT_ASTC_8x5_UNORM_BLOCK, + VK_FORMAT_ASTC_8x5_SRGB_BLOCK, + VK_FORMAT_ASTC_8x6_UNORM_BLOCK, + VK_FORMAT_ASTC_8x6_SRGB_BLOCK, + VK_FORMAT_ASTC_8x8_UNORM_BLOCK, + VK_FORMAT_ASTC_8x8_SRGB_BLOCK, + VK_FORMAT_ASTC_10x5_UNORM_BLOCK, + VK_FORMAT_ASTC_10x5_SRGB_BLOCK, + VK_FORMAT_ASTC_10x6_UNORM_BLOCK, + VK_FORMAT_ASTC_10x6_SRGB_BLOCK, + VK_FORMAT_ASTC_10x8_UNORM_BLOCK, + VK_FORMAT_ASTC_10x8_SRGB_BLOCK, + VK_FORMAT_ASTC_10x10_UNORM_BLOCK, + VK_FORMAT_ASTC_10x10_SRGB_BLOCK, + VK_FORMAT_ASTC_12x10_UNORM_BLOCK, + VK_FORMAT_ASTC_12x10_SRGB_BLOCK, + VK_FORMAT_ASTC_12x12_UNORM_BLOCK, + VK_FORMAT_ASTC_12x12_SRGB_BLOCK, + VK_FORMAT_G8B8G8R8_422_UNORM, + VK_FORMAT_B8G8R8G8_422_UNORM, + VK_FORMAT_G8_B8_R8_3PLANE_420_UNORM, + VK_FORMAT_G8_B8R8_2PLANE_420_UNORM, + VK_FORMAT_G8_B8_R8_3PLANE_422_UNORM, + VK_FORMAT_G8_B8R8_2PLANE_422_UNORM, + VK_FORMAT_G8_B8_R8_3PLANE_444_UNORM, + VK_FORMAT_R10X6_UNORM_PACK16, + VK_FORMAT_R10X6G10X6_UNORM_2PACK16, + VK_FORMAT_R10X6G10X6B10X6A10X6_UNORM_4PACK16, + VK_FORMAT_G10X6B10X6G10X6R10X6_422_UNORM_4PACK16, + VK_FORMAT_B10X6G10X6R10X6G10X6_422_UNORM_4PACK16, + VK_FORMAT_G10X6_B10X6_R10X6_3PLANE_420_UNORM_3PACK16, + VK_FORMAT_G10X6_B10X6R10X6_2PLANE_420_UNORM_3PACK16, + VK_FORMAT_G10X6_B10X6_R10X6_3PLANE_422_UNORM_3PACK16, + VK_FORMAT_G10X6_B10X6R10X6_2PLANE_422_UNORM_3PACK16, + VK_FORMAT_G10X6_B10X6_R10X6_3PLANE_444_UNORM_3PACK16, + VK_FORMAT_R12X4_UNORM_PACK16, + VK_FORMAT_R12X4G12X4_UNORM_2PACK16, + VK_FORMAT_R12X4G12X4B12X4A12X4_UNORM_4PACK16, + VK_FORMAT_G12X4B12X4G12X4R12X4_422_UNORM_4PACK16, + VK_FORMAT_B12X4G12X4R12X4G12X4_422_UNORM_4PACK16, + VK_FORMAT_G12X4_B12X4_R12X4_3PLANE_420_UNORM_3PACK16, + VK_FORMAT_G12X4_B12X4R12X4_2PLANE_420_UNORM_3PACK16, + VK_FORMAT_G12X4_B12X4_R12X4_3PLANE_422_UNORM_3PACK16, + VK_FORMAT_G12X4_B12X4R12X4_2PLANE_422_UNORM_3PACK16, + VK_FORMAT_G12X4_B12X4_R12X4_3PLANE_444_UNORM_3PACK16, + VK_FORMAT_G16B16G16R16_422_UNORM, + VK_FORMAT_B16G16R16G16_422_UNORM, + VK_FORMAT_G16_B16_R16_3PLANE_420_UNORM, + VK_FORMAT_G16_B16R16_2PLANE_420_UNORM, + VK_FORMAT_G16_B16_R16_3PLANE_422_UNORM, + VK_FORMAT_G16_B16R16_2PLANE_422_UNORM, + VK_FORMAT_G16_B16_R16_3PLANE_444_UNORM, + VK_FORMAT_PVRTC1_2BPP_UNORM_BLOCK_IMG, + VK_FORMAT_PVRTC1_4BPP_UNORM_BLOCK_IMG, + VK_FORMAT_PVRTC2_2BPP_UNORM_BLOCK_IMG, + VK_FORMAT_PVRTC2_4BPP_UNORM_BLOCK_IMG, + VK_FORMAT_PVRTC1_2BPP_SRGB_BLOCK_IMG, + VK_FORMAT_PVRTC1_4BPP_SRGB_BLOCK_IMG, + VK_FORMAT_PVRTC2_2BPP_SRGB_BLOCK_IMG, + VK_FORMAT_PVRTC2_4BPP_SRGB_BLOCK_IMG, +}; + +const char *RenderingDeviceVulkan::named_formats[RenderingDevice::DATA_FORMAT_MAX] = { + "R4G4_Unorm_Pack8", + "R4G4B4A4_Unorm_Pack16", + "B4G4R4A4_Unorm_Pack16", + "R5G6B5_Unorm_Pack16", + "B5G6R5_Unorm_Pack16", + "R5G5B5A1_Unorm_Pack16", + "B5G5R5A1_Unorm_Pack16", + "A1R5G5B5_Unorm_Pack16", + "R8_Unorm", + "R8_Snorm", + "R8_Uscaled", + "R8_Sscaled", + "R8_Uint", + "R8_Sint", + "R8_Srgb", + "R8G8_Unorm", + "R8G8_Snorm", + "R8G8_Uscaled", + "R8G8_Sscaled", + "R8G8_Uint", + "R8G8_Sint", + "R8G8_Srgb", + "R8G8B8_Unorm", + "R8G8B8_Snorm", + "R8G8B8_Uscaled", + "R8G8B8_Sscaled", + "R8G8B8_Uint", + "R8G8B8_Sint", + "R8G8B8_Srgb", + "B8G8R8_Unorm", + "B8G8R8_Snorm", + "B8G8R8_Uscaled", + "B8G8R8_Sscaled", + "B8G8R8_Uint", + "B8G8R8_Sint", + "B8G8R8_Srgb", + "R8G8B8A8_Unorm", + "R8G8B8A8_Snorm", + "R8G8B8A8_Uscaled", + "R8G8B8A8_Sscaled", + "R8G8B8A8_Uint", + "R8G8B8A8_Sint", + "R8G8B8A8_Srgb", + "B8G8R8A8_Unorm", + "B8G8R8A8_Snorm", + "B8G8R8A8_Uscaled", + "B8G8R8A8_Sscaled", + "B8G8R8A8_Uint", + "B8G8R8A8_Sint", + "B8G8R8A8_Srgb", + "A8B8G8R8_Unorm_Pack32", + "A8B8G8R8_Snorm_Pack32", + "A8B8G8R8_Uscaled_Pack32", + "A8B8G8R8_Sscaled_Pack32", + "A8B8G8R8_Uint_Pack32", + "A8B8G8R8_Sint_Pack32", + "A8B8G8R8_Srgb_Pack32", + "A2R10G10B10_Unorm_Pack32", + "A2R10G10B10_Snorm_Pack32", + "A2R10G10B10_Uscaled_Pack32", + "A2R10G10B10_Sscaled_Pack32", + "A2R10G10B10_Uint_Pack32", + "A2R10G10B10_Sint_Pack32", + "A2B10G10R10_Unorm_Pack32", + "A2B10G10R10_Snorm_Pack32", + "A2B10G10R10_Uscaled_Pack32", + "A2B10G10R10_Sscaled_Pack32", + "A2B10G10R10_Uint_Pack32", + "A2B10G10R10_Sint_Pack32", + "R16_Unorm", + "R16_Snorm", + "R16_Uscaled", + "R16_Sscaled", + "R16_Uint", + "R16_Sint", + "R16_Sfloat", + "R16G16_Unorm", + "R16G16_Snorm", + "R16G16_Uscaled", + "R16G16_Sscaled", + "R16G16_Uint", + "R16G16_Sint", + "R16G16_Sfloat", + "R16G16B16_Unorm", + "R16G16B16_Snorm", + "R16G16B16_Uscaled", + "R16G16B16_Sscaled", + "R16G16B16_Uint", + "R16G16B16_Sint", + "R16G16B16_Sfloat", + "R16G16B16A16_Unorm", + "R16G16B16A16_Snorm", + "R16G16B16A16_Uscaled", + "R16G16B16A16_Sscaled", + "R16G16B16A16_Uint", + "R16G16B16A16_Sint", + "R16G16B16A16_Sfloat", + "R32_Uint", + "R32_Sint", + "R32_Sfloat", + "R32G32_Uint", + "R32G32_Sint", + "R32G32_Sfloat", + "R32G32B32_Uint", + "R32G32B32_Sint", + "R32G32B32_Sfloat", + "R32G32B32A32_Uint", + "R32G32B32A32_Sint", + "R32G32B32A32_Sfloat", + "R64_Uint", + "R64_Sint", + "R64_Sfloat", + "R64G64_Uint", + "R64G64_Sint", + "R64G64_Sfloat", + "R64G64B64_Uint", + "R64G64B64_Sint", + "R64G64B64_Sfloat", + "R64G64B64A64_Uint", + "R64G64B64A64_Sint", + "R64G64B64A64_Sfloat", + "B10G11R11_Ufloat_Pack32", + "E5B9G9R9_Ufloat_Pack32", + "D16_Unorm", + "X8_D24_Unorm_Pack32", + "D32_Sfloat", + "S8_Uint", + "D16_Unorm_S8_Uint", + "D24_Unorm_S8_Uint", + "D32_Sfloat_S8_Uint", + "Bc1_Rgb_Unorm_Block", + "Bc1_Rgb_Srgb_Block", + "Bc1_Rgba_Unorm_Block", + "Bc1_Rgba_Srgb_Block", + "Bc2_Unorm_Block", + "Bc2_Srgb_Block", + "Bc3_Unorm_Block", + "Bc3_Srgb_Block", + "Bc4_Unorm_Block", + "Bc4_Snorm_Block", + "Bc5_Unorm_Block", + "Bc5_Snorm_Block", + "Bc6H_Ufloat_Block", + "Bc6H_Sfloat_Block", + "Bc7_Unorm_Block", + "Bc7_Srgb_Block", + "Etc2_R8G8B8_Unorm_Block", + "Etc2_R8G8B8_Srgb_Block", + "Etc2_R8G8B8A1_Unorm_Block", + "Etc2_R8G8B8A1_Srgb_Block", + "Etc2_R8G8B8A8_Unorm_Block", + "Etc2_R8G8B8A8_Srgb_Block", + "Eac_R11_Unorm_Block", + "Eac_R11_Snorm_Block", + "Eac_R11G11_Unorm_Block", + "Eac_R11G11_Snorm_Block", + "Astc_4X4_Unorm_Block", + "Astc_4X4_Srgb_Block", + "Astc_5X4_Unorm_Block", + "Astc_5X4_Srgb_Block", + "Astc_5X5_Unorm_Block", + "Astc_5X5_Srgb_Block", + "Astc_6X5_Unorm_Block", + "Astc_6X5_Srgb_Block", + "Astc_6X6_Unorm_Block", + "Astc_6X6_Srgb_Block", + "Astc_8X5_Unorm_Block", + "Astc_8X5_Srgb_Block", + "Astc_8X6_Unorm_Block", + "Astc_8X6_Srgb_Block", + "Astc_8X8_Unorm_Block", + "Astc_8X8_Srgb_Block", + "Astc_10X5_Unorm_Block", + "Astc_10X5_Srgb_Block", + "Astc_10X6_Unorm_Block", + "Astc_10X6_Srgb_Block", + "Astc_10X8_Unorm_Block", + "Astc_10X8_Srgb_Block", + "Astc_10X10_Unorm_Block", + "Astc_10X10_Srgb_Block", + "Astc_12X10_Unorm_Block", + "Astc_12X10_Srgb_Block", + "Astc_12X12_Unorm_Block", + "Astc_12X12_Srgb_Block", + "G8B8G8R8_422_Unorm", + "B8G8R8G8_422_Unorm", + "G8_B8_R8_3Plane_420_Unorm", + "G8_B8R8_2Plane_420_Unorm", + "G8_B8_R8_3Plane_422_Unorm", + "G8_B8R8_2Plane_422_Unorm", + "G8_B8_R8_3Plane_444_Unorm", + "R10X6_Unorm_Pack16", + "R10X6G10X6_Unorm_2Pack16", + "R10X6G10X6B10X6A10X6_Unorm_4Pack16", + "G10X6B10X6G10X6R10X6_422_Unorm_4Pack16", + "B10X6G10X6R10X6G10X6_422_Unorm_4Pack16", + "G10X6_B10X6_R10X6_3Plane_420_Unorm_3Pack16", + "G10X6_B10X6R10X6_2Plane_420_Unorm_3Pack16", + "G10X6_B10X6_R10X6_3Plane_422_Unorm_3Pack16", + "G10X6_B10X6R10X6_2Plane_422_Unorm_3Pack16", + "G10X6_B10X6_R10X6_3Plane_444_Unorm_3Pack16", + "R12X4_Unorm_Pack16", + "R12X4G12X4_Unorm_2Pack16", + "R12X4G12X4B12X4A12X4_Unorm_4Pack16", + "G12X4B12X4G12X4R12X4_422_Unorm_4Pack16", + "B12X4G12X4R12X4G12X4_422_Unorm_4Pack16", + "G12X4_B12X4_R12X4_3Plane_420_Unorm_3Pack16", + "G12X4_B12X4R12X4_2Plane_420_Unorm_3Pack16", + "G12X4_B12X4_R12X4_3Plane_422_Unorm_3Pack16", + "G12X4_B12X4R12X4_2Plane_422_Unorm_3Pack16", + "G12X4_B12X4_R12X4_3Plane_444_Unorm_3Pack16", + "G16B16G16R16_422_Unorm", + "B16G16R16G16_422_Unorm", + "G16_B16_R16_3Plane_420_Unorm", + "G16_B16R16_2Plane_420_Unorm", + "G16_B16_R16_3Plane_422_Unorm", + "G16_B16R16_2Plane_422_Unorm", + "G16_B16_R16_3Plane_444_Unorm", + "Pvrtc1_2Bpp_Unorm_Block_Img", + "Pvrtc1_4Bpp_Unorm_Block_Img", + "Pvrtc2_2Bpp_Unorm_Block_Img", + "Pvrtc2_4Bpp_Unorm_Block_Img", + "Pvrtc1_2Bpp_Srgb_Block_Img", + "Pvrtc1_4Bpp_Srgb_Block_Img", + "Pvrtc2_2Bpp_Srgb_Block_Img", + "Pvrtc2_4Bpp_Srgb_Block_Img" +}; + +int RenderingDeviceVulkan::get_format_vertex_size(DataFormat p_format) { + switch (p_format) { + case DATA_FORMAT_R8_UNORM: + case DATA_FORMAT_R8_SNORM: + case DATA_FORMAT_R8_UINT: + case DATA_FORMAT_R8_SINT: + case DATA_FORMAT_R8G8_UNORM: + case DATA_FORMAT_R8G8_SNORM: + case DATA_FORMAT_R8G8_UINT: + case DATA_FORMAT_R8G8_SINT: + case DATA_FORMAT_R8G8B8_UNORM: + case DATA_FORMAT_R8G8B8_SNORM: + case DATA_FORMAT_R8G8B8_UINT: + case DATA_FORMAT_R8G8B8_SINT: + case DATA_FORMAT_B8G8R8_UNORM: + case DATA_FORMAT_B8G8R8_SNORM: + case DATA_FORMAT_B8G8R8_UINT: + case DATA_FORMAT_B8G8R8_SINT: + case DATA_FORMAT_R8G8B8A8_UNORM: + case DATA_FORMAT_R8G8B8A8_SNORM: + case DATA_FORMAT_R8G8B8A8_UINT: + case DATA_FORMAT_R8G8B8A8_SINT: + case DATA_FORMAT_B8G8R8A8_UNORM: + case DATA_FORMAT_B8G8R8A8_SNORM: + case DATA_FORMAT_B8G8R8A8_UINT: + case DATA_FORMAT_B8G8R8A8_SINT: return 4; + case DATA_FORMAT_R16_UNORM: + case DATA_FORMAT_R16_SNORM: + case DATA_FORMAT_R16_UINT: + case DATA_FORMAT_R16_SINT: + case DATA_FORMAT_R16_SFLOAT: return 4; + case DATA_FORMAT_R16G16_UNORM: + case DATA_FORMAT_R16G16_SNORM: + case DATA_FORMAT_R16G16_UINT: + case DATA_FORMAT_R16G16_SINT: + case DATA_FORMAT_R16G16_SFLOAT: return 4; + case DATA_FORMAT_R16G16B16_UNORM: + case DATA_FORMAT_R16G16B16_SNORM: + case DATA_FORMAT_R16G16B16_UINT: + case DATA_FORMAT_R16G16B16_SINT: + case DATA_FORMAT_R16G16B16_SFLOAT: return 8; + case DATA_FORMAT_R16G16B16A16_UNORM: + case DATA_FORMAT_R16G16B16A16_SNORM: + case DATA_FORMAT_R16G16B16A16_UINT: + case DATA_FORMAT_R16G16B16A16_SINT: + case DATA_FORMAT_R16G16B16A16_SFLOAT: return 8; + case DATA_FORMAT_R32_UINT: + case DATA_FORMAT_R32_SINT: + case DATA_FORMAT_R32_SFLOAT: return 4; + case DATA_FORMAT_R32G32_UINT: + case DATA_FORMAT_R32G32_SINT: + case DATA_FORMAT_R32G32_SFLOAT: return 8; + case DATA_FORMAT_R32G32B32_UINT: + case DATA_FORMAT_R32G32B32_SINT: + case DATA_FORMAT_R32G32B32_SFLOAT: return 12; + case DATA_FORMAT_R32G32B32A32_UINT: + case DATA_FORMAT_R32G32B32A32_SINT: + case DATA_FORMAT_R32G32B32A32_SFLOAT: return 16; + case DATA_FORMAT_R64_UINT: + case DATA_FORMAT_R64_SINT: + case DATA_FORMAT_R64_SFLOAT: return 8; + case DATA_FORMAT_R64G64_UINT: + case DATA_FORMAT_R64G64_SINT: + case DATA_FORMAT_R64G64_SFLOAT: return 16; + case DATA_FORMAT_R64G64B64_UINT: + case DATA_FORMAT_R64G64B64_SINT: + case DATA_FORMAT_R64G64B64_SFLOAT: return 24; + case DATA_FORMAT_R64G64B64A64_UINT: + case DATA_FORMAT_R64G64B64A64_SINT: + case DATA_FORMAT_R64G64B64A64_SFLOAT: return 32; + default: return 0; + } +} + +uint32_t RenderingDeviceVulkan::get_image_format_pixel_size(DataFormat p_format) { + + switch (p_format) { + + case DATA_FORMAT_R4G4_UNORM_PACK8: return 1; + case DATA_FORMAT_R4G4B4A4_UNORM_PACK16: + case DATA_FORMAT_B4G4R4A4_UNORM_PACK16: + case DATA_FORMAT_R5G6B5_UNORM_PACK16: + case DATA_FORMAT_B5G6R5_UNORM_PACK16: + case DATA_FORMAT_R5G5B5A1_UNORM_PACK16: + case DATA_FORMAT_B5G5R5A1_UNORM_PACK16: + case DATA_FORMAT_A1R5G5B5_UNORM_PACK16: return 2; + case DATA_FORMAT_R8_UNORM: + case DATA_FORMAT_R8_SNORM: + case DATA_FORMAT_R8_USCALED: + case DATA_FORMAT_R8_SSCALED: + case DATA_FORMAT_R8_UINT: + case DATA_FORMAT_R8_SINT: + case DATA_FORMAT_R8_SRGB: return 1; + case DATA_FORMAT_R8G8_UNORM: + case DATA_FORMAT_R8G8_SNORM: + case DATA_FORMAT_R8G8_USCALED: + case DATA_FORMAT_R8G8_SSCALED: + case DATA_FORMAT_R8G8_UINT: + case DATA_FORMAT_R8G8_SINT: + case DATA_FORMAT_R8G8_SRGB: return 2; + case DATA_FORMAT_R8G8B8_UNORM: + case DATA_FORMAT_R8G8B8_SNORM: + case DATA_FORMAT_R8G8B8_USCALED: + case DATA_FORMAT_R8G8B8_SSCALED: + case DATA_FORMAT_R8G8B8_UINT: + case DATA_FORMAT_R8G8B8_SINT: + case DATA_FORMAT_R8G8B8_SRGB: + case DATA_FORMAT_B8G8R8_UNORM: + case DATA_FORMAT_B8G8R8_SNORM: + case DATA_FORMAT_B8G8R8_USCALED: + case DATA_FORMAT_B8G8R8_SSCALED: + case DATA_FORMAT_B8G8R8_UINT: + case DATA_FORMAT_B8G8R8_SINT: + case DATA_FORMAT_B8G8R8_SRGB: return 3; + case DATA_FORMAT_R8G8B8A8_UNORM: + case DATA_FORMAT_R8G8B8A8_SNORM: + case DATA_FORMAT_R8G8B8A8_USCALED: + case DATA_FORMAT_R8G8B8A8_SSCALED: + case DATA_FORMAT_R8G8B8A8_UINT: + case DATA_FORMAT_R8G8B8A8_SINT: + case DATA_FORMAT_R8G8B8A8_SRGB: + case DATA_FORMAT_B8G8R8A8_UNORM: + case DATA_FORMAT_B8G8R8A8_SNORM: + case DATA_FORMAT_B8G8R8A8_USCALED: + case DATA_FORMAT_B8G8R8A8_SSCALED: + case DATA_FORMAT_B8G8R8A8_UINT: + case DATA_FORMAT_B8G8R8A8_SINT: + case DATA_FORMAT_B8G8R8A8_SRGB: return 4; + case DATA_FORMAT_A8B8G8R8_UNORM_PACK32: + case DATA_FORMAT_A8B8G8R8_SNORM_PACK32: + case DATA_FORMAT_A8B8G8R8_USCALED_PACK32: + case DATA_FORMAT_A8B8G8R8_SSCALED_PACK32: + case DATA_FORMAT_A8B8G8R8_UINT_PACK32: + case DATA_FORMAT_A8B8G8R8_SINT_PACK32: + case DATA_FORMAT_A8B8G8R8_SRGB_PACK32: + case DATA_FORMAT_A2R10G10B10_UNORM_PACK32: + case DATA_FORMAT_A2R10G10B10_SNORM_PACK32: + case DATA_FORMAT_A2R10G10B10_USCALED_PACK32: + case DATA_FORMAT_A2R10G10B10_SSCALED_PACK32: + case DATA_FORMAT_A2R10G10B10_UINT_PACK32: + case DATA_FORMAT_A2R10G10B10_SINT_PACK32: + case DATA_FORMAT_A2B10G10R10_UNORM_PACK32: + case DATA_FORMAT_A2B10G10R10_SNORM_PACK32: + case DATA_FORMAT_A2B10G10R10_USCALED_PACK32: + case DATA_FORMAT_A2B10G10R10_SSCALED_PACK32: + case DATA_FORMAT_A2B10G10R10_UINT_PACK32: + case DATA_FORMAT_A2B10G10R10_SINT_PACK32: return 4; + case DATA_FORMAT_R16_UNORM: + case DATA_FORMAT_R16_SNORM: + case DATA_FORMAT_R16_USCALED: + case DATA_FORMAT_R16_SSCALED: + case DATA_FORMAT_R16_UINT: + case DATA_FORMAT_R16_SINT: + case DATA_FORMAT_R16_SFLOAT: return 2; + case DATA_FORMAT_R16G16_UNORM: + case DATA_FORMAT_R16G16_SNORM: + case DATA_FORMAT_R16G16_USCALED: + case DATA_FORMAT_R16G16_SSCALED: + case DATA_FORMAT_R16G16_UINT: + case DATA_FORMAT_R16G16_SINT: + case DATA_FORMAT_R16G16_SFLOAT: return 4; + case DATA_FORMAT_R16G16B16_UNORM: + case DATA_FORMAT_R16G16B16_SNORM: + case DATA_FORMAT_R16G16B16_USCALED: + case DATA_FORMAT_R16G16B16_SSCALED: + case DATA_FORMAT_R16G16B16_UINT: + case DATA_FORMAT_R16G16B16_SINT: + case DATA_FORMAT_R16G16B16_SFLOAT: return 6; + case DATA_FORMAT_R16G16B16A16_UNORM: + case DATA_FORMAT_R16G16B16A16_SNORM: + case DATA_FORMAT_R16G16B16A16_USCALED: + case DATA_FORMAT_R16G16B16A16_SSCALED: + case DATA_FORMAT_R16G16B16A16_UINT: + case DATA_FORMAT_R16G16B16A16_SINT: + case DATA_FORMAT_R16G16B16A16_SFLOAT: return 8; + case DATA_FORMAT_R32_UINT: + case DATA_FORMAT_R32_SINT: + case DATA_FORMAT_R32_SFLOAT: return 4; + case DATA_FORMAT_R32G32_UINT: + case DATA_FORMAT_R32G32_SINT: + case DATA_FORMAT_R32G32_SFLOAT: return 8; + case DATA_FORMAT_R32G32B32_UINT: + case DATA_FORMAT_R32G32B32_SINT: + case DATA_FORMAT_R32G32B32_SFLOAT: return 12; + case DATA_FORMAT_R32G32B32A32_UINT: + case DATA_FORMAT_R32G32B32A32_SINT: + case DATA_FORMAT_R32G32B32A32_SFLOAT: return 16; + case DATA_FORMAT_R64_UINT: + case DATA_FORMAT_R64_SINT: + case DATA_FORMAT_R64_SFLOAT: return 8; + case DATA_FORMAT_R64G64_UINT: + case DATA_FORMAT_R64G64_SINT: + case DATA_FORMAT_R64G64_SFLOAT: return 16; + case DATA_FORMAT_R64G64B64_UINT: + case DATA_FORMAT_R64G64B64_SINT: + case DATA_FORMAT_R64G64B64_SFLOAT: return 24; + case DATA_FORMAT_R64G64B64A64_UINT: + case DATA_FORMAT_R64G64B64A64_SINT: + case DATA_FORMAT_R64G64B64A64_SFLOAT: return 32; + case DATA_FORMAT_B10G11R11_UFLOAT_PACK32: + case DATA_FORMAT_E5B9G9R9_UFLOAT_PACK32: return 4; + case DATA_FORMAT_D16_UNORM: return 2; + case DATA_FORMAT_X8_D24_UNORM_PACK32: return 4; + case DATA_FORMAT_D32_SFLOAT: return 4; + case DATA_FORMAT_S8_UINT: return 1; + case DATA_FORMAT_D16_UNORM_S8_UINT: return 4; + case DATA_FORMAT_D24_UNORM_S8_UINT: return 4; + case DATA_FORMAT_D32_SFLOAT_S8_UINT: + return 5; //? + case DATA_FORMAT_BC1_RGB_UNORM_BLOCK: + case DATA_FORMAT_BC1_RGB_SRGB_BLOCK: + case DATA_FORMAT_BC1_RGBA_UNORM_BLOCK: + case DATA_FORMAT_BC1_RGBA_SRGB_BLOCK: + case DATA_FORMAT_BC2_UNORM_BLOCK: + case DATA_FORMAT_BC2_SRGB_BLOCK: + case DATA_FORMAT_BC3_UNORM_BLOCK: + case DATA_FORMAT_BC3_SRGB_BLOCK: + case DATA_FORMAT_BC4_UNORM_BLOCK: + case DATA_FORMAT_BC4_SNORM_BLOCK: + case DATA_FORMAT_BC5_UNORM_BLOCK: + case DATA_FORMAT_BC5_SNORM_BLOCK: + case DATA_FORMAT_BC6H_UFLOAT_BLOCK: + case DATA_FORMAT_BC6H_SFLOAT_BLOCK: + case DATA_FORMAT_BC7_UNORM_BLOCK: + case DATA_FORMAT_BC7_SRGB_BLOCK: return 1; + case DATA_FORMAT_ETC2_R8G8B8_UNORM_BLOCK: + case DATA_FORMAT_ETC2_R8G8B8_SRGB_BLOCK: + case DATA_FORMAT_ETC2_R8G8B8A1_UNORM_BLOCK: + case DATA_FORMAT_ETC2_R8G8B8A1_SRGB_BLOCK: + case DATA_FORMAT_ETC2_R8G8B8A8_UNORM_BLOCK: + case DATA_FORMAT_ETC2_R8G8B8A8_SRGB_BLOCK: return 1; + case DATA_FORMAT_EAC_R11_UNORM_BLOCK: + case DATA_FORMAT_EAC_R11_SNORM_BLOCK: + case DATA_FORMAT_EAC_R11G11_UNORM_BLOCK: + case DATA_FORMAT_EAC_R11G11_SNORM_BLOCK: return 1; + case DATA_FORMAT_ASTC_4x4_UNORM_BLOCK: + case DATA_FORMAT_ASTC_4x4_SRGB_BLOCK: + case DATA_FORMAT_ASTC_5x4_UNORM_BLOCK: + case DATA_FORMAT_ASTC_5x4_SRGB_BLOCK: + case DATA_FORMAT_ASTC_5x5_UNORM_BLOCK: + case DATA_FORMAT_ASTC_5x5_SRGB_BLOCK: + case DATA_FORMAT_ASTC_6x5_UNORM_BLOCK: + case DATA_FORMAT_ASTC_6x5_SRGB_BLOCK: + case DATA_FORMAT_ASTC_6x6_UNORM_BLOCK: + case DATA_FORMAT_ASTC_6x6_SRGB_BLOCK: + case DATA_FORMAT_ASTC_8x5_UNORM_BLOCK: + case DATA_FORMAT_ASTC_8x5_SRGB_BLOCK: + case DATA_FORMAT_ASTC_8x6_UNORM_BLOCK: + case DATA_FORMAT_ASTC_8x6_SRGB_BLOCK: + case DATA_FORMAT_ASTC_8x8_UNORM_BLOCK: + case DATA_FORMAT_ASTC_8x8_SRGB_BLOCK: + case DATA_FORMAT_ASTC_10x5_UNORM_BLOCK: + case DATA_FORMAT_ASTC_10x5_SRGB_BLOCK: + case DATA_FORMAT_ASTC_10x6_UNORM_BLOCK: + case DATA_FORMAT_ASTC_10x6_SRGB_BLOCK: + case DATA_FORMAT_ASTC_10x8_UNORM_BLOCK: + case DATA_FORMAT_ASTC_10x8_SRGB_BLOCK: + case DATA_FORMAT_ASTC_10x10_UNORM_BLOCK: + case DATA_FORMAT_ASTC_10x10_SRGB_BLOCK: + case DATA_FORMAT_ASTC_12x10_UNORM_BLOCK: + case DATA_FORMAT_ASTC_12x10_SRGB_BLOCK: + case DATA_FORMAT_ASTC_12x12_UNORM_BLOCK: + case DATA_FORMAT_ASTC_12x12_SRGB_BLOCK: return 1; + case DATA_FORMAT_G8B8G8R8_422_UNORM: + case DATA_FORMAT_B8G8R8G8_422_UNORM: return 4; + case DATA_FORMAT_G8_B8_R8_3PLANE_420_UNORM: + case DATA_FORMAT_G8_B8R8_2PLANE_420_UNORM: + case DATA_FORMAT_G8_B8_R8_3PLANE_422_UNORM: + case DATA_FORMAT_G8_B8R8_2PLANE_422_UNORM: + case DATA_FORMAT_G8_B8_R8_3PLANE_444_UNORM: return 4; + case DATA_FORMAT_R10X6_UNORM_PACK16: + case DATA_FORMAT_R10X6G10X6_UNORM_2PACK16: + case DATA_FORMAT_R10X6G10X6B10X6A10X6_UNORM_4PACK16: + case DATA_FORMAT_G10X6B10X6G10X6R10X6_422_UNORM_4PACK16: + case DATA_FORMAT_B10X6G10X6R10X6G10X6_422_UNORM_4PACK16: + case DATA_FORMAT_G10X6_B10X6_R10X6_3PLANE_420_UNORM_3PACK16: + case DATA_FORMAT_G10X6_B10X6R10X6_2PLANE_420_UNORM_3PACK16: + case DATA_FORMAT_G10X6_B10X6_R10X6_3PLANE_422_UNORM_3PACK16: + case DATA_FORMAT_G10X6_B10X6R10X6_2PLANE_422_UNORM_3PACK16: + case DATA_FORMAT_G10X6_B10X6_R10X6_3PLANE_444_UNORM_3PACK16: + case DATA_FORMAT_R12X4_UNORM_PACK16: + case DATA_FORMAT_R12X4G12X4_UNORM_2PACK16: + case DATA_FORMAT_R12X4G12X4B12X4A12X4_UNORM_4PACK16: + case DATA_FORMAT_G12X4B12X4G12X4R12X4_422_UNORM_4PACK16: + case DATA_FORMAT_B12X4G12X4R12X4G12X4_422_UNORM_4PACK16: + case DATA_FORMAT_G12X4_B12X4_R12X4_3PLANE_420_UNORM_3PACK16: + case DATA_FORMAT_G12X4_B12X4R12X4_2PLANE_420_UNORM_3PACK16: + case DATA_FORMAT_G12X4_B12X4_R12X4_3PLANE_422_UNORM_3PACK16: + case DATA_FORMAT_G12X4_B12X4R12X4_2PLANE_422_UNORM_3PACK16: + case DATA_FORMAT_G12X4_B12X4_R12X4_3PLANE_444_UNORM_3PACK16: return 2; + case DATA_FORMAT_G16B16G16R16_422_UNORM: + case DATA_FORMAT_B16G16R16G16_422_UNORM: + case DATA_FORMAT_G16_B16_R16_3PLANE_420_UNORM: + case DATA_FORMAT_G16_B16R16_2PLANE_420_UNORM: + case DATA_FORMAT_G16_B16_R16_3PLANE_422_UNORM: + case DATA_FORMAT_G16_B16R16_2PLANE_422_UNORM: + case DATA_FORMAT_G16_B16_R16_3PLANE_444_UNORM: return 8; + case DATA_FORMAT_PVRTC1_2BPP_UNORM_BLOCK_IMG: + case DATA_FORMAT_PVRTC1_4BPP_UNORM_BLOCK_IMG: + case DATA_FORMAT_PVRTC2_2BPP_UNORM_BLOCK_IMG: + case DATA_FORMAT_PVRTC2_4BPP_UNORM_BLOCK_IMG: + case DATA_FORMAT_PVRTC1_2BPP_SRGB_BLOCK_IMG: + case DATA_FORMAT_PVRTC1_4BPP_SRGB_BLOCK_IMG: + case DATA_FORMAT_PVRTC2_2BPP_SRGB_BLOCK_IMG: + case DATA_FORMAT_PVRTC2_4BPP_SRGB_BLOCK_IMG: return 1; + default: { + ERR_PRINT("Format not handled, bug"); + } + } + + return 1; +} + +// https://www.khronos.org/registry/DataFormat/specs/1.1/dataformat.1.1.pdf + +void RenderingDeviceVulkan::get_compressed_image_format_block_dimensions(DataFormat p_format, uint32_t &r_w, uint32_t &r_h) { + + switch (p_format) { + case DATA_FORMAT_BC1_RGB_UNORM_BLOCK: + case DATA_FORMAT_BC1_RGB_SRGB_BLOCK: + case DATA_FORMAT_BC1_RGBA_UNORM_BLOCK: + case DATA_FORMAT_BC1_RGBA_SRGB_BLOCK: + case DATA_FORMAT_BC2_UNORM_BLOCK: + case DATA_FORMAT_BC2_SRGB_BLOCK: + case DATA_FORMAT_BC3_UNORM_BLOCK: + case DATA_FORMAT_BC3_SRGB_BLOCK: + case DATA_FORMAT_BC4_UNORM_BLOCK: + case DATA_FORMAT_BC4_SNORM_BLOCK: + case DATA_FORMAT_BC5_UNORM_BLOCK: + case DATA_FORMAT_BC5_SNORM_BLOCK: + case DATA_FORMAT_BC6H_UFLOAT_BLOCK: + case DATA_FORMAT_BC6H_SFLOAT_BLOCK: + case DATA_FORMAT_BC7_UNORM_BLOCK: + case DATA_FORMAT_BC7_SRGB_BLOCK: + case DATA_FORMAT_ETC2_R8G8B8_UNORM_BLOCK: + case DATA_FORMAT_ETC2_R8G8B8_SRGB_BLOCK: + case DATA_FORMAT_ETC2_R8G8B8A1_UNORM_BLOCK: + case DATA_FORMAT_ETC2_R8G8B8A1_SRGB_BLOCK: + case DATA_FORMAT_ETC2_R8G8B8A8_UNORM_BLOCK: + case DATA_FORMAT_ETC2_R8G8B8A8_SRGB_BLOCK: + case DATA_FORMAT_EAC_R11_UNORM_BLOCK: + 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_SRGB_BLOCK: + case DATA_FORMAT_ASTC_5x4_UNORM_BLOCK: + case DATA_FORMAT_ASTC_5x4_SRGB_BLOCK: + case DATA_FORMAT_ASTC_5x5_UNORM_BLOCK: + case DATA_FORMAT_ASTC_5x5_SRGB_BLOCK: + case DATA_FORMAT_ASTC_6x5_UNORM_BLOCK: + case DATA_FORMAT_ASTC_6x5_SRGB_BLOCK: + case DATA_FORMAT_ASTC_6x6_UNORM_BLOCK: + case DATA_FORMAT_ASTC_6x6_SRGB_BLOCK: + case DATA_FORMAT_ASTC_8x5_UNORM_BLOCK: + case DATA_FORMAT_ASTC_8x5_SRGB_BLOCK: + case DATA_FORMAT_ASTC_8x6_UNORM_BLOCK: + case DATA_FORMAT_ASTC_8x6_SRGB_BLOCK: + case DATA_FORMAT_ASTC_8x8_UNORM_BLOCK: + case DATA_FORMAT_ASTC_8x8_SRGB_BLOCK: + case DATA_FORMAT_ASTC_10x5_UNORM_BLOCK: + case DATA_FORMAT_ASTC_10x5_SRGB_BLOCK: + case DATA_FORMAT_ASTC_10x6_UNORM_BLOCK: + case DATA_FORMAT_ASTC_10x6_SRGB_BLOCK: + case DATA_FORMAT_ASTC_10x8_UNORM_BLOCK: + case DATA_FORMAT_ASTC_10x8_SRGB_BLOCK: + case DATA_FORMAT_ASTC_10x10_UNORM_BLOCK: + case DATA_FORMAT_ASTC_10x10_SRGB_BLOCK: + case DATA_FORMAT_ASTC_12x10_UNORM_BLOCK: + case DATA_FORMAT_ASTC_12x10_SRGB_BLOCK: + case DATA_FORMAT_ASTC_12x12_UNORM_BLOCK: + case DATA_FORMAT_ASTC_12x12_SRGB_BLOCK: + r_w = 4; + r_h = 4; + return; + case DATA_FORMAT_PVRTC1_4BPP_UNORM_BLOCK_IMG: + case DATA_FORMAT_PVRTC2_4BPP_UNORM_BLOCK_IMG: + case DATA_FORMAT_PVRTC1_4BPP_SRGB_BLOCK_IMG: + case DATA_FORMAT_PVRTC2_4BPP_SRGB_BLOCK_IMG: + r_w = 4; + r_h = 4; + return; + case DATA_FORMAT_PVRTC1_2BPP_UNORM_BLOCK_IMG: + case DATA_FORMAT_PVRTC2_2BPP_UNORM_BLOCK_IMG: + case DATA_FORMAT_PVRTC1_2BPP_SRGB_BLOCK_IMG: + case DATA_FORMAT_PVRTC2_2BPP_SRGB_BLOCK_IMG: + r_w = 8; + r_h = 4; + return; + default: { + r_w = 1; + r_h = 1; + } + } +} + +uint32_t RenderingDeviceVulkan::get_compressed_image_format_block_byte_size(DataFormat p_format) { + + switch (p_format) { + case DATA_FORMAT_BC1_RGB_UNORM_BLOCK: + case DATA_FORMAT_BC1_RGB_SRGB_BLOCK: + case DATA_FORMAT_BC1_RGBA_UNORM_BLOCK: + case DATA_FORMAT_BC1_RGBA_SRGB_BLOCK: return 8; + case DATA_FORMAT_BC2_UNORM_BLOCK: + case DATA_FORMAT_BC2_SRGB_BLOCK: return 16; + case DATA_FORMAT_BC3_UNORM_BLOCK: + case DATA_FORMAT_BC3_SRGB_BLOCK: return 16; + case DATA_FORMAT_BC4_UNORM_BLOCK: + case DATA_FORMAT_BC4_SNORM_BLOCK: return 8; + case DATA_FORMAT_BC5_UNORM_BLOCK: + case DATA_FORMAT_BC5_SNORM_BLOCK: return 16; + case DATA_FORMAT_BC6H_UFLOAT_BLOCK: + case DATA_FORMAT_BC6H_SFLOAT_BLOCK: return 16; + case DATA_FORMAT_BC7_UNORM_BLOCK: + case DATA_FORMAT_BC7_SRGB_BLOCK: return 16; + case DATA_FORMAT_ETC2_R8G8B8_UNORM_BLOCK: + case DATA_FORMAT_ETC2_R8G8B8_SRGB_BLOCK: return 8; + case DATA_FORMAT_ETC2_R8G8B8A1_UNORM_BLOCK: + case DATA_FORMAT_ETC2_R8G8B8A1_SRGB_BLOCK: return 8; + case DATA_FORMAT_ETC2_R8G8B8A8_UNORM_BLOCK: + case DATA_FORMAT_ETC2_R8G8B8A8_SRGB_BLOCK: return 16; + case DATA_FORMAT_EAC_R11_UNORM_BLOCK: + case DATA_FORMAT_EAC_R11_SNORM_BLOCK: return 8; + 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_SRGB_BLOCK: + case DATA_FORMAT_ASTC_5x4_UNORM_BLOCK: + case DATA_FORMAT_ASTC_5x4_SRGB_BLOCK: + case DATA_FORMAT_ASTC_5x5_UNORM_BLOCK: + case DATA_FORMAT_ASTC_5x5_SRGB_BLOCK: + case DATA_FORMAT_ASTC_6x5_UNORM_BLOCK: + case DATA_FORMAT_ASTC_6x5_SRGB_BLOCK: + case DATA_FORMAT_ASTC_6x6_UNORM_BLOCK: + case DATA_FORMAT_ASTC_6x6_SRGB_BLOCK: + case DATA_FORMAT_ASTC_8x5_UNORM_BLOCK: + case DATA_FORMAT_ASTC_8x5_SRGB_BLOCK: + case DATA_FORMAT_ASTC_8x6_UNORM_BLOCK: + case DATA_FORMAT_ASTC_8x6_SRGB_BLOCK: + case DATA_FORMAT_ASTC_8x8_UNORM_BLOCK: + case DATA_FORMAT_ASTC_8x8_SRGB_BLOCK: + case DATA_FORMAT_ASTC_10x5_UNORM_BLOCK: + case DATA_FORMAT_ASTC_10x5_SRGB_BLOCK: + case DATA_FORMAT_ASTC_10x6_UNORM_BLOCK: + case DATA_FORMAT_ASTC_10x6_SRGB_BLOCK: + case DATA_FORMAT_ASTC_10x8_UNORM_BLOCK: + case DATA_FORMAT_ASTC_10x8_SRGB_BLOCK: + case DATA_FORMAT_ASTC_10x10_UNORM_BLOCK: + case DATA_FORMAT_ASTC_10x10_SRGB_BLOCK: + case DATA_FORMAT_ASTC_12x10_UNORM_BLOCK: + case DATA_FORMAT_ASTC_12x10_SRGB_BLOCK: + case DATA_FORMAT_ASTC_12x12_UNORM_BLOCK: + case DATA_FORMAT_ASTC_12x12_SRGB_BLOCK: + return 8; //wrong + case DATA_FORMAT_PVRTC1_4BPP_UNORM_BLOCK_IMG: + case DATA_FORMAT_PVRTC2_4BPP_UNORM_BLOCK_IMG: + case DATA_FORMAT_PVRTC1_4BPP_SRGB_BLOCK_IMG: + case DATA_FORMAT_PVRTC2_4BPP_SRGB_BLOCK_IMG: + case DATA_FORMAT_PVRTC1_2BPP_UNORM_BLOCK_IMG: + case DATA_FORMAT_PVRTC2_2BPP_UNORM_BLOCK_IMG: + case DATA_FORMAT_PVRTC1_2BPP_SRGB_BLOCK_IMG: + case DATA_FORMAT_PVRTC2_2BPP_SRGB_BLOCK_IMG: + return 8; //what varies is resolution + default: { + } + } + return 1; +} + +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_SRGB_BLOCK: + case DATA_FORMAT_BC1_RGBA_UNORM_BLOCK: + case DATA_FORMAT_BC1_RGBA_SRGB_BLOCK: + case DATA_FORMAT_BC4_UNORM_BLOCK: + case DATA_FORMAT_BC4_SNORM_BLOCK: + case DATA_FORMAT_ETC2_R8G8B8_UNORM_BLOCK: + case DATA_FORMAT_ETC2_R8G8B8_SRGB_BLOCK: + case DATA_FORMAT_ETC2_R8G8B8A1_UNORM_BLOCK: + case DATA_FORMAT_ETC2_R8G8B8A1_SRGB_BLOCK: + case DATA_FORMAT_EAC_R11_UNORM_BLOCK: + case DATA_FORMAT_EAC_R11_SNORM_BLOCK: + case DATA_FORMAT_PVRTC1_4BPP_UNORM_BLOCK_IMG: + case DATA_FORMAT_PVRTC2_4BPP_UNORM_BLOCK_IMG: + case DATA_FORMAT_PVRTC1_4BPP_SRGB_BLOCK_IMG: + case DATA_FORMAT_PVRTC2_4BPP_SRGB_BLOCK_IMG: return 1; + case DATA_FORMAT_PVRTC1_2BPP_UNORM_BLOCK_IMG: //these formats are quarter byte size, so rshift is 1 + case DATA_FORMAT_PVRTC2_2BPP_UNORM_BLOCK_IMG: + case DATA_FORMAT_PVRTC1_2BPP_SRGB_BLOCK_IMG: + case DATA_FORMAT_PVRTC2_2BPP_SRGB_BLOCK_IMG: return 2; + default: { + } + } + + return 0; +} + +bool RenderingDeviceVulkan::format_has_stencil(DataFormat p_format) { + switch (p_format) { + case DATA_FORMAT_S8_UINT: + case DATA_FORMAT_D16_UNORM_S8_UINT: + case DATA_FORMAT_D24_UNORM_S8_UINT: + case DATA_FORMAT_D32_SFLOAT_S8_UINT: { + return true; + } + default: { + } + } + return false; +} + +uint32_t RenderingDeviceVulkan::get_image_format_required_size(DataFormat p_format, uint32_t p_width, uint32_t p_height, uint32_t p_depth, uint32_t p_mipmaps, uint32_t *r_blockw, uint32_t *r_blockh, uint32_t *r_depth) { + + ERR_FAIL_COND_V(p_mipmaps == 0, 0); + uint32_t w = p_width; + uint32_t h = p_height; + uint32_t d = p_depth; + + uint32_t size = 0; + + uint32_t pixel_size = get_image_format_pixel_size(p_format); + uint32_t pixel_rshift = get_compressed_image_format_pixel_rshift(p_format); + uint32_t blockw, blockh; + get_compressed_image_format_block_dimensions(p_format, blockw, blockh); + + for (uint32_t i = 0; i < p_mipmaps; i++) { + uint32_t bw = w % blockw != 0 ? w + (blockw - w % blockw) : w; + uint32_t bh = h % blockh != 0 ? h + (blockh - h % blockh) : h; + + uint32_t s = bw * bh; + + s *= pixel_size; + s >>= pixel_rshift; + size += s * d; + if (r_blockw) { + *r_blockw = bw; + } + if (r_blockh) { + *r_blockh = bh; + } + if (r_depth) { + *r_depth = d; + } + w = MAX(blockw, w >> 1); + h = MAX(blockh, h >> 1); + d = MAX(1, d >> 1); + } + + return size; +} + +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) + int w = p_width; + int h = p_height; + int d = p_depth; + + int mipmaps = 1; + + while (true) { + + if (w == 1 && h == 1 && d == 1) { + break; + } + + w = MAX(1, w >> 1); + h = MAX(1, h >> 1); + d = MAX(1, d >> 1); + + mipmaps++; + }; + + return mipmaps; +} + +/////////////////////// + +const VkCompareOp RenderingDeviceVulkan::compare_operators[RenderingDevice::COMPARE_OP_MAX] = { + VK_COMPARE_OP_NEVER, + VK_COMPARE_OP_LESS, + VK_COMPARE_OP_EQUAL, + VK_COMPARE_OP_LESS_OR_EQUAL, + VK_COMPARE_OP_GREATER, + VK_COMPARE_OP_NOT_EQUAL, + VK_COMPARE_OP_GREATER_OR_EQUAL, + VK_COMPARE_OP_ALWAYS +}; + +const VkStencilOp RenderingDeviceVulkan::stencil_operations[RenderingDevice::STENCIL_OP_MAX] = { + VK_STENCIL_OP_KEEP, + VK_STENCIL_OP_ZERO, + VK_STENCIL_OP_REPLACE, + VK_STENCIL_OP_INCREMENT_AND_CLAMP, + VK_STENCIL_OP_DECREMENT_AND_CLAMP, + VK_STENCIL_OP_INVERT, + VK_STENCIL_OP_INCREMENT_AND_WRAP, + VK_STENCIL_OP_DECREMENT_AND_WRAP +}; + +const VkSampleCountFlagBits RenderingDeviceVulkan::rasterization_sample_count[RenderingDevice::TEXTURE_SAMPLES_MAX] = { + VK_SAMPLE_COUNT_1_BIT, + VK_SAMPLE_COUNT_2_BIT, + VK_SAMPLE_COUNT_4_BIT, + VK_SAMPLE_COUNT_8_BIT, + VK_SAMPLE_COUNT_16_BIT, + VK_SAMPLE_COUNT_32_BIT, + VK_SAMPLE_COUNT_64_BIT, +}; + +const VkLogicOp RenderingDeviceVulkan::logic_operations[RenderingDevice::LOGIC_OP_MAX] = { + VK_LOGIC_OP_CLEAR, + VK_LOGIC_OP_AND, + VK_LOGIC_OP_AND_REVERSE, + VK_LOGIC_OP_COPY, + VK_LOGIC_OP_AND_INVERTED, + VK_LOGIC_OP_NO_OP, + VK_LOGIC_OP_XOR, + VK_LOGIC_OP_OR, + VK_LOGIC_OP_NOR, + VK_LOGIC_OP_EQUIVALENT, + VK_LOGIC_OP_INVERT, + VK_LOGIC_OP_OR_REVERSE, + VK_LOGIC_OP_COPY_INVERTED, + VK_LOGIC_OP_OR_INVERTED, + VK_LOGIC_OP_NAND, + VK_LOGIC_OP_SET +}; + +const VkBlendFactor RenderingDeviceVulkan::blend_factors[RenderingDevice::BLEND_FACTOR_MAX] = { + VK_BLEND_FACTOR_ZERO, + VK_BLEND_FACTOR_ONE, + VK_BLEND_FACTOR_SRC_COLOR, + VK_BLEND_FACTOR_ONE_MINUS_SRC_COLOR, + VK_BLEND_FACTOR_DST_COLOR, + VK_BLEND_FACTOR_ONE_MINUS_DST_COLOR, + VK_BLEND_FACTOR_SRC_ALPHA, + VK_BLEND_FACTOR_ONE_MINUS_SRC_ALPHA, + VK_BLEND_FACTOR_DST_ALPHA, + VK_BLEND_FACTOR_ONE_MINUS_DST_ALPHA, + VK_BLEND_FACTOR_CONSTANT_COLOR, + VK_BLEND_FACTOR_ONE_MINUS_CONSTANT_COLOR, + VK_BLEND_FACTOR_CONSTANT_ALPHA, + VK_BLEND_FACTOR_ONE_MINUS_CONSTANT_ALPHA, + VK_BLEND_FACTOR_SRC_ALPHA_SATURATE, + VK_BLEND_FACTOR_SRC1_COLOR, + VK_BLEND_FACTOR_ONE_MINUS_SRC1_COLOR, + VK_BLEND_FACTOR_SRC1_ALPHA, + VK_BLEND_FACTOR_ONE_MINUS_SRC1_ALPHA +}; +const VkBlendOp RenderingDeviceVulkan::blend_operations[RenderingDevice::BLEND_OP_MAX] = { + VK_BLEND_OP_ADD, + VK_BLEND_OP_SUBTRACT, + VK_BLEND_OP_REVERSE_SUBTRACT, + VK_BLEND_OP_MIN, + VK_BLEND_OP_MAX +}; + +const VkSamplerAddressMode RenderingDeviceVulkan::address_modes[RenderingDevice::SAMPLER_REPEAT_MODE_MAX] = { + VK_SAMPLER_ADDRESS_MODE_REPEAT, + VK_SAMPLER_ADDRESS_MODE_MIRRORED_REPEAT, + VK_SAMPLER_ADDRESS_MODE_CLAMP_TO_EDGE, + VK_SAMPLER_ADDRESS_MODE_CLAMP_TO_BORDER, + VK_SAMPLER_ADDRESS_MODE_MIRROR_CLAMP_TO_EDGE +}; + +const VkBorderColor RenderingDeviceVulkan::sampler_border_colors[RenderingDevice::SAMPLER_BORDER_COLOR_MAX] = { + VK_BORDER_COLOR_FLOAT_TRANSPARENT_BLACK, + VK_BORDER_COLOR_INT_TRANSPARENT_BLACK, + VK_BORDER_COLOR_FLOAT_OPAQUE_BLACK, + VK_BORDER_COLOR_INT_OPAQUE_BLACK, + VK_BORDER_COLOR_FLOAT_OPAQUE_WHITE, + VK_BORDER_COLOR_INT_OPAQUE_WHITE +}; + +const VkImageType RenderingDeviceVulkan::vulkan_image_type[RenderingDevice::TEXTURE_TYPE_MAX] = { + VK_IMAGE_TYPE_1D, + VK_IMAGE_TYPE_2D, + VK_IMAGE_TYPE_3D, + VK_IMAGE_TYPE_2D, + VK_IMAGE_TYPE_1D, + VK_IMAGE_TYPE_2D, + VK_IMAGE_TYPE_2D +}; + +/***************************/ +/**** BUFFER MANAGEMENT ****/ +/***************************/ + +Error RenderingDeviceVulkan::_buffer_allocate(Buffer *p_buffer, uint32_t p_size, uint32_t p_usage, VmaMemoryUsage p_mapping) { + VkBufferCreateInfo bufferInfo; + bufferInfo.sType = VK_STRUCTURE_TYPE_BUFFER_CREATE_INFO; + bufferInfo.pNext = NULL; + bufferInfo.flags = 0; + bufferInfo.size = p_size; + bufferInfo.usage = p_usage; + bufferInfo.sharingMode = VK_SHARING_MODE_EXCLUSIVE; + bufferInfo.queueFamilyIndexCount = 0; + bufferInfo.pQueueFamilyIndices = 0; + + VmaAllocationCreateInfo allocInfo; + allocInfo.flags = 0; + allocInfo.usage = p_mapping; + allocInfo.requiredFlags = 0; + allocInfo.preferredFlags = 0; + allocInfo.memoryTypeBits = 0; + allocInfo.pool = NULL; + allocInfo.pUserData = NULL; + + VkResult err = vmaCreateBuffer(allocator, &bufferInfo, &allocInfo, &p_buffer->buffer, &p_buffer->allocation, NULL); + ERR_FAIL_COND_V_MSG(err, ERR_CANT_CREATE, "Can't create buffer of size: " + itos(p_size)); + p_buffer->size = p_size; + p_buffer->buffer_info.buffer = p_buffer->buffer; + p_buffer->buffer_info.offset = 0; + p_buffer->buffer_info.range = p_size; + + return OK; +} + +Error RenderingDeviceVulkan::_buffer_free(Buffer *p_buffer) { + ERR_FAIL_COND_V(p_buffer->size == 0, ERR_INVALID_PARAMETER); + + vmaDestroyBuffer(allocator, p_buffer->buffer, p_buffer->allocation); + p_buffer->buffer = NULL; + p_buffer->allocation = NULL; + p_buffer->size = 0; + + return OK; +} + +Error RenderingDeviceVulkan::_insert_staging_block() { + + VkBufferCreateInfo bufferInfo; + bufferInfo.sType = VK_STRUCTURE_TYPE_BUFFER_CREATE_INFO; + bufferInfo.pNext = NULL; + bufferInfo.flags = 0; + bufferInfo.size = staging_buffer_block_size; + bufferInfo.usage = VK_BUFFER_USAGE_TRANSFER_SRC_BIT; + bufferInfo.sharingMode = VK_SHARING_MODE_EXCLUSIVE; + bufferInfo.queueFamilyIndexCount = 0; + bufferInfo.pQueueFamilyIndices = 0; + + VmaAllocationCreateInfo allocInfo; + allocInfo.flags = 0; + allocInfo.usage = VMA_MEMORY_USAGE_CPU_ONLY; + allocInfo.requiredFlags = 0; + allocInfo.preferredFlags = 0; + allocInfo.memoryTypeBits = 0; + allocInfo.pool = NULL; + allocInfo.pUserData = NULL; + + StagingBufferBlock block; + + VkResult err = vmaCreateBuffer(allocator, &bufferInfo, &allocInfo, &block.buffer, &block.allocation, NULL); + ERR_FAIL_COND_V(err, ERR_CANT_CREATE); + + block.frame_used = 0; + block.fill_amount = 0; + + staging_buffer_blocks.insert(staging_buffer_current, 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 + + r_alloc_size = p_amount; + + while (true) { + + r_alloc_offset = 0; + + //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 + + uint32_t write_from = staging_buffer_blocks[staging_buffer_current].fill_amount; + + { + uint32_t align_remainder = write_from % p_required_align; + if (align_remainder != 0) { + write_from += p_required_align - align_remainder; + } + } + + 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 + 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 + 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 + + 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 + if (staging_buffer_blocks[staging_buffer_current].frame_used == frames_drawn) { + //guess we did.. ok, let's see if we can insert a new block.. + if (staging_buffer_blocks.size() * staging_buffer_block_size < staging_buffer_max_size) { + //we can, so we are safe + Error err = _insert_staging_block(); + if (err) { + return err; + } + //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 (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(true); + + //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 + staging_buffer_blocks.write[staging_buffer_current].frame_used = frames_drawn; + } + } + + } else { + //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 + staging_buffer_blocks.write[staging_buffer_current].frame_used = frames_drawn; + staging_buffer_blocks.write[staging_buffer_current].fill_amount = 0; + } else if (staging_buffer_blocks[staging_buffer_current].frame_used > frames_drawn - frame_count) { + //this block may still be in use, let's not touch it unless we have to, so.. can we create a new one? + if (staging_buffer_blocks.size() * staging_buffer_block_size < staging_buffer_max_size) { + //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 + 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. + // 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 + //block_until_next_frame() + 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 + 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 + } + + staging_buffer_blocks.write[block_idx].frame_used = 0; + staging_buffer_blocks.write[block_idx].fill_amount = 0; + } + + //claim for current frame + staging_buffer_blocks.write[staging_buffer_current].frame_used = frames_drawn; + } + } + } + + //all was good, break + break; + } + + staging_buffer_used = true; + + return OK; +} + +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 + size_t to_submit = p_data_size; + size_t submit_from = 0; + + while (to_submit > 0) { + + 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); + if (err) { + return err; + } + + //map staging buffer (It's CPU and coherent) + + void *data_ptr = NULL; + { + VkResult vkerr = vmaMapMemory(allocator, staging_buffer_blocks[staging_buffer_current].allocation, &data_ptr); + if (vkerr) { + ERR_FAIL_V(ERR_CANT_CREATE); + } + } + + //copy to staging buffer + copymem(((uint8_t *)data_ptr) + block_write_offset, p_data + submit_from, block_write_amount); + + //unmap + vmaUnmapMemory(allocator, staging_buffer_blocks[staging_buffer_current].allocation); + //insert a command to copy this + + VkBufferCopy region; + region.srcOffset = block_write_offset; + region.dstOffset = submit_from + p_offset; + region.size = block_write_amount; + + vkCmdCopyBuffer(p_use_draw_command_buffer ? frames[frame].draw_command_buffer : frames[frame].setup_command_buffer, staging_buffer_blocks[staging_buffer_current].buffer, p_buffer->buffer, 1, ®ion); + + staging_buffer_blocks.write[staging_buffer_current].fill_amount = block_write_offset + block_write_amount; + + to_submit -= block_write_amount; + submit_from += block_write_amount; + } + + return OK; +} + +void RenderingDeviceVulkan::_memory_barrier(VkPipelineStageFlags p_src_stage_mask, VkPipelineStageFlags p_dst_stage_mask, VkAccessFlags p_src_access, VkAccessFlags p_dst_sccess, bool p_sync_with_draw) { + + VkMemoryBarrier mem_barrier; + mem_barrier.sType = VK_STRUCTURE_TYPE_MEMORY_BARRIER; + mem_barrier.pNext = NULL; + mem_barrier.srcAccessMask = p_src_access; + mem_barrier.dstAccessMask = p_dst_sccess; + + 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, NULL, 0, NULL); +} + +void RenderingDeviceVulkan::_full_barrier(bool p_sync_with_draw) { + //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 | + VK_ACCESS_VERTEX_ATTRIBUTE_READ_BIT | + VK_ACCESS_UNIFORM_READ_BIT | + VK_ACCESS_INPUT_ATTACHMENT_READ_BIT | + VK_ACCESS_SHADER_READ_BIT | + VK_ACCESS_SHADER_WRITE_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 | + VK_ACCESS_TRANSFER_READ_BIT | + VK_ACCESS_TRANSFER_WRITE_BIT | + VK_ACCESS_HOST_READ_BIT | + VK_ACCESS_HOST_WRITE_BIT, + VK_ACCESS_INDIRECT_COMMAND_READ_BIT | + VK_ACCESS_INDEX_READ_BIT | + VK_ACCESS_VERTEX_ATTRIBUTE_READ_BIT | + VK_ACCESS_UNIFORM_READ_BIT | + VK_ACCESS_INPUT_ATTACHMENT_READ_BIT | + VK_ACCESS_SHADER_READ_BIT | + VK_ACCESS_SHADER_WRITE_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 | + VK_ACCESS_TRANSFER_READ_BIT | + VK_ACCESS_TRANSFER_WRITE_BIT | + VK_ACCESS_HOST_READ_BIT | + VK_ACCESS_HOST_WRITE_BIT, + p_sync_with_draw); +} + +void RenderingDeviceVulkan::_buffer_memory_barrier(VkBuffer buffer, uint64_t p_from, uint64_t p_size, VkPipelineStageFlags p_src_stage_mask, VkPipelineStageFlags p_dst_stage_mask, VkAccessFlags p_src_access, VkAccessFlags p_dst_sccess, bool p_sync_with_draw) { + + VkBufferMemoryBarrier buffer_mem_barrier; + buffer_mem_barrier.sType = VK_STRUCTURE_TYPE_BUFFER_MEMORY_BARRIER; + buffer_mem_barrier.pNext = NULL; + buffer_mem_barrier.srcQueueFamilyIndex = VK_QUEUE_FAMILY_IGNORED; + buffer_mem_barrier.dstQueueFamilyIndex = VK_QUEUE_FAMILY_IGNORED; + buffer_mem_barrier.srcAccessMask = p_src_access; + buffer_mem_barrier.dstAccessMask = p_dst_sccess; + buffer_mem_barrier.buffer = buffer; + buffer_mem_barrier.offset = p_from; + buffer_mem_barrier.size = p_size; + + vkCmdPipelineBarrier(p_sync_with_draw ? frames[frame].draw_command_buffer : frames[frame].setup_command_buffer, p_src_stage_mask, p_dst_stage_mask, 0, 0, NULL, 1, &buffer_mem_barrier, 0, NULL); +} + +/*****************/ +/**** TEXTURE ****/ +/*****************/ + +RID RenderingDeviceVulkan::texture_create(const TextureFormat &p_format, const TextureView &p_view, const Vector<PoolVector<uint8_t> > &p_data) { + + _THREAD_SAFE_METHOD_ + + VkImageCreateInfo image_create_info; + image_create_info.sType = VK_STRUCTURE_TYPE_IMAGE_CREATE_INFO; + image_create_info.pNext = NULL; + image_create_info.flags = 0; + + VkImageFormatListCreateInfoKHR format_list_create_info; + Vector<VkFormat> allowed_formats; + + if (p_format.shareable_formats.size()) { + image_create_info.flags |= VK_IMAGE_CREATE_MUTABLE_FORMAT_BIT; + for (int i = 0; i < p_format.shareable_formats.size(); i++) { + allowed_formats.push_back(vulkan_formats[p_format.shareable_formats[i]]); + } + + format_list_create_info.sType = VK_STRUCTURE_TYPE_IMAGE_FORMAT_LIST_CREATE_INFO_KHR; + format_list_create_info.pNext = NULL; + format_list_create_info.viewFormatCount = allowed_formats.size(); + format_list_create_info.pViewFormats = allowed_formats.ptr(); + image_create_info.pNext = &format_list_create_info; + + ERR_FAIL_COND_V_MSG(p_format.shareable_formats.find(p_format.format) == -1, RID(), + "If supplied a list of shareable formats, the current format must be present in the list"); + ERR_FAIL_COND_V_MSG(p_view.format_override != DATA_FORMAT_MAX && p_format.shareable_formats.find(p_view.format_override) == -1, RID(), + "If supplied a list of shareable formats, the current view format override must be present in the list"); + } + if (p_format.type == TEXTURE_TYPE_CUBE || p_format.type == TEXTURE_TYPE_CUBE_ARRAY) { + image_create_info.flags |= VK_IMAGE_CREATE_CUBE_COMPATIBLE_BIT; + } + /*if (p_format.type == TEXTURE_TYPE_2D || p_format.type == TEXTURE_TYPE_2D_ARRAY) { + image_create_info.flags |= VK_IMAGE_CREATE_2D_ARRAY_COMPATIBLE_BIT; + }*/ + + ERR_FAIL_INDEX_V(p_format.type, TEXTURE_TYPE_MAX, RID()); + + image_create_info.imageType = vulkan_image_type[p_format.type]; + + ERR_FAIL_COND_V_MSG(p_format.width < 1, RID(), "Width must be equal or greater than 1 for all textures"); + + image_create_info.format = vulkan_formats[p_format.format]; + + image_create_info.extent.width = p_format.width; + if (image_create_info.imageType == VK_IMAGE_TYPE_3D || image_create_info.imageType == VK_IMAGE_TYPE_2D) { + ERR_FAIL_COND_V_MSG(p_format.height < 1, RID(), "Height must be equal or greater than 1 for 2D and 3D textures"); + image_create_info.extent.height = p_format.height; + } else { + image_create_info.extent.height = 1; + } + + if (image_create_info.imageType == VK_IMAGE_TYPE_3D) { + ERR_FAIL_COND_V_MSG(p_format.depth < 1, RID(), "Depth must be equal or greater than 1 for 3D textures"); + image_create_info.extent.depth = p_format.depth; + } else { + image_create_info.extent.depth = 1; + } + + ERR_FAIL_COND_V(p_format.mipmaps < 1, RID()); + + image_create_info.mipLevels = p_format.mipmaps; + + if (p_format.type == TEXTURE_TYPE_1D_ARRAY || p_format.type == TEXTURE_TYPE_2D_ARRAY || p_format.type == TEXTURE_TYPE_CUBE_ARRAY || p_format.type == TEXTURE_TYPE_CUBE) { + ERR_FAIL_COND_V_MSG(p_format.array_layers < 1, RID(), + "Amount of layers must be equal or greater than 1 for arrays and cubemaps."); + ERR_FAIL_COND_V_MSG((p_format.type == TEXTURE_TYPE_CUBE_ARRAY || p_format.type == TEXTURE_TYPE_CUBE) && (p_format.array_layers % 6) != 0, RID(), + "Cubemap and cubemap array textures must provide a layer number that is multiple of 6"); + image_create_info.arrayLayers = p_format.array_layers; + } else { + image_create_info.arrayLayers = 1; + } + + ERR_FAIL_INDEX_V(p_format.samples, TEXTURE_SAMPLES_MAX, RID()); + + 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 + image_create_info.usage = 0; + + if (p_format.usage_bits & TEXTURE_USAGE_SAMPLING_BIT) { + image_create_info.usage |= VK_IMAGE_USAGE_SAMPLED_BIT; + } + + if (p_format.usage_bits & TEXTURE_USAGE_STORAGE_BIT) { + image_create_info.usage |= VK_IMAGE_USAGE_STORAGE_BIT; + } + + if (p_format.usage_bits & TEXTURE_USAGE_COLOR_ATTACHMENT_BIT) { + image_create_info.usage |= VK_IMAGE_USAGE_COLOR_ATTACHMENT_BIT; + } + + if (p_format.usage_bits & TEXTURE_USAGE_DEPTH_STENCIL_ATTACHMENT_BIT) { + image_create_info.usage |= VK_IMAGE_USAGE_DEPTH_STENCIL_ATTACHMENT_BIT; + } + + if (p_format.usage_bits & TEXTURE_USAGE_CAN_UPDATE_BIT) { + image_create_info.usage |= VK_IMAGE_USAGE_TRANSFER_DST_BIT; + } + if (p_format.usage_bits & TEXTURE_USAGE_CAN_COPY_FROM_BIT) { + image_create_info.usage |= VK_IMAGE_USAGE_TRANSFER_SRC_BIT; + } + + if (p_format.usage_bits & TEXTURE_USAGE_CAN_COPY_TO_BIT) { + image_create_info.usage |= VK_IMAGE_USAGE_TRANSFER_DST_BIT; + } + + image_create_info.sharingMode = VK_SHARING_MODE_EXCLUSIVE; + image_create_info.queueFamilyIndexCount = 0; + image_create_info.pQueueFamilyIndices = NULL; + image_create_info.initialLayout = VK_IMAGE_LAYOUT_UNDEFINED; + + uint32_t required_mipmaps = get_image_required_mipmaps(image_create_info.extent.width, image_create_info.extent.height, image_create_info.extent.depth); + + ERR_FAIL_COND_V_MSG(required_mipmaps < image_create_info.mipLevels, RID(), + "Too many mipmaps requested for texture format and dimensions (" + itos(image_create_info.mipLevels) + "), maximum allowed: (" + itos(required_mipmaps) + ")."); + + if (p_data.size()) { + + ERR_FAIL_COND_V_MSG(!(p_format.usage_bits & TEXTURE_USAGE_CAN_UPDATE_BIT), RID(), + "Texture needs the TEXTURE_USAGE_CAN_UPDATE_BIT usage flag in order to be updated at initialization or later"); + + int expected_images = image_create_info.arrayLayers; + ERR_FAIL_COND_V_MSG(p_data.size() != expected_images, RID(), + "Default supplied data for image format is of invalid length (" + itos(p_data.size()) + "), should be (" + itos(expected_images) + ")."); + + for (uint32_t i = 0; i < image_create_info.arrayLayers; i++) { + uint32_t required_size = get_image_format_required_size(p_format.format, image_create_info.extent.width, image_create_info.extent.height, image_create_info.extent.depth, image_create_info.mipLevels); + ERR_FAIL_COND_V_MSG((uint32_t)p_data[i].size() != required_size, RID(), + "Data for slice index " + itos(i) + " (mapped to layer " + itos(i) + ") differs in size (supplied: " + itos(p_data[i].size()) + ") than what is required by the format (" + itos(required_size) + ")."); + } + } + + { + //validate that this image is supported for the intended use + VkFormatProperties properties; + vkGetPhysicalDeviceFormatProperties(context->get_physical_device(), image_create_info.format, &properties); + VkFormatFeatureFlags flags; + + String format_text = "'" + String(named_formats[p_format.format]) + "'"; + + if (p_format.usage_bits & TEXTURE_USAGE_CPU_READ_BIT) { + flags = properties.linearTilingFeatures; + format_text += " (with CPU read bit)"; + } else { + flags = properties.optimalTilingFeatures; + } + + if (p_format.usage_bits & TEXTURE_USAGE_SAMPLING_BIT && !(flags & VK_FORMAT_FEATURE_SAMPLED_IMAGE_BIT)) { + ERR_FAIL_V_MSG(RID(), "Format " + format_text + " does not support usage as sampling texture."); + } + + if (p_format.usage_bits & TEXTURE_USAGE_COLOR_ATTACHMENT_BIT && !(flags & VK_FORMAT_FEATURE_COLOR_ATTACHMENT_BIT)) { + ERR_FAIL_V_MSG(RID(), "Format " + format_text + " does not support usage as color attachment."); + } + + if (p_format.usage_bits & TEXTURE_USAGE_DEPTH_STENCIL_ATTACHMENT_BIT && !(flags & VK_FORMAT_FEATURE_DEPTH_STENCIL_ATTACHMENT_BIT)) { + printf("vkformat: %x\n", image_create_info.format); + ERR_FAIL_V_MSG(RID(), "Format " + format_text + " does not support usage as depth-stencil attachment."); + } + + if (p_format.usage_bits & TEXTURE_USAGE_STORAGE_BIT && !(flags & VK_FORMAT_FEATURE_STORAGE_IMAGE_BIT)) { + ERR_FAIL_V_MSG(RID(), "Format " + format_text + " does not support usage as storage image."); + } + + 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."); + } + } + + //some view validation + + if (p_view.format_override != DATA_FORMAT_MAX) { + ERR_FAIL_INDEX_V(p_view.format_override, DATA_FORMAT_MAX, RID()); + } + ERR_FAIL_INDEX_V(p_view.swizzle_r, TEXTURE_SWIZZLE_MAX, RID()); + ERR_FAIL_INDEX_V(p_view.swizzle_g, TEXTURE_SWIZZLE_MAX, RID()); + 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 + + VmaAllocationCreateInfo allocInfo; + allocInfo.flags = 0; + allocInfo.usage = p_format.usage_bits & TEXTURE_USAGE_CPU_READ_BIT ? VMA_MEMORY_USAGE_CPU_ONLY : VMA_MEMORY_USAGE_GPU_ONLY; + allocInfo.requiredFlags = 0; + allocInfo.preferredFlags = 0; + allocInfo.memoryTypeBits = 0; + allocInfo.pool = NULL; + allocInfo.pUserData = NULL; + + Texture texture; + + VkResult err = vmaCreateImage(allocator, &image_create_info, &allocInfo, &texture.image, &texture.allocation, &texture.allocation_info); + ERR_FAIL_COND_V(err, RID()); + + texture.type = p_format.type; + texture.format = p_format.format; + texture.width = image_create_info.extent.width; + texture.height = image_create_info.extent.height; + texture.depth = image_create_info.extent.depth; + texture.layers = image_create_info.arrayLayers; + texture.mipmaps = image_create_info.mipLevels; + texture.usage_flags = p_format.usage_bits; + texture.samples = p_format.samples; + texture.allowed_shared_formats = p_format.shareable_formats; + + //set base layout based on usage priority + + if (p_format.usage_bits & TEXTURE_USAGE_SAMPLING_BIT) { + //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 + + texture.layout = VK_IMAGE_LAYOUT_GENERAL; + + } else if (p_format.usage_bits & TEXTURE_USAGE_COLOR_ATTACHMENT_BIT) { + //third priority, color or depth + + texture.layout = VK_IMAGE_LAYOUT_COLOR_ATTACHMENT_OPTIMAL; + + } else if (p_format.usage_bits & TEXTURE_USAGE_DEPTH_STENCIL_ATTACHMENT_BIT) { + + texture.layout = VK_IMAGE_LAYOUT_DEPTH_STENCIL_ATTACHMENT_OPTIMAL; + + } else { + texture.layout = VK_IMAGE_LAYOUT_GENERAL; + } + + if (p_format.usage_bits & TEXTURE_USAGE_DEPTH_STENCIL_ATTACHMENT_BIT) { + + texture.read_aspect_mask = VK_IMAGE_ASPECT_DEPTH_BIT; + texture.barrier_aspect_mask = VK_IMAGE_ASPECT_DEPTH_BIT; + + if (format_has_stencil(p_format.format)) { + texture.barrier_aspect_mask |= VK_IMAGE_ASPECT_STENCIL_BIT; + } + } else { + texture.read_aspect_mask = VK_IMAGE_ASPECT_COLOR_BIT; + texture.barrier_aspect_mask = VK_IMAGE_ASPECT_COLOR_BIT; + } + + texture.bound = false; + + //create view + + VkImageViewCreateInfo image_view_create_info; + image_view_create_info.sType = VK_STRUCTURE_TYPE_IMAGE_VIEW_CREATE_INFO; + image_view_create_info.pNext = NULL; + image_view_create_info.flags = 0; + image_view_create_info.image = texture.image; + + static const VkImageViewType view_types[TEXTURE_TYPE_MAX] = { + VK_IMAGE_VIEW_TYPE_1D, + VK_IMAGE_VIEW_TYPE_2D, + VK_IMAGE_VIEW_TYPE_3D, + VK_IMAGE_VIEW_TYPE_CUBE, + VK_IMAGE_VIEW_TYPE_1D_ARRAY, + VK_IMAGE_VIEW_TYPE_2D_ARRAY, + VK_IMAGE_VIEW_TYPE_CUBE_ARRAY, + }; + + image_view_create_info.viewType = view_types[p_format.type]; + if (p_view.format_override == DATA_FORMAT_MAX) { + image_view_create_info.format = image_create_info.format; + } else { + image_view_create_info.format = vulkan_formats[p_view.format_override]; + } + + static const VkComponentSwizzle component_swizzles[TEXTURE_SWIZZLE_MAX] = { + VK_COMPONENT_SWIZZLE_IDENTITY, + VK_COMPONENT_SWIZZLE_ZERO, + VK_COMPONENT_SWIZZLE_ONE, + VK_COMPONENT_SWIZZLE_R, + VK_COMPONENT_SWIZZLE_G, + VK_COMPONENT_SWIZZLE_B, + VK_COMPONENT_SWIZZLE_A + }; + + image_view_create_info.components.r = component_swizzles[p_view.swizzle_r]; + image_view_create_info.components.g = component_swizzles[p_view.swizzle_g]; + image_view_create_info.components.b = component_swizzles[p_view.swizzle_b]; + image_view_create_info.components.a = component_swizzles[p_view.swizzle_a]; + + image_view_create_info.subresourceRange.baseMipLevel = 0; + image_view_create_info.subresourceRange.levelCount = image_create_info.mipLevels; + image_view_create_info.subresourceRange.baseArrayLayer = 0; + image_view_create_info.subresourceRange.layerCount = image_create_info.arrayLayers; + if (p_format.usage_bits & TEXTURE_USAGE_DEPTH_STENCIL_ATTACHMENT_BIT) { + image_view_create_info.subresourceRange.aspectMask = VK_IMAGE_ASPECT_DEPTH_BIT; + } else { + image_view_create_info.subresourceRange.aspectMask = VK_IMAGE_ASPECT_COLOR_BIT; + } + + err = vkCreateImageView(device, &image_view_create_info, NULL, &texture.view); + + if (err) { + vmaDestroyImage(allocator, texture.image, texture.allocation); + ERR_FAIL_V(RID()); + } + + //barrier to set layout + { + VkImageMemoryBarrier image_memory_barrier; + image_memory_barrier.sType = VK_STRUCTURE_TYPE_IMAGE_MEMORY_BARRIER; + image_memory_barrier.pNext = NULL; + image_memory_barrier.srcAccessMask = 0; + image_memory_barrier.dstAccessMask = VK_ACCESS_SHADER_READ_BIT; + image_memory_barrier.oldLayout = VK_IMAGE_LAYOUT_UNDEFINED; + image_memory_barrier.newLayout = texture.layout; + image_memory_barrier.srcQueueFamilyIndex = VK_QUEUE_FAMILY_IGNORED; + image_memory_barrier.dstQueueFamilyIndex = VK_QUEUE_FAMILY_IGNORED; + image_memory_barrier.image = texture.image; + image_memory_barrier.subresourceRange.aspectMask = texture.barrier_aspect_mask; + image_memory_barrier.subresourceRange.baseMipLevel = 0; + image_memory_barrier.subresourceRange.levelCount = image_create_info.mipLevels; + image_memory_barrier.subresourceRange.baseArrayLayer = 0; + image_memory_barrier.subresourceRange.layerCount = image_create_info.arrayLayers; + + vkCmdPipelineBarrier(frames[frame].setup_command_buffer, VK_PIPELINE_STAGE_TOP_OF_PIPE_BIT, VK_PIPELINE_STAGE_VERTEX_SHADER_BIT | VK_PIPELINE_STAGE_COMPUTE_SHADER_BIT, 0, 0, NULL, 0, NULL, 1, &image_memory_barrier); + } + + RID id = texture_owner.make_rid(texture); + + if (p_data.size()) { + + for (uint32_t i = 0; i < image_create_info.arrayLayers; i++) { + texture_update(id, i, p_data[i]); + } + } + return id; +} + +RID RenderingDeviceVulkan::texture_create_shared(const TextureView &p_view, RID p_with_texture) { + + _THREAD_SAFE_METHOD_ + + Texture *src_texture = texture_owner.getornull(p_with_texture); + ERR_FAIL_COND_V(!src_texture, RID()); + + if (src_texture->owner.is_valid()) { //ahh this is a share + p_with_texture = src_texture->owner; + src_texture = texture_owner.getornull(src_texture->owner); + ERR_FAIL_COND_V(!src_texture, RID()); //this is a bug + } + + //create view + + Texture texture = *src_texture; + + VkImageViewCreateInfo image_view_create_info; + image_view_create_info.sType = VK_STRUCTURE_TYPE_IMAGE_VIEW_CREATE_INFO; + image_view_create_info.pNext = NULL; + image_view_create_info.flags = 0; + image_view_create_info.image = texture.image; + + static const VkImageViewType view_types[TEXTURE_TYPE_MAX] = { + VK_IMAGE_VIEW_TYPE_1D, + VK_IMAGE_VIEW_TYPE_2D, + VK_IMAGE_VIEW_TYPE_3D, + VK_IMAGE_VIEW_TYPE_CUBE, + VK_IMAGE_VIEW_TYPE_1D_ARRAY, + VK_IMAGE_VIEW_TYPE_2D_ARRAY, + VK_IMAGE_VIEW_TYPE_CUBE_ARRAY, + }; + + image_view_create_info.viewType = view_types[texture.type]; + if (p_view.format_override == DATA_FORMAT_MAX || p_view.format_override == texture.format) { + image_view_create_info.format = vulkan_formats[texture.format]; + } else { + ERR_FAIL_INDEX_V(p_view.format_override, DATA_FORMAT_MAX, RID()); + + ERR_FAIL_COND_V_MSG(texture.allowed_shared_formats.find(p_view.format_override) == -1, RID(), + "Format override is not in the list of allowed shareable formats for original texture."); + image_view_create_info.format = vulkan_formats[p_view.format_override]; + } + + static const VkComponentSwizzle component_swizzles[TEXTURE_SWIZZLE_MAX] = { + VK_COMPONENT_SWIZZLE_IDENTITY, + VK_COMPONENT_SWIZZLE_ZERO, + VK_COMPONENT_SWIZZLE_ONE, + VK_COMPONENT_SWIZZLE_R, + VK_COMPONENT_SWIZZLE_G, + VK_COMPONENT_SWIZZLE_B, + VK_COMPONENT_SWIZZLE_A + }; + + image_view_create_info.components.r = component_swizzles[p_view.swizzle_r]; + image_view_create_info.components.g = component_swizzles[p_view.swizzle_g]; + image_view_create_info.components.b = component_swizzles[p_view.swizzle_b]; + image_view_create_info.components.a = component_swizzles[p_view.swizzle_a]; + + image_view_create_info.subresourceRange.baseMipLevel = 0; + image_view_create_info.subresourceRange.levelCount = texture.mipmaps; + image_view_create_info.subresourceRange.layerCount = texture.layers; + image_view_create_info.subresourceRange.baseArrayLayer = 0; + + if (texture.usage_flags & TEXTURE_USAGE_DEPTH_STENCIL_ATTACHMENT_BIT) { + image_view_create_info.subresourceRange.aspectMask = VK_IMAGE_ASPECT_DEPTH_BIT; + } else { + image_view_create_info.subresourceRange.aspectMask = VK_IMAGE_ASPECT_COLOR_BIT; + } + + VkResult err = vkCreateImageView(device, &image_view_create_info, NULL, &texture.view); + + if (err) { + ERR_FAIL_V(RID()); + } + + texture.owner = p_with_texture; + RID id = texture_owner.make_rid(texture); + _add_dependency(id, p_with_texture); + + return id; +} + +RID RenderingDeviceVulkan::texture_create_shared_from_slice(const TextureView &p_view, RID p_with_texture, uint32_t p_layer, uint32_t p_mipmap, TextureSliceType p_slice_type) { + + _THREAD_SAFE_METHOD_ + + Texture *src_texture = texture_owner.getornull(p_with_texture); + ERR_FAIL_COND_V(!src_texture, RID()); + + if (src_texture->owner.is_valid()) { //ahh this is a share + p_with_texture = src_texture->owner; + src_texture = texture_owner.getornull(src_texture->owner); + 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(), + "Can only create a cubemap slice from a cubemap or cubemap array mipmap"); + + ERR_FAIL_COND_V_MSG(p_slice_type == TEXTURE_SLICE_3D && src_texture->type != TEXTURE_TYPE_3D, RID(), + "Can only create a 3D slice from a 3D texture"); + + //create view + + ERR_FAIL_INDEX_V(p_mipmap, src_texture->mipmaps, RID()); + ERR_FAIL_INDEX_V(p_layer, src_texture->layers, RID()); + + Texture texture = *src_texture; + get_image_format_required_size(texture.format, texture.width, texture.height, texture.depth, p_mipmap + 1, &texture.width, &texture.height); + texture.mipmaps = 1; + texture.layers = p_slice_type == TEXTURE_SLICE_CUBEMAP ? 6 : 1; + + VkImageViewCreateInfo image_view_create_info; + image_view_create_info.sType = VK_STRUCTURE_TYPE_IMAGE_VIEW_CREATE_INFO; + image_view_create_info.pNext = NULL; + image_view_create_info.flags = 0; + image_view_create_info.image = texture.image; + + static const VkImageViewType view_types[TEXTURE_TYPE_MAX] = { + VK_IMAGE_VIEW_TYPE_1D, + VK_IMAGE_VIEW_TYPE_2D, + VK_IMAGE_VIEW_TYPE_2D, + VK_IMAGE_VIEW_TYPE_2D, + VK_IMAGE_VIEW_TYPE_1D, + VK_IMAGE_VIEW_TYPE_2D, + VK_IMAGE_VIEW_TYPE_2D, + }; + + image_view_create_info.viewType = p_slice_type == TEXTURE_SLICE_CUBEMAP ? VK_IMAGE_VIEW_TYPE_CUBE : (p_slice_type == TEXTURE_SLICE_3D ? VK_IMAGE_VIEW_TYPE_3D : view_types[texture.type]); + if (p_view.format_override == DATA_FORMAT_MAX || p_view.format_override == texture.format) { + image_view_create_info.format = vulkan_formats[texture.format]; + } else { + ERR_FAIL_INDEX_V(p_view.format_override, DATA_FORMAT_MAX, RID()); + + ERR_FAIL_COND_V_MSG(texture.allowed_shared_formats.find(p_view.format_override) == -1, RID(), + "Format override is not in the list of allowed shareable formats for original texture."); + image_view_create_info.format = vulkan_formats[p_view.format_override]; + } + + static const VkComponentSwizzle component_swizzles[TEXTURE_SWIZZLE_MAX] = { + VK_COMPONENT_SWIZZLE_IDENTITY, + VK_COMPONENT_SWIZZLE_ZERO, + VK_COMPONENT_SWIZZLE_ONE, + VK_COMPONENT_SWIZZLE_R, + VK_COMPONENT_SWIZZLE_G, + VK_COMPONENT_SWIZZLE_B, + VK_COMPONENT_SWIZZLE_A + }; + + image_view_create_info.components.r = component_swizzles[p_view.swizzle_r]; + image_view_create_info.components.g = component_swizzles[p_view.swizzle_g]; + image_view_create_info.components.b = component_swizzles[p_view.swizzle_b]; + image_view_create_info.components.a = component_swizzles[p_view.swizzle_a]; + + if (p_slice_type == TEXTURE_SLICE_CUBEMAP) { + ERR_FAIL_COND_V_MSG(p_layer >= src_texture->layers, RID(), + "Specified layer is invalid for cubemap"); + ERR_FAIL_COND_V_MSG((p_layer % 6) != 0, RID(), + "Specified layer must be a multiple of 6."); + } + image_view_create_info.subresourceRange.baseMipLevel = p_mipmap; + image_view_create_info.subresourceRange.levelCount = 1; + image_view_create_info.subresourceRange.layerCount = p_slice_type == TEXTURE_SLICE_CUBEMAP ? 6 : 1; + image_view_create_info.subresourceRange.baseArrayLayer = p_layer; + + if (texture.usage_flags & TEXTURE_USAGE_DEPTH_STENCIL_ATTACHMENT_BIT) { + image_view_create_info.subresourceRange.aspectMask = VK_IMAGE_ASPECT_DEPTH_BIT; + } else { + image_view_create_info.subresourceRange.aspectMask = VK_IMAGE_ASPECT_COLOR_BIT; + } + + VkResult err = vkCreateImageView(device, &image_view_create_info, NULL, &texture.view); + + if (err) { + ERR_FAIL_V(RID()); + } + + texture.owner = p_with_texture; + RID id = texture_owner.make_rid(texture); + _add_dependency(id, p_with_texture); + + return id; +} + +Error RenderingDeviceVulkan::texture_update(RID p_texture, uint32_t p_layer, const PoolVector<uint8_t> &p_data, bool p_sync_with_draw) { + + _THREAD_SAFE_METHOD_ + + ERR_FAIL_COND_V_MSG(draw_list && p_sync_with_draw, ERR_INVALID_PARAMETER, + "Updating textures in 'sync to draw' mode is forbidden during creation of a draw list"); + + Texture *texture = texture_owner.getornull(p_texture); + ERR_FAIL_COND_V(!texture, ERR_INVALID_PARAMETER); + + if (texture->owner != RID()) { + p_texture = texture->owner; + texture = texture_owner.getornull(texture->owner); + ERR_FAIL_COND_V(!texture, ERR_BUG); //this is a bug + } + + ERR_FAIL_COND_V_MSG(texture->bound, ERR_CANT_ACQUIRE_RESOURCE, + "Texture can't be updated while a render pass that uses it is being created. Ensure render pass is finalized (and that it was created with RENDER_PASS_CONTENTS_FINISH) to unbind this texture."); + + ERR_FAIL_COND_V_MSG(!(texture->usage_flags & TEXTURE_USAGE_CAN_UPDATE_BIT), ERR_INVALID_PARAMETER, + "Texture requires the TEXTURE_USAGE_CAN_UPDATE_BIT in order to be updatable."); + + uint32_t layer_count = texture->layers; + if (texture->type == TEXTURE_TYPE_CUBE || texture->type == TEXTURE_TYPE_CUBE_ARRAY) { + layer_count *= 6; + } + ERR_FAIL_COND_V(p_layer >= layer_count, ERR_INVALID_PARAMETER); + + uint32_t width, height; + uint32_t image_size = get_image_format_required_size(texture->format, texture->width, texture->height, texture->depth, texture->mipmaps, &width, &height); + uint32_t required_size = image_size; + uint32_t required_align = get_compressed_image_format_block_byte_size(texture->format); + if (required_align == 1) { + required_align = get_image_format_pixel_size(texture->format); + } + if ((required_align % 4) != 0) { //alignment rules are really strange + required_align *= 4; + } + + ERR_FAIL_COND_V_MSG(required_size != (uint32_t)p_data.size(), ERR_INVALID_PARAMETER, + "Required size for texture update (" + itos(required_size) + ") does not match data supplied size (" + itos(p_data.size()) + ")."); + + uint32_t region_size = texture_upload_region_size_px; + + PoolVector<uint8_t>::Read r = p_data.read(); + + VkCommandBuffer command_buffer = p_sync_with_draw ? frames[frame].draw_command_buffer : frames[frame].setup_command_buffer; + + //barrier to transfer + { + VkImageMemoryBarrier image_memory_barrier; + image_memory_barrier.sType = VK_STRUCTURE_TYPE_IMAGE_MEMORY_BARRIER; + image_memory_barrier.pNext = NULL; + image_memory_barrier.srcAccessMask = 0; + image_memory_barrier.dstAccessMask = VK_ACCESS_TRANSFER_WRITE_BIT; + image_memory_barrier.oldLayout = texture->layout; + image_memory_barrier.newLayout = VK_IMAGE_LAYOUT_TRANSFER_DST_OPTIMAL; + + image_memory_barrier.srcQueueFamilyIndex = VK_QUEUE_FAMILY_IGNORED; + image_memory_barrier.dstQueueFamilyIndex = VK_QUEUE_FAMILY_IGNORED; + image_memory_barrier.image = texture->image; + image_memory_barrier.subresourceRange.aspectMask = texture->barrier_aspect_mask; + image_memory_barrier.subresourceRange.baseMipLevel = 0; + image_memory_barrier.subresourceRange.levelCount = texture->mipmaps; + image_memory_barrier.subresourceRange.baseArrayLayer = p_layer; + image_memory_barrier.subresourceRange.layerCount = 1; + + vkCmdPipelineBarrier(command_buffer, VK_PIPELINE_STAGE_TOP_OF_PIPE_BIT, VK_PIPELINE_STAGE_TRANSFER_BIT, 0, 0, NULL, 0, NULL, 1, &image_memory_barrier); + } + + uint32_t mipmap_offset = 0; + for (uint32_t mm_i = 0; mm_i < texture->mipmaps; mm_i++) { + + uint32_t depth; + uint32_t image_total = get_image_format_required_size(texture->format, texture->width, texture->height, texture->depth, mm_i + 1, &width, &height, &depth); + + const uint8_t *read_ptr_mipmap = r.ptr() + mipmap_offset; + image_size = image_total - mipmap_offset; + + 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) { + + uint32_t region_w = MIN(region_size, width - x); + uint32_t region_h = MIN(region_size, height - y); + + uint32_t pixel_size = get_image_format_pixel_size(texture->format); + uint32_t to_allocate = region_w * region_h * pixel_size; + 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_sync_with_draw); + ERR_FAIL_COND_V(err, ERR_CANT_CREATE); + + uint8_t *write_ptr; + + { //map + void *data_ptr = NULL; + VkResult vkerr = vmaMapMemory(allocator, staging_buffer_blocks[staging_buffer_current].allocation, &data_ptr); + if (vkerr) { + ERR_FAIL_V(ERR_CANT_CREATE); + } + write_ptr = (uint8_t *)data_ptr; + write_ptr += alloc_offset; + } + + uint32_t block_w, block_h; + get_compressed_image_format_block_dimensions(texture->format, block_w, block_h); + + ERR_FAIL_COND_V(region_w % block_w, ERR_BUG); + 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 + + uint32_t block_size = get_compressed_image_format_block_byte_size(texture->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]; + } + } + } + + } 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]; + } + } + } + } + + { //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; //tigthly packed + buffer_image_copy.bufferImageHeight = 0; //tigthly packed + + buffer_image_copy.imageSubresource.aspectMask = texture->read_aspect_mask; + buffer_image_copy.imageSubresource.mipLevel = mm_i; + buffer_image_copy.imageSubresource.baseArrayLayer = p_layer; + buffer_image_copy.imageSubresource.layerCount = 1; + + buffer_image_copy.imageOffset.x = x; + buffer_image_copy.imageOffset.y = y; + buffer_image_copy.imageOffset.z = z; + + buffer_image_copy.imageExtent.width = region_w; + buffer_image_copy.imageExtent.height = region_h; + buffer_image_copy.imageExtent.depth = 1; + + vkCmdCopyBufferToImage(command_buffer, staging_buffer_blocks[staging_buffer_current].buffer, texture->image, VK_IMAGE_LAYOUT_TRANSFER_DST_OPTIMAL, 1, &buffer_image_copy); + + staging_buffer_blocks.write[staging_buffer_current].fill_amount += alloc_size; + } + } + } + + mipmap_offset = image_total; + } + + //barrier to restore layout + { + VkImageMemoryBarrier image_memory_barrier; + image_memory_barrier.sType = VK_STRUCTURE_TYPE_IMAGE_MEMORY_BARRIER; + image_memory_barrier.pNext = NULL; + image_memory_barrier.srcAccessMask = VK_ACCESS_TRANSFER_WRITE_BIT; + image_memory_barrier.dstAccessMask = VK_ACCESS_SHADER_READ_BIT; + image_memory_barrier.oldLayout = VK_IMAGE_LAYOUT_TRANSFER_DST_OPTIMAL; + image_memory_barrier.newLayout = texture->layout; + image_memory_barrier.srcQueueFamilyIndex = VK_QUEUE_FAMILY_IGNORED; + image_memory_barrier.dstQueueFamilyIndex = VK_QUEUE_FAMILY_IGNORED; + image_memory_barrier.image = texture->image; + image_memory_barrier.subresourceRange.aspectMask = texture->barrier_aspect_mask; + image_memory_barrier.subresourceRange.baseMipLevel = 0; + image_memory_barrier.subresourceRange.levelCount = texture->mipmaps; + image_memory_barrier.subresourceRange.baseArrayLayer = p_layer; + image_memory_barrier.subresourceRange.layerCount = 1; + + vkCmdPipelineBarrier(command_buffer, VK_ACCESS_TRANSFER_WRITE_BIT, VK_PIPELINE_STAGE_VERTEX_SHADER_BIT | VK_PIPELINE_STAGE_COMPUTE_SHADER_BIT, 0, 0, NULL, 0, NULL, 1, &image_memory_barrier); + } + + return OK; +} + +PoolVector<uint8_t> RenderingDeviceVulkan::_texture_get_data_from_image(Texture *tex, VkImage p_image, VmaAllocation p_allocation, uint32_t p_layer, bool p_2d) { + + uint32_t width, height, depth; + uint32_t image_size = get_image_format_required_size(tex->format, tex->width, tex->height, p_2d ? 1 : tex->depth, tex->mipmaps, &width, &height, &depth); + + PoolVector<uint8_t> image_data; + image_data.resize(image_size); + + void *img_mem; + vmaMapMemory(allocator, p_allocation, &img_mem); + + uint32_t blockw, blockh; + get_compressed_image_format_block_dimensions(tex->format, blockw, blockh); + uint32_t block_size = get_compressed_image_format_block_byte_size(tex->format); + uint32_t pixel_size = get_image_format_pixel_size(tex->format); + + { + PoolVector<uint8_t>::Write w = image_data.write(); + + uint32_t mipmap_offset = 0; + for (uint32_t mm_i = 0; mm_i < tex->mipmaps; mm_i++) { + + uint32_t image_total = get_image_format_required_size(tex->format, tex->width, tex->height, p_2d ? 1 : tex->depth, mm_i + 1, &width, &height, &depth); + + uint8_t *write_ptr_mipmap = w.ptr() + mipmap_offset; + image_size = image_total - mipmap_offset; + + VkImageSubresource image_sub_resorce; + image_sub_resorce.aspectMask = VK_IMAGE_ASPECT_COLOR_BIT; + image_sub_resorce.arrayLayer = p_layer; + image_sub_resorce.mipLevel = mm_i; + VkSubresourceLayout layout; + vkGetImageSubresourceLayout(device, p_image, &image_sub_resorce, &layout); + + for (uint32_t z = 0; z < depth; z++) { + uint8_t *write_ptr = write_ptr_mipmap + z * image_size / depth; + const uint8_t *slice_read_ptr = ((uint8_t *)img_mem) + layout.offset + z * layout.depthPitch; + + if (block_size > 1) { + //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; + uint8_t *wptr = write_ptr + y * line_width; + + copymem(wptr, rptr, line_width); + } + + } else { + //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; + copymem(wptr, rptr, pixel_size * width); + } + } + } + + mipmap_offset = image_total; + } + } + + vmaUnmapMemory(allocator, p_allocation); + + return image_data; +} + +PoolVector<uint8_t> RenderingDeviceVulkan::texture_get_data(RID p_texture, uint32_t p_layer) { + + _THREAD_SAFE_METHOD_ + + Texture *tex = texture_owner.getornull(p_texture); + ERR_FAIL_COND_V(!tex, PoolVector<uint8_t>()); + + ERR_FAIL_COND_V_MSG(tex->bound, PoolVector<uint8_t>(), + "Texture can't be retrieved while a render pass that uses it is being created. Ensure render pass is finalized (and that it was created with RENDER_PASS_CONTENTS_FINISH) to unbind this texture."); + ERR_FAIL_COND_V_MSG(!(tex->usage_flags & TEXTURE_USAGE_CAN_COPY_FROM_BIT), PoolVector<uint8_t>(), + "Texture requires the TEXTURE_USAGE_CAN_COPY_FROM_BIT in order to be retrieved."); + + uint32_t layer_count = tex->layers; + if (tex->type == TEXTURE_TYPE_CUBE || tex->type == TEXTURE_TYPE_CUBE_ARRAY) { + layer_count *= 6; + } + ERR_FAIL_COND_V(p_layer >= layer_count, PoolVector<uint8_t>()); + + if (tex->usage_flags & TEXTURE_USAGE_CPU_READ_BIT) { + //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 + 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].setup_command_buffer; + Buffer tmp_buffer; + _buffer_allocate(&tmp_buffer, buffer_size, VK_BUFFER_USAGE_TRANSFER_DST_BIT, VMA_MEMORY_USAGE_CPU_ONLY); + + { //Source image barrier + VkImageMemoryBarrier image_memory_barrier; + image_memory_barrier.sType = VK_STRUCTURE_TYPE_IMAGE_MEMORY_BARRIER; + image_memory_barrier.pNext = NULL; + image_memory_barrier.srcAccessMask = 0; + image_memory_barrier.dstAccessMask = VK_ACCESS_TRANSFER_READ_BIT; + image_memory_barrier.oldLayout = tex->layout; + image_memory_barrier.newLayout = VK_IMAGE_LAYOUT_TRANSFER_SRC_OPTIMAL; + + image_memory_barrier.srcQueueFamilyIndex = VK_QUEUE_FAMILY_IGNORED; + image_memory_barrier.dstQueueFamilyIndex = VK_QUEUE_FAMILY_IGNORED; + image_memory_barrier.image = tex->image; + image_memory_barrier.subresourceRange.aspectMask = tex->barrier_aspect_mask; + image_memory_barrier.subresourceRange.baseMipLevel = 0; + image_memory_barrier.subresourceRange.levelCount = tex->mipmaps; + image_memory_barrier.subresourceRange.baseArrayLayer = p_layer; + image_memory_barrier.subresourceRange.layerCount = 1; + + vkCmdPipelineBarrier(command_buffer, VK_PIPELINE_STAGE_TOP_OF_PIPE_BIT, VK_PIPELINE_STAGE_TRANSFER_BIT, 0, 0, NULL, 0, NULL, 1, &image_memory_barrier); + } + + uint32_t computed_w = tex->width; + uint32_t computed_h = tex->height; + uint32_t computed_d = tex->depth; + + uint32_t prev_size = 0; + uint32_t offset = 0; + for (uint32_t i = 0; i < tex->mipmaps; i++) { + + VkBufferImageCopy buffer_image_copy; + + uint32_t image_size = get_image_format_required_size(tex->format, tex->width, tex->height, tex->depth, i + 1); + uint32_t size = image_size - prev_size; + prev_size = image_size; + + buffer_image_copy.bufferOffset = offset; + buffer_image_copy.bufferImageHeight = 0; + buffer_image_copy.bufferRowLength = 0; + buffer_image_copy.imageSubresource.aspectMask = tex->read_aspect_mask; + buffer_image_copy.imageSubresource.baseArrayLayer = p_layer; + buffer_image_copy.imageSubresource.layerCount = 1; + buffer_image_copy.imageSubresource.mipLevel = i; + buffer_image_copy.imageOffset.x = 0; + buffer_image_copy.imageOffset.y = 0; + buffer_image_copy.imageOffset.z = 0; + buffer_image_copy.imageExtent.width = computed_w; + buffer_image_copy.imageExtent.height = computed_h; + buffer_image_copy.imageExtent.depth = computed_d; + + vkCmdCopyImageToBuffer(command_buffer, tex->image, VK_IMAGE_LAYOUT_TRANSFER_SRC_OPTIMAL, tmp_buffer.buffer, 1, &buffer_image_copy); + + computed_w = MAX(1, computed_w >> 1); + computed_h = MAX(1, computed_h >> 1); + computed_d = MAX(1, computed_d >> 1); + offset += size; + } + + { //restore src + VkImageMemoryBarrier image_memory_barrier; + image_memory_barrier.sType = VK_STRUCTURE_TYPE_IMAGE_MEMORY_BARRIER; + image_memory_barrier.pNext = NULL; + image_memory_barrier.srcAccessMask = VK_ACCESS_TRANSFER_READ_BIT; + image_memory_barrier.dstAccessMask = VK_ACCESS_SHADER_READ_BIT; + image_memory_barrier.oldLayout = VK_IMAGE_LAYOUT_TRANSFER_SRC_OPTIMAL; + image_memory_barrier.newLayout = tex->layout; + image_memory_barrier.srcQueueFamilyIndex = VK_QUEUE_FAMILY_IGNORED; + image_memory_barrier.dstQueueFamilyIndex = VK_QUEUE_FAMILY_IGNORED; + image_memory_barrier.image = tex->image; + image_memory_barrier.subresourceRange.aspectMask = tex->barrier_aspect_mask; + image_memory_barrier.subresourceRange.baseMipLevel = 0; + image_memory_barrier.subresourceRange.levelCount = tex->mipmaps; + image_memory_barrier.subresourceRange.baseArrayLayer = p_layer; + image_memory_barrier.subresourceRange.layerCount = 1; + + vkCmdPipelineBarrier(command_buffer, VK_ACCESS_TRANSFER_WRITE_BIT, VK_PIPELINE_STAGE_VERTEX_SHADER_BIT | VK_PIPELINE_STAGE_COMPUTE_SHADER_BIT, 0, 0, NULL, 0, NULL, 1, &image_memory_barrier); + } + + _flush(true); + + void *buffer_mem; + VkResult vkerr = vmaMapMemory(allocator, tmp_buffer.allocation, &buffer_mem); + if (vkerr) { + ERR_FAIL_V(PoolVector<uint8_t>()); + } + + PoolVector<uint8_t> buffer_data; + { + + buffer_data.resize(buffer_size); + PoolVector<uint8_t>::Write w = buffer_data.write(); + copymem(w.ptr(), buffer_mem, buffer_size); + } + + vmaUnmapMemory(allocator, tmp_buffer.allocation); + + _buffer_free(&tmp_buffer); + + return buffer_data; + } +} + +bool RenderingDeviceVulkan::texture_is_shared(RID p_texture) { + _THREAD_SAFE_METHOD_ + + Texture *tex = texture_owner.getornull(p_texture); + ERR_FAIL_COND_V(!tex, false); + return tex->owner.is_valid(); +} + +bool RenderingDeviceVulkan::texture_is_valid(RID p_texture) { + return texture_owner.owns(p_texture); +} + +Error RenderingDeviceVulkan::texture_copy(RID p_from_texture, RID p_to_texture, const Vector3 &p_from, const Vector3 &p_to, const Vector3 &p_size, uint32_t p_src_mipmap, uint32_t p_dst_mipmap, uint32_t p_src_layer, uint32_t p_dst_layer, bool p_sync_with_draw) { + + _THREAD_SAFE_METHOD_ + + Texture *src_tex = texture_owner.getornull(p_from_texture); + ERR_FAIL_COND_V(!src_tex, ERR_INVALID_PARAMETER); + + ERR_FAIL_COND_V_MSG(p_sync_with_draw && src_tex->bound, ERR_INVALID_PARAMETER, + "Source texture can't be copied while a render pass that uses it is being created. Ensure render pass is finalized (and that it was created with RENDER_PASS_CONTENTS_FINISH) to unbind this texture."); + ERR_FAIL_COND_V_MSG(!(src_tex->usage_flags & TEXTURE_USAGE_CAN_COPY_FROM_BIT), ERR_INVALID_PARAMETER, + "Source texture requires the TEXTURE_USAGE_CAN_COPY_FROM_BIT in order to be retrieved."); + + uint32_t src_layer_count = src_tex->layers; + uint32_t src_width, src_height, src_depth; + get_image_format_required_size(src_tex->format, src_tex->width, src_tex->height, src_tex->depth, p_src_mipmap + 1, &src_width, &src_height, &src_depth); + if (src_tex->type == TEXTURE_TYPE_CUBE || src_tex->type == TEXTURE_TYPE_CUBE_ARRAY) { + src_layer_count *= 6; + } + + ERR_FAIL_COND_V(p_from.x < 0 || p_from.x + p_size.x > src_width, ERR_INVALID_PARAMETER); + ERR_FAIL_COND_V(p_from.y < 0 || p_from.y + p_size.y > src_height, ERR_INVALID_PARAMETER); + ERR_FAIL_COND_V(p_from.z < 0 || p_from.z + p_size.z > src_depth, ERR_INVALID_PARAMETER); + ERR_FAIL_COND_V(p_src_mipmap >= src_tex->mipmaps, ERR_INVALID_PARAMETER); + ERR_FAIL_COND_V(p_src_layer >= src_layer_count, ERR_INVALID_PARAMETER); + + Texture *dst_tex = texture_owner.getornull(p_to_texture); + ERR_FAIL_COND_V(!dst_tex, ERR_INVALID_PARAMETER); + + ERR_FAIL_COND_V_MSG(p_sync_with_draw && dst_tex->bound, ERR_INVALID_PARAMETER, + "Destination texture can't be copied while a render pass that uses it is being created. Ensure render pass is finalized (and that it was created with RENDER_PASS_CONTENTS_FINISH) to unbind this texture."); + ERR_FAIL_COND_V_MSG(!(dst_tex->usage_flags & TEXTURE_USAGE_CAN_COPY_TO_BIT), ERR_INVALID_PARAMETER, + "Destination texture requires the TEXTURE_USAGE_CAN_COPY_TO_BIT in order to be retrieved."); + + uint32_t dst_layer_count = dst_tex->layers; + uint32_t dst_width, dst_height, dst_depth; + get_image_format_required_size(dst_tex->format, dst_tex->width, dst_tex->height, dst_tex->depth, p_dst_mipmap + 1, &dst_width, &dst_height, &dst_depth); + if (dst_tex->type == TEXTURE_TYPE_CUBE || dst_tex->type == TEXTURE_TYPE_CUBE_ARRAY) { + dst_layer_count *= 6; + } + + ERR_FAIL_COND_V(p_to.x < 0 || p_to.x + p_size.x > dst_width, ERR_INVALID_PARAMETER); + ERR_FAIL_COND_V(p_to.y < 0 || p_to.y + p_size.y > dst_height, ERR_INVALID_PARAMETER); + ERR_FAIL_COND_V(p_to.z < 0 || p_to.z + p_size.z > dst_depth, ERR_INVALID_PARAMETER); + ERR_FAIL_COND_V(p_dst_mipmap >= dst_tex->mipmaps, ERR_INVALID_PARAMETER); + ERR_FAIL_COND_V(p_dst_layer >= dst_layer_count, ERR_INVALID_PARAMETER); + + ERR_FAIL_COND_V_MSG(src_tex->read_aspect_mask != dst_tex->read_aspect_mask, ERR_INVALID_PARAMETER, + "Source and destination texture must be of the same type (color or depth)."); + + VkCommandBuffer command_buffer = p_sync_with_draw ? frames[frame].draw_command_buffer : frames[frame].setup_command_buffer; + + { + + //PRE Copy the image + + { //Source + VkImageMemoryBarrier image_memory_barrier; + image_memory_barrier.sType = VK_STRUCTURE_TYPE_IMAGE_MEMORY_BARRIER; + image_memory_barrier.pNext = NULL; + image_memory_barrier.srcAccessMask = 0; + image_memory_barrier.dstAccessMask = VK_ACCESS_TRANSFER_READ_BIT; + image_memory_barrier.oldLayout = src_tex->layout; + image_memory_barrier.newLayout = VK_IMAGE_LAYOUT_TRANSFER_SRC_OPTIMAL; + + image_memory_barrier.srcQueueFamilyIndex = VK_QUEUE_FAMILY_IGNORED; + image_memory_barrier.dstQueueFamilyIndex = VK_QUEUE_FAMILY_IGNORED; + image_memory_barrier.image = src_tex->image; + image_memory_barrier.subresourceRange.aspectMask = src_tex->barrier_aspect_mask; + image_memory_barrier.subresourceRange.baseMipLevel = p_src_mipmap; + image_memory_barrier.subresourceRange.levelCount = 1; + image_memory_barrier.subresourceRange.baseArrayLayer = p_src_layer; + image_memory_barrier.subresourceRange.layerCount = 1; + + vkCmdPipelineBarrier(command_buffer, VK_PIPELINE_STAGE_TOP_OF_PIPE_BIT, VK_PIPELINE_STAGE_TRANSFER_BIT, 0, 0, NULL, 0, NULL, 1, &image_memory_barrier); + } + { //Dest + VkImageMemoryBarrier image_memory_barrier; + image_memory_barrier.sType = VK_STRUCTURE_TYPE_IMAGE_MEMORY_BARRIER; + image_memory_barrier.pNext = NULL; + image_memory_barrier.srcAccessMask = 0; + image_memory_barrier.dstAccessMask = VK_ACCESS_TRANSFER_WRITE_BIT; + image_memory_barrier.oldLayout = dst_tex->layout; + image_memory_barrier.newLayout = VK_IMAGE_LAYOUT_TRANSFER_DST_OPTIMAL; + + image_memory_barrier.srcQueueFamilyIndex = VK_QUEUE_FAMILY_IGNORED; + image_memory_barrier.dstQueueFamilyIndex = VK_QUEUE_FAMILY_IGNORED; + image_memory_barrier.image = dst_tex->image; + image_memory_barrier.subresourceRange.aspectMask = dst_tex->read_aspect_mask; + image_memory_barrier.subresourceRange.baseMipLevel = p_dst_mipmap; + image_memory_barrier.subresourceRange.levelCount = 1; + image_memory_barrier.subresourceRange.baseArrayLayer = p_dst_layer; + image_memory_barrier.subresourceRange.layerCount = 1; + + vkCmdPipelineBarrier(command_buffer, VK_PIPELINE_STAGE_TOP_OF_PIPE_BIT, VK_PIPELINE_STAGE_TRANSFER_BIT, 0, 0, NULL, 0, NULL, 1, &image_memory_barrier); + } + + //COPY + + { + + VkImageCopy image_copy_region; + image_copy_region.srcSubresource.aspectMask = src_tex->read_aspect_mask; + image_copy_region.srcSubresource.baseArrayLayer = p_src_layer; + image_copy_region.srcSubresource.layerCount = 1; + image_copy_region.srcSubresource.mipLevel = p_src_mipmap; + image_copy_region.srcOffset.x = p_from.x; + image_copy_region.srcOffset.y = p_from.y; + image_copy_region.srcOffset.z = p_from.z; + + image_copy_region.dstSubresource.aspectMask = dst_tex->read_aspect_mask; + image_copy_region.dstSubresource.baseArrayLayer = p_dst_layer; + image_copy_region.dstSubresource.layerCount = 1; + image_copy_region.dstSubresource.mipLevel = p_dst_mipmap; + image_copy_region.dstOffset.x = p_to.x; + image_copy_region.dstOffset.y = p_to.y; + image_copy_region.dstOffset.z = p_to.z; + + image_copy_region.extent.width = p_size.x; + image_copy_region.extent.height = p_size.y; + image_copy_region.extent.depth = p_size.z; + + 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 src + VkImageMemoryBarrier image_memory_barrier; + image_memory_barrier.sType = VK_STRUCTURE_TYPE_IMAGE_MEMORY_BARRIER; + image_memory_barrier.pNext = NULL; + image_memory_barrier.srcAccessMask = VK_ACCESS_TRANSFER_READ_BIT; + image_memory_barrier.dstAccessMask = VK_ACCESS_SHADER_READ_BIT | VK_ACCESS_SHADER_WRITE_BIT; + image_memory_barrier.oldLayout = VK_IMAGE_LAYOUT_TRANSFER_SRC_OPTIMAL; + image_memory_barrier.newLayout = src_tex->layout; + image_memory_barrier.srcQueueFamilyIndex = VK_QUEUE_FAMILY_IGNORED; + image_memory_barrier.dstQueueFamilyIndex = VK_QUEUE_FAMILY_IGNORED; + image_memory_barrier.image = src_tex->image; + image_memory_barrier.subresourceRange.aspectMask = src_tex->barrier_aspect_mask; + image_memory_barrier.subresourceRange.baseMipLevel = p_src_mipmap; + image_memory_barrier.subresourceRange.levelCount = src_tex->mipmaps; + image_memory_barrier.subresourceRange.baseArrayLayer = p_src_layer; + image_memory_barrier.subresourceRange.layerCount = 1; + + vkCmdPipelineBarrier(command_buffer, VK_ACCESS_TRANSFER_WRITE_BIT, VK_PIPELINE_STAGE_VERTEX_SHADER_BIT | VK_PIPELINE_STAGE_COMPUTE_SHADER_BIT, 0, 0, NULL, 0, NULL, 1, &image_memory_barrier); + } + + { //make dst readable + + VkImageMemoryBarrier image_memory_barrier; + image_memory_barrier.sType = VK_STRUCTURE_TYPE_IMAGE_MEMORY_BARRIER; + image_memory_barrier.pNext = NULL; + image_memory_barrier.srcAccessMask = VK_ACCESS_TRANSFER_WRITE_BIT; + image_memory_barrier.dstAccessMask = VK_ACCESS_SHADER_READ_BIT | VK_ACCESS_SHADER_WRITE_BIT; + image_memory_barrier.oldLayout = VK_IMAGE_LAYOUT_TRANSFER_DST_OPTIMAL; + image_memory_barrier.newLayout = dst_tex->layout; + + image_memory_barrier.srcQueueFamilyIndex = VK_QUEUE_FAMILY_IGNORED; + image_memory_barrier.dstQueueFamilyIndex = VK_QUEUE_FAMILY_IGNORED; + image_memory_barrier.image = dst_tex->image; + image_memory_barrier.subresourceRange.aspectMask = VK_IMAGE_ASPECT_COLOR_BIT; + image_memory_barrier.subresourceRange.baseMipLevel = p_src_mipmap; + image_memory_barrier.subresourceRange.levelCount = 1; + image_memory_barrier.subresourceRange.baseArrayLayer = p_src_layer; + image_memory_barrier.subresourceRange.layerCount = 1; + + vkCmdPipelineBarrier(command_buffer, VK_PIPELINE_STAGE_TRANSFER_BIT, VK_PIPELINE_STAGE_VERTEX_SHADER_BIT | VK_PIPELINE_STAGE_COMPUTE_SHADER_BIT, 0, 0, NULL, 0, NULL, 1, &image_memory_barrier); + } + } + + return OK; +} + +Error RenderingDeviceVulkan::texture_clear(RID p_texture, const Color &p_color, uint32_t p_base_mipmap, uint32_t p_mipmaps, uint32_t p_base_layer, uint32_t p_layers, bool p_sync_with_draw) { + + _THREAD_SAFE_METHOD_ + + Texture *src_tex = texture_owner.getornull(p_texture); + ERR_FAIL_COND_V(!src_tex, ERR_INVALID_PARAMETER); + + ERR_FAIL_COND_V_MSG(p_sync_with_draw && src_tex->bound, ERR_INVALID_PARAMETER, + "Source texture can't be cleared while a render pass that uses it is being created. Ensure render pass is finalized (and that it was created with RENDER_PASS_CONTENTS_FINISH) to unbind this texture."); + + ERR_FAIL_COND_V(p_layers == 0, ERR_INVALID_PARAMETER); + ERR_FAIL_COND_V(p_mipmaps == 0, ERR_INVALID_PARAMETER); + + ERR_FAIL_COND_V_MSG(!(src_tex->usage_flags & TEXTURE_USAGE_CAN_COPY_TO_BIT), ERR_INVALID_PARAMETER, + "Source texture requires the TEXTURE_USAGE_CAN_COPY_TO_BIT in order to be cleared."); + + uint32_t src_layer_count = src_tex->layers; + if (src_tex->type == TEXTURE_TYPE_CUBE || src_tex->type == TEXTURE_TYPE_CUBE_ARRAY) { + src_layer_count *= 6; + } + + ERR_FAIL_COND_V(p_base_mipmap + p_mipmaps > src_tex->mipmaps, ERR_INVALID_PARAMETER); + ERR_FAIL_COND_V(p_base_layer + p_layers > src_layer_count, ERR_INVALID_PARAMETER); + + VkCommandBuffer command_buffer = p_sync_with_draw ? frames[frame].draw_command_buffer : frames[frame].setup_command_buffer; + + VkImageLayout layout = src_tex->layout; + + if (src_tex->layout != VK_IMAGE_LAYOUT_GENERAL) { //storage may be in general state + VkImageMemoryBarrier image_memory_barrier; + image_memory_barrier.sType = VK_STRUCTURE_TYPE_IMAGE_MEMORY_BARRIER; + image_memory_barrier.pNext = NULL; + image_memory_barrier.srcAccessMask = 0; + image_memory_barrier.dstAccessMask = VK_ACCESS_TRANSFER_WRITE_BIT; + image_memory_barrier.oldLayout = src_tex->layout; + image_memory_barrier.newLayout = VK_IMAGE_LAYOUT_TRANSFER_DST_OPTIMAL; + + image_memory_barrier.srcQueueFamilyIndex = VK_QUEUE_FAMILY_IGNORED; + image_memory_barrier.dstQueueFamilyIndex = VK_QUEUE_FAMILY_IGNORED; + image_memory_barrier.image = src_tex->image; + image_memory_barrier.subresourceRange.aspectMask = src_tex->read_aspect_mask; + image_memory_barrier.subresourceRange.baseMipLevel = p_base_mipmap; + image_memory_barrier.subresourceRange.levelCount = p_mipmaps; + image_memory_barrier.subresourceRange.baseArrayLayer = p_base_layer; + image_memory_barrier.subresourceRange.layerCount = p_layers; + + layout = VK_IMAGE_LAYOUT_TRANSFER_DST_OPTIMAL; + vkCmdPipelineBarrier(command_buffer, VK_PIPELINE_STAGE_TOP_OF_PIPE_BIT, VK_PIPELINE_STAGE_TRANSFER_BIT, 0, 0, NULL, 0, NULL, 1, &image_memory_barrier); + } + + VkClearColorValue clear_color; + clear_color.float32[0] = p_color.r; + clear_color.float32[1] = p_color.g; + clear_color.float32[2] = p_color.b; + clear_color.float32[3] = p_color.a; + + VkImageSubresourceRange range; + range.aspectMask = src_tex->read_aspect_mask; + range.baseArrayLayer = p_base_layer; + range.layerCount = p_layers; + range.baseMipLevel = p_base_mipmap; + range.levelCount = p_mipmaps; + + vkCmdClearColorImage(command_buffer, src_tex->image, layout, &clear_color, 1, &range); + + if (src_tex->layout != VK_IMAGE_LAYOUT_GENERAL) { //storage may be in general state + + VkImageMemoryBarrier image_memory_barrier; + image_memory_barrier.sType = VK_STRUCTURE_TYPE_IMAGE_MEMORY_BARRIER; + image_memory_barrier.pNext = NULL; + image_memory_barrier.srcAccessMask = VK_ACCESS_TRANSFER_WRITE_BIT; + image_memory_barrier.dstAccessMask = VK_ACCESS_SHADER_READ_BIT; + image_memory_barrier.oldLayout = VK_IMAGE_LAYOUT_TRANSFER_DST_OPTIMAL; + image_memory_barrier.newLayout = src_tex->layout; + + image_memory_barrier.srcQueueFamilyIndex = VK_QUEUE_FAMILY_IGNORED; + image_memory_barrier.dstQueueFamilyIndex = VK_QUEUE_FAMILY_IGNORED; + image_memory_barrier.image = src_tex->image; + image_memory_barrier.subresourceRange.aspectMask = src_tex->read_aspect_mask; + image_memory_barrier.subresourceRange.baseMipLevel = p_base_mipmap; + image_memory_barrier.subresourceRange.levelCount = p_mipmaps; + image_memory_barrier.subresourceRange.baseArrayLayer = p_base_layer; + image_memory_barrier.subresourceRange.layerCount = p_layers; + + vkCmdPipelineBarrier(command_buffer, VK_PIPELINE_STAGE_TRANSFER_BIT, VK_PIPELINE_STAGE_VERTEX_SHADER_BIT | VK_PIPELINE_STAGE_COMPUTE_SHADER_BIT, 0, 0, NULL, 0, NULL, 1, &image_memory_barrier); + } + + return OK; +} + +bool RenderingDeviceVulkan::texture_is_format_supported_for_usage(DataFormat p_format, uint32_t p_usage) const { + ERR_FAIL_INDEX_V(p_format, DATA_FORMAT_MAX, false); + + _THREAD_SAFE_METHOD_ + + //validate that this image is supported for the intended use + VkFormatProperties properties; + vkGetPhysicalDeviceFormatProperties(context->get_physical_device(), vulkan_formats[p_format], &properties); + VkFormatFeatureFlags flags; + + if (p_usage & TEXTURE_USAGE_CPU_READ_BIT) { + flags = properties.linearTilingFeatures; + } else { + flags = properties.optimalTilingFeatures; + } + + if (p_usage & TEXTURE_USAGE_SAMPLING_BIT && !(flags & VK_FORMAT_FEATURE_SAMPLED_IMAGE_BIT)) { + return false; + } + + if (p_usage & TEXTURE_USAGE_COLOR_ATTACHMENT_BIT && !(flags & VK_FORMAT_FEATURE_COLOR_ATTACHMENT_BIT)) { + return false; + } + + if (p_usage & TEXTURE_USAGE_DEPTH_STENCIL_ATTACHMENT_BIT && !(flags & VK_FORMAT_FEATURE_DEPTH_STENCIL_ATTACHMENT_BIT)) { + return false; + } + + if (p_usage & TEXTURE_USAGE_STORAGE_BIT && !(flags & VK_FORMAT_FEATURE_STORAGE_IMAGE_BIT)) { + return false; + } + + if (p_usage & TEXTURE_USAGE_STORAGE_ATOMIC_BIT && !(flags & VK_FORMAT_FEATURE_STORAGE_IMAGE_ATOMIC_BIT)) { + return false; + } + + return true; +} + +/********************/ +/**** ATTACHMENT ****/ +/********************/ + +VkRenderPass RenderingDeviceVulkan::_render_pass_create(const Vector<AttachmentFormat> &p_format, InitialAction p_initial_color_action, FinalAction p_final_color_action, InitialAction p_initial_depth_action, FinalAction p_final_depth_action, int *r_color_attachment_count) { + + Vector<VkAttachmentDescription> attachments; + Vector<VkAttachmentReference> color_references; + Vector<VkAttachmentReference> depth_stencil_references; + Vector<VkAttachmentReference> resolve_references; + + for (int i = 0; i < p_format.size(); i++) { + + VkAttachmentDescription description; + + description.flags = 0; + ERR_FAIL_INDEX_V(p_format[i].format, DATA_FORMAT_MAX, VK_NULL_HANDLE); + description.format = vulkan_formats[p_format[i].format]; + ERR_FAIL_INDEX_V(p_format[i].samples, TEXTURE_SAMPLES_MAX, VK_NULL_HANDLE); + description.samples = rasterization_sample_count[p_format[i].samples]; + //anything below does not really matter, as vulkan just ignores it when creating a pipeline + ERR_FAIL_COND_V_MSG(!(p_format[i].usage_flags & (TEXTURE_USAGE_COLOR_ATTACHMENT_BIT | TEXTURE_USAGE_DEPTH_STENCIL_ATTACHMENT_BIT | TEXTURE_USAGE_RESOLVE_ATTACHMENT_BIT)), VK_NULL_HANDLE, + "Texture format for index (" + itos(i) + ") requires an attachment (depth, stencil or resolve) bit set."); + + bool is_depth_stencil = p_format[i].usage_flags & TEXTURE_USAGE_DEPTH_STENCIL_ATTACHMENT_BIT; + bool is_sampled = p_format[i].usage_flags & TEXTURE_USAGE_SAMPLING_BIT; + bool is_storage = p_format[i].usage_flags & TEXTURE_USAGE_STORAGE_BIT; + + switch (is_depth_stencil ? p_initial_depth_action : p_initial_color_action) { + + case INITIAL_ACTION_CLEAR: { + description.loadOp = VK_ATTACHMENT_LOAD_OP_CLEAR; + description.stencilLoadOp = VK_ATTACHMENT_LOAD_OP_CLEAR; + description.initialLayout = VK_IMAGE_LAYOUT_UNDEFINED; //don't care what is there + } break; + case INITIAL_ACTION_KEEP: { + if (p_format[i].usage_flags & TEXTURE_USAGE_COLOR_ATTACHMENT_BIT) { + description.loadOp = VK_ATTACHMENT_LOAD_OP_LOAD; + description.initialLayout = is_sampled ? VK_IMAGE_LAYOUT_SHADER_READ_ONLY_OPTIMAL : (is_storage ? VK_IMAGE_LAYOUT_GENERAL : VK_IMAGE_LAYOUT_COLOR_ATTACHMENT_OPTIMAL); + description.stencilLoadOp = VK_ATTACHMENT_LOAD_OP_DONT_CARE; + } else if (p_format[i].usage_flags & TEXTURE_USAGE_DEPTH_STENCIL_ATTACHMENT_BIT) { + description.loadOp = VK_ATTACHMENT_LOAD_OP_CLEAR; + description.initialLayout = VK_IMAGE_LAYOUT_UNDEFINED; //don't care what is there + description.stencilLoadOp = VK_ATTACHMENT_LOAD_OP_CLEAR; + } 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 INITIAL_ACTION_CONTINUE: { + if (p_format[i].usage_flags & TEXTURE_USAGE_COLOR_ATTACHMENT_BIT) { + description.loadOp = VK_ATTACHMENT_LOAD_OP_LOAD; + description.initialLayout = VK_IMAGE_LAYOUT_COLOR_ATTACHMENT_OPTIMAL; + description.stencilLoadOp = VK_ATTACHMENT_LOAD_OP_DONT_CARE; + } else if (p_format[i].usage_flags & TEXTURE_USAGE_DEPTH_STENCIL_ATTACHMENT_BIT) { + description.loadOp = VK_ATTACHMENT_LOAD_OP_LOAD; + description.initialLayout = VK_IMAGE_LAYOUT_DEPTH_STENCIL_ATTACHMENT_OPTIMAL; //don't care what is there + 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 + } + } break; + default: { + ERR_FAIL_V(VK_NULL_HANDLE); //should never reach here + } + } + + switch (is_depth_stencil ? p_final_depth_action : p_final_color_action) { + case FINAL_ACTION_READ: { + + if (p_format[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); + } else if (p_format[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); + } 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_DISCARD: { + if (p_format[i].usage_flags & TEXTURE_USAGE_COLOR_ATTACHMENT_BIT) { + description.storeOp = VK_ATTACHMENT_STORE_OP_DONT_CARE; + description.stencilStoreOp = VK_ATTACHMENT_STORE_OP_DONT_CARE; + description.finalLayout = is_sampled ? VK_IMAGE_LAYOUT_SHADER_READ_ONLY_OPTIMAL : (is_storage ? VK_IMAGE_LAYOUT_GENERAL : VK_IMAGE_LAYOUT_COLOR_ATTACHMENT_OPTIMAL); + } else if (p_format[i].usage_flags & TEXTURE_USAGE_DEPTH_STENCIL_ATTACHMENT_BIT) { + + 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_format[i].usage_flags & TEXTURE_USAGE_COLOR_ATTACHMENT_BIT) { + description.storeOp = VK_ATTACHMENT_STORE_OP_STORE; + description.stencilStoreOp = VK_ATTACHMENT_STORE_OP_DONT_CARE; + description.finalLayout = VK_IMAGE_LAYOUT_COLOR_ATTACHMENT_OPTIMAL; + } else if (p_format[i].usage_flags & TEXTURE_USAGE_DEPTH_STENCIL_ATTACHMENT_BIT) { + + 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 + } + + } break; + default: { + ERR_FAIL_V(VK_NULL_HANDLE); //should never reach here + } + } + + attachments.push_back(description); + + VkAttachmentReference reference; + reference.attachment = i; + + if (p_format[i].usage_flags & TEXTURE_USAGE_COLOR_ATTACHMENT_BIT) { + reference.layout = VK_IMAGE_LAYOUT_COLOR_ATTACHMENT_OPTIMAL; + color_references.push_back(reference); + } else if (p_format[i].usage_flags & TEXTURE_USAGE_DEPTH_STENCIL_ATTACHMENT_BIT) { + reference.layout = VK_IMAGE_LAYOUT_DEPTH_STENCIL_ATTACHMENT_OPTIMAL; + depth_stencil_references.push_back(reference); + } else if (p_format[i].usage_flags & TEXTURE_USAGE_RESOLVE_ATTACHMENT_BIT) { + reference.layout = VK_IMAGE_LAYOUT_COLOR_ATTACHMENT_OPTIMAL; + resolve_references.push_back(reference); + } else { + ERR_FAIL_V_MSG(VK_NULL_HANDLE, "Texture index " + itos(i) + " is neither color, depth stencil or resolve so it can't be used as attachment."); + } + } + + ERR_FAIL_COND_V_MSG(depth_stencil_references.size() > 1, VK_NULL_HANDLE, + "Formats can only have one depth/stencil attachment, supplied (" + itos(depth_stencil_references.size()) + ")."); + + ERR_FAIL_COND_V_MSG(resolve_references.size() > 1, VK_NULL_HANDLE, + "Formats can only have one resolve attachment, supplied (" + itos(resolve_references.size()) + ")."); + + VkSubpassDescription subpass; + subpass.flags = 0; + subpass.pipelineBindPoint = VK_PIPELINE_BIND_POINT_GRAPHICS; + subpass.inputAttachmentCount = 0; //unsupported for now + subpass.pInputAttachments = NULL; + subpass.colorAttachmentCount = color_references.size(); + subpass.pColorAttachments = color_references.ptr(); + subpass.pDepthStencilAttachment = depth_stencil_references.ptr(); + subpass.pResolveAttachments = resolve_references.ptr(); + subpass.preserveAttachmentCount = 0; + subpass.pPreserveAttachments = NULL; + + VkRenderPassCreateInfo render_pass_create_info; + render_pass_create_info.sType = VK_STRUCTURE_TYPE_RENDER_PASS_CREATE_INFO; + render_pass_create_info.pNext = NULL; + render_pass_create_info.flags = 0; + render_pass_create_info.attachmentCount = attachments.size(); + render_pass_create_info.pAttachments = attachments.ptr(); + render_pass_create_info.subpassCount = 1; + render_pass_create_info.pSubpasses = &subpass; + render_pass_create_info.dependencyCount = 0; + render_pass_create_info.pDependencies = NULL; + + VkRenderPass render_pass; + VkResult res = vkCreateRenderPass(device, &render_pass_create_info, NULL, &render_pass); + ERR_FAIL_COND_V(res, VK_NULL_HANDLE); + + if (r_color_attachment_count) { + *r_color_attachment_count = color_references.size(); + } + return render_pass; +} + +RenderingDevice::FramebufferFormatID RenderingDeviceVulkan::framebuffer_format_create(const Vector<AttachmentFormat> &p_format) { + + _THREAD_SAFE_METHOD_ + + FramebufferFormatKey key; + key.attachments = p_format; + + const Map<FramebufferFormatKey, FramebufferFormatID>::Element *E = framebuffer_format_cache.find(key); + if (E) { + //exists, return + return E->get(); + } + + int color_references; + VkRenderPass render_pass = _render_pass_create(p_format, INITIAL_ACTION_CLEAR, FINAL_ACTION_DISCARD, INITIAL_ACTION_CLEAR, FINAL_ACTION_DISCARD, &color_references); //actions don't matter for this use case + + 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)); + + E = framebuffer_format_cache.insert(key, id); + FramebufferFormat fb_format; + fb_format.E = E; + fb_format.color_attachments = color_references; + fb_format.render_pass = render_pass; + fb_format.samples = p_format[0].samples; + framebuffer_formats[id] = fb_format; + return id; +} + +RenderingDevice::TextureSamples RenderingDeviceVulkan::framebuffer_format_get_texture_samples(FramebufferFormatID p_format) { + Map<FramebufferFormatID, FramebufferFormat>::Element *E = framebuffer_formats.find(p_format); + ERR_FAIL_COND_V(!E, TEXTURE_SAMPLES_1); + + return E->get().samples; +} + +/***********************/ +/**** RENDER TARGET ****/ +/***********************/ + +RID RenderingDeviceVulkan::framebuffer_create(const Vector<RID> &p_texture_attachments, FramebufferFormatID p_format_check) { + + _THREAD_SAFE_METHOD_ + + Vector<AttachmentFormat> attachments; + Size2i size; + + for (int i = 0; i < p_texture_attachments.size(); i++) { + Texture *texture = texture_owner.getornull(p_texture_attachments[i]); + ERR_FAIL_COND_V_MSG(!texture, RID(), "Texture index supplied for framebuffer (" + itos(i) + ") is not a valid texture."); + + if (i == 0) { + size.width = texture->width; + size.height = texture->height; + } 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."); + } + + AttachmentFormat af; + af.format = texture->format; + af.samples = texture->samples; + af.usage_flags = texture->usage_flags; + attachments.push_back(af); + } + + FramebufferFormatID format_id = framebuffer_format_create(attachments); + if (format_id == INVALID_ID) { + return RID(); + } + + ERR_FAIL_COND_V_MSG(p_format_check != INVALID_ID && format_id != p_format_check, RID(), + "The format used to check this framebuffer differs from the intended framebuffer format."); + + Framebuffer framebuffer; + framebuffer.format_id = format_id; + framebuffer.texture_ids = p_texture_attachments; + framebuffer.size = size; + + RID id = framebuffer_owner.make_rid(framebuffer); + + for (int i = 0; i < p_texture_attachments.size(); i++) { + _add_dependency(id, p_texture_attachments[i]); + } + + return id; +} + +RenderingDevice::FramebufferFormatID RenderingDeviceVulkan::framebuffer_get_format(RID p_framebuffer) { + + _THREAD_SAFE_METHOD_ + + Framebuffer *framebuffer = framebuffer_owner.getornull(p_framebuffer); + ERR_FAIL_COND_V(!framebuffer, INVALID_ID); + + return framebuffer->format_id; +} + +/*****************/ +/**** SAMPLER ****/ +/*****************/ + +RID RenderingDeviceVulkan::sampler_create(const SamplerState &p_state) { + + _THREAD_SAFE_METHOD_ + + VkSamplerCreateInfo sampler_create_info; + sampler_create_info.sType = VK_STRUCTURE_TYPE_SAMPLER_CREATE_INFO; + sampler_create_info.pNext = NULL; + sampler_create_info.flags = 0; + sampler_create_info.magFilter = p_state.mag_filter == SAMPLER_FILTER_LINEAR ? VK_FILTER_LINEAR : VK_FILTER_NEAREST; + sampler_create_info.minFilter = p_state.min_filter == SAMPLER_FILTER_LINEAR ? VK_FILTER_LINEAR : VK_FILTER_NEAREST; + sampler_create_info.mipmapMode = p_state.mip_filter == SAMPLER_FILTER_LINEAR ? VK_SAMPLER_MIPMAP_MODE_LINEAR : VK_SAMPLER_MIPMAP_MODE_NEAREST; + + ERR_FAIL_INDEX_V(p_state.repeat_u, SAMPLER_REPEAT_MODE_MAX, RID()); + sampler_create_info.addressModeU = address_modes[p_state.repeat_u]; + ERR_FAIL_INDEX_V(p_state.repeat_v, SAMPLER_REPEAT_MODE_MAX, RID()); + sampler_create_info.addressModeV = address_modes[p_state.repeat_v]; + ERR_FAIL_INDEX_V(p_state.repeat_w, SAMPLER_REPEAT_MODE_MAX, RID()); + sampler_create_info.addressModeW = address_modes[p_state.repeat_w]; + + sampler_create_info.mipLodBias = p_state.lod_bias; + sampler_create_info.anisotropyEnable = p_state.use_anisotropy; + sampler_create_info.maxAnisotropy = p_state.anisotropy_max; + sampler_create_info.compareEnable = p_state.enable_compare; + + ERR_FAIL_INDEX_V(p_state.compare_op, COMPARE_OP_MAX, RID()); + sampler_create_info.compareOp = compare_operators[p_state.compare_op]; + + sampler_create_info.minLod = p_state.min_lod; + sampler_create_info.maxLod = p_state.max_lod; + + ERR_FAIL_INDEX_V(p_state.border_color, SAMPLER_BORDER_COLOR_MAX, RID()); + sampler_create_info.borderColor = sampler_border_colors[p_state.border_color]; + + sampler_create_info.unnormalizedCoordinates = p_state.unnormalized_uvw; + + VkSampler sampler; + VkResult res = vkCreateSampler(device, &sampler_create_info, NULL, &sampler); + ERR_FAIL_COND_V(res, RID()); + + return sampler_owner.make_rid(sampler); +} + +/**********************/ +/**** VERTEX ARRAY ****/ +/**********************/ + +RID RenderingDeviceVulkan::vertex_buffer_create(uint32_t p_size_bytes, const PoolVector<uint8_t> &p_data) { + + _THREAD_SAFE_METHOD_ + + ERR_FAIL_COND_V(p_data.size() && (uint32_t)p_data.size() != p_size_bytes, RID()); + + Buffer buffer; + _buffer_allocate(&buffer, p_size_bytes, VK_BUFFER_USAGE_TRANSFER_SRC_BIT | VK_BUFFER_USAGE_TRANSFER_DST_BIT | VK_BUFFER_USAGE_VERTEX_BUFFER_BIT, VMA_MEMORY_USAGE_GPU_ONLY); + if (p_data.size()) { + uint64_t data_size = p_data.size(); + PoolVector<uint8_t>::Read r = p_data.read(); + _buffer_update(&buffer, 0, r.ptr(), data_size); + _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); +} + +// 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<VertexDescription> &p_vertex_formats) { + + _THREAD_SAFE_METHOD_ + + VertexDescriptionKey key; + key.vertex_formats = p_vertex_formats; + + VertexFormatID *idptr = vertex_format_cache.getptr(key); + if (idptr) { + return *idptr; + } + + //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; + 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); + + ERR_FAIL_COND_V_MSG(get_format_vertex_size(p_vertex_formats[i].format) == 0, INVALID_ID, + "Data format for attachment (" + itos(i) + ") is not valid for a vertex array."); + + vdcache.bindings[i].binding = i; + vdcache.bindings[i].stride = p_vertex_formats[i].stride; + vdcache.bindings[i].inputRate = p_vertex_formats[i].frequency == VERTEX_FREQUENCY_INSTANCE ? VK_VERTEX_INPUT_RATE_INSTANCE : VK_VERTEX_INPUT_RATE_VERTEX; + vdcache.attributes[i].binding = i; + vdcache.attributes[i].location = p_vertex_formats[i].location; + vdcache.attributes[i].format = vulkan_formats[p_vertex_formats[i].format]; + vdcache.attributes[i].offset = p_vertex_formats[i].offset; + used_locations.insert(p_vertex_formats[i].location); + } + + vdcache.create_info.sType = VK_STRUCTURE_TYPE_PIPELINE_VERTEX_INPUT_STATE_CREATE_INFO; + vdcache.create_info.pNext = NULL; + vdcache.create_info.flags = 0; + + vdcache.create_info.vertexAttributeDescriptionCount = p_vertex_formats.size(); + vdcache.create_info.pVertexAttributeDescriptions = vdcache.attributes; + + vdcache.create_info.vertexBindingDescriptionCount = p_vertex_formats.size(); + vdcache.create_info.pVertexBindingDescriptions = vdcache.bindings; + vdcache.vertex_formats = p_vertex_formats; + + VertexFormatID id = VertexFormatID(vertex_format_cache.size()) | (VertexFormatID(ID_TYPE_VERTEX_FORMAT) << ID_BASE_SHIFT); + vertex_format_cache[key] = id; + vertex_formats[id] = vdcache; + return id; +} + +RID RenderingDeviceVulkan::vertex_array_create(uint32_t p_vertex_count, VertexFormatID p_vertex_format, const Vector<RID> &p_src_buffers) { + + _THREAD_SAFE_METHOD_ + + ERR_FAIL_COND_V(!vertex_formats.has(p_vertex_format), RID()); + const VertexDescriptionCache &vd = vertex_formats[p_vertex_format]; + + ERR_FAIL_COND_V(vd.vertex_formats.size() != p_src_buffers.size(), RID()); + + for (int i = 0; i < p_src_buffers.size(); i++) { + ERR_FAIL_COND_V(!vertex_buffer_owner.owns(p_src_buffers[i]), RID()); + } + + VertexArray vertex_array; + + 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 + for (int i = 0; i < p_src_buffers.size(); i++) { + Buffer *buffer = vertex_buffer_owner.getornull(p_src_buffers[i]); + + //validate with buffer + { + const VertexDescription &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 + + if (atf.frequency == VERTEX_FREQUENCY_VERTEX) { + //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 + 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."); + + uint32_t instances_allowed = available / atf.stride; + vertex_array.max_instances_allowed = MIN(instances_allowed, vertex_array.max_instances_allowed); + } + } + + vertex_array.buffers.push_back(buffer->buffer); + vertex_array.offsets.push_back(0); //offset unused, but passing anyway + } + + RID id = vertex_array_owner.make_rid(vertex_array); + for (int i = 0; i < p_src_buffers.size(); i++) { + _add_dependency(id, p_src_buffers[i]); + } + + return id; +} + +RID RenderingDeviceVulkan::index_buffer_create(uint32_t p_index_count, IndexBufferFormat p_format, const PoolVector<uint8_t> &p_data, bool p_use_restart_indices) { + + _THREAD_SAFE_METHOD_ + + ERR_FAIL_COND_V(p_index_count == 0, RID()); + + IndexBuffer index_buffer; + index_buffer.index_type = (p_format == INDEX_BUFFER_FORMAT_UINT16) ? VK_INDEX_TYPE_UINT16 : VK_INDEX_TYPE_UINT32; + index_buffer.supports_restart_indices = p_use_restart_indices; + index_buffer.index_count = p_index_count; + uint32_t size_bytes = p_index_count * ((p_format == INDEX_BUFFER_FORMAT_UINT16) ? 2 : 4); +#ifdef DEBUG_ENABLED + if (p_data.size()) { + index_buffer.max_index = 0; + ERR_FAIL_COND_V_MSG((uint32_t)p_data.size() != size_bytes, RID(), + "Default index buffer initializer array size (" + itos(p_data.size()) + ") does not match format required size (" + itos(size_bytes) + ")."); + PoolVector<uint8_t>::Read r = p_data.read(); + if (p_format == INDEX_BUFFER_FORMAT_UINT16) { + const uint16_t *index16 = (const uint16_t *)r.ptr(); + for (uint32_t i = 0; i < p_index_count; i++) { + if (p_use_restart_indices && index16[i] == 0xFFFF) { + continue; //restart index, ingnore + } + index_buffer.max_index = MAX(index16[i], index_buffer.max_index); + } + } else { + const uint32_t *index32 = (const uint32_t *)r.ptr(); + for (uint32_t i = 0; i < p_index_count; i++) { + if (p_use_restart_indices && index32[i] == 0xFFFFFFFF) { + continue; //restart index, ingnore + } + index_buffer.max_index = MAX(index32[i], index_buffer.max_index); + } + } + } else { + index_buffer.max_index = 0xFFFFFFFF; + } +#else + index_buffer.max_index = 0xFFFFFFFF; +#endif + _buffer_allocate(&index_buffer, size_bytes, VK_BUFFER_USAGE_TRANSFER_SRC_BIT | VK_BUFFER_USAGE_TRANSFER_DST_BIT | VK_BUFFER_USAGE_INDEX_BUFFER_BIT, VMA_MEMORY_USAGE_GPU_ONLY); + if (p_data.size()) { + uint64_t data_size = p_data.size(); + PoolVector<uint8_t>::Read r = p_data.read(); + _buffer_update(&index_buffer, 0, r.ptr(), 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 RenderingDeviceVulkan::index_array_create(RID p_index_buffer, uint32_t p_index_offset, uint32_t p_index_count) { + + _THREAD_SAFE_METHOD_ + + ERR_FAIL_COND_V(!index_buffer_owner.owns(p_index_buffer), RID()); + + IndexBuffer *index_buffer = index_buffer_owner.getornull(p_index_buffer); + + ERR_FAIL_COND_V(p_index_count == 0, RID()); + ERR_FAIL_COND_V(p_index_offset + p_index_count > index_buffer->index_count, RID()); + + IndexArray index_array; + index_array.max_index = index_buffer->max_index; + index_array.buffer = index_buffer->buffer; + index_array.offset = p_index_offset; + index_array.indices = p_index_count; + index_array.index_type = index_buffer->index_type; + index_array.supports_restart_indices = index_buffer->supports_restart_indices; + + RID id = index_array_owner.make_rid(index_array); + _add_dependency(id, p_index_buffer); + return id; +} + +/****************/ +/**** SHADER ****/ +/****************/ + +static const char *shader_stage_names[RenderingDevice::SHADER_STAGE_MAX] = { + "Vertex", + "Fragment", + "TesselationControl", + "TesselationEvaluation", + "Compute" +}; + +static const char *shader_uniform_names[RenderingDevice::UNIFORM_TYPE_MAX] = { + "Sampler", "CombinedSampler", "Texture", "Image", "TextureBuffer", "SamplerTextureBuffer", "ImageBuffer", "UniformBuffer", "StorageBuffer", "InputAttachment" +}; + +static VkShaderStageFlagBits shader_stage_masks[RenderingDevice::SHADER_STAGE_MAX] = { + VK_SHADER_STAGE_VERTEX_BIT, + VK_SHADER_STAGE_FRAGMENT_BIT, + VK_SHADER_STAGE_TESSELLATION_CONTROL_BIT, + VK_SHADER_STAGE_TESSELLATION_EVALUATION_BIT, + VK_SHADER_STAGE_COMPUTE_BIT, +}; + +String RenderingDeviceVulkan::_shader_uniform_debug(RID p_shader, int p_set) { + String ret; + const Shader *shader = shader_owner.getornull(p_shader); + ERR_FAIL_COND_V(!shader, String()); + for (int i = 0; i < shader->sets.size(); i++) { + if (p_set >= 0 && i != p_set) { + continue; + } + for (int j = 0; j < shader->sets[i].uniform_info.size(); j++) { + const UniformInfo &ui = shader->sets[i].uniform_info[j]; + if (ret != String()) { + ret += "\n"; + } + ret += "Set: " + itos(i) + " Binding: " + itos(ui.binding) + " Type: " + shader_uniform_names[ui.type] + " Length: " + itos(ui.length); + } + } + return ret; +} +#if 0 +bool RenderingDeviceVulkan::_uniform_add_binding(Vector<Vector<VkDescriptorSetLayoutBinding> > &bindings, Vector<Vector<UniformInfo> > &uniform_infos, const glslang::TObjectReflection &reflection, RenderingDevice::ShaderStage p_stage, Shader::PushConstant &push_constant, String *r_error) { + + VkDescriptorSetLayoutBinding layout_binding; + UniformInfo info; + + switch (reflection.getType()->getBasicType()) { + case glslang::EbtSampler: { + + //print_line("DEBUG: IsSampler"); + if (reflection.getType()->getSampler().dim == glslang::EsdBuffer) { + //texture buffers + if (reflection.getType()->getSampler().isCombined()) { + layout_binding.descriptorType = VK_DESCRIPTOR_TYPE_UNIFORM_TEXEL_BUFFER; + info.type = UNIFORM_TYPE_SAMPLER_WITH_TEXTURE_BUFFER; + //print_line("DEBUG: SAMPLER: texel combined"); + } else if (reflection.getType()->getSampler().isTexture()) { + layout_binding.descriptorType = VK_DESCRIPTOR_TYPE_UNIFORM_TEXEL_BUFFER; + info.type = UNIFORM_TYPE_TEXTURE_BUFFER; + //print_line("DEBUG: SAMPLER: texel alone"); + } else if (reflection.getType()->getSampler().isImage()) { + layout_binding.descriptorType = VK_DESCRIPTOR_TYPE_STORAGE_TEXEL_BUFFER; + info.type = UNIFORM_TYPE_IMAGE_BUFFER; + //print_line("DEBUG: SAMPLER: texel buffer"); + } else { + if (r_error) { + *r_error = "On shader stage '" + String(shader_stage_names[p_stage]) + "', uniform '" + reflection.name.c_str() + "' is of unsupported buffer type."; + } + return false; + } + } else if (reflection.getType()->getSampler().isCombined()) { + layout_binding.descriptorType = VK_DESCRIPTOR_TYPE_COMBINED_IMAGE_SAMPLER; + info.type = UNIFORM_TYPE_SAMPLER_WITH_TEXTURE; + //print_line("DEBUG: SAMPLER: combined"); + } else if (reflection.getType()->getSampler().isPureSampler()) { + layout_binding.descriptorType = VK_DESCRIPTOR_TYPE_SAMPLER; + info.type = UNIFORM_TYPE_SAMPLER; + //print_line("DEBUG: SAMPLER: sampler"); + } else if (reflection.getType()->getSampler().isTexture()) { + layout_binding.descriptorType = VK_DESCRIPTOR_TYPE_SAMPLED_IMAGE; + info.type = UNIFORM_TYPE_TEXTURE; + //print_line("DEBUG: SAMPLER: image"); + } else if (reflection.getType()->getSampler().isImage()) { + layout_binding.descriptorType = VK_DESCRIPTOR_TYPE_STORAGE_IMAGE; + info.type = UNIFORM_TYPE_IMAGE; + //print_line("DEBUG: SAMPLER: storage image"); + } else { + //print_line("DEBUG: sampler unknown"); + if (r_error) { + *r_error = "On shader stage '" + String(shader_stage_names[p_stage]) + "', uniform '" + reflection.name.c_str() + "' is of unsupported sampler type."; + } + return false; + } + + if (reflection.getType()->isArray()) { + layout_binding.descriptorCount = reflection.getType()->getArraySizes()->getCumulativeSize(); + //print_line("DEBUG: array of size: " + itos(layout_binding.descriptorCount)); + } else { + layout_binding.descriptorCount = 1; + } + + info.length = layout_binding.descriptorCount; + + } break; + /*case glslang::EbtStruct: { + print_line("DEBUG: Struct"); + + } break;*/ + case glslang::EbtBlock: { + //print_line("DEBUG: Block"); + if (reflection.getType()->getQualifier().storage == glslang::EvqUniform) { + if (reflection.getType()->getQualifier().layoutPushConstant) { + uint32_t len = reflection.size; + if (push_constant.push_constant_size != 0 && push_constant.push_constant_size != len) { + *r_error = "On shader stage '" + String(shader_stage_names[p_stage]) + "', uniform '" + reflection.name.c_str() + "' push constants for different stages should all be the same size."; + return false; + } + push_constant.push_constant_size = len; + push_constant.push_constants_vk_stage |= shader_stage_masks[p_stage]; + return true; + } + //print_line("DEBUG: Uniform buffer"); + layout_binding.descriptorType = VK_DESCRIPTOR_TYPE_UNIFORM_BUFFER; + info.type = UNIFORM_TYPE_UNIFORM_BUFFER; + } else if (reflection.getType()->getQualifier().storage == glslang::EvqBuffer) { + layout_binding.descriptorType = VK_DESCRIPTOR_TYPE_STORAGE_BUFFER; + info.type = UNIFORM_TYPE_STORAGE_BUFFER; + //print_line("DEBUG: Storage buffer"); + } else { + if (r_error) { + *r_error = "On shader stage '" + String(shader_stage_names[p_stage]) + "', uniform '" + reflection.name.c_str() + "' is of unsupported block type: (" + itos(reflection.getType()->getQualifier().storage) + ")."; + } + return false; + } + + if (reflection.getType()->isArray()) { + layout_binding.descriptorCount = reflection.getType()->getArraySizes()->getCumulativeSize(); + //print_line("DEBUG: array of size: " + itos(layout_binding.descriptorCount)); + } else { + layout_binding.descriptorCount = 1; + } + + info.length = reflection.size; + + } break; + /*case glslang::EbtReference: { + + } break;*/ + /*case glslang::EbtAtomicUint: { + + } break;*/ + default: { + + if (reflection.getType()->getQualifier().hasOffset() || reflection.name.find(".") != std::string::npos) { + //member of uniform block? + return true; + } + + if (r_error) { + *r_error = "On shader stage '" + String(shader_stage_names[p_stage]) + "', uniform '" + reflection.name.c_str() + "' unsupported uniform type."; + } + return false; + } + } + + if (!reflection.getType()->getQualifier().hasBinding()) { + if (r_error) { + *r_error = "On shader stage '" + String(shader_stage_names[p_stage]) + "', uniform '" + reflection.name.c_str() + "' lacks a binding number."; + } + return false; + } + + uint32_t set = reflection.getType()->getQualifier().hasSet() ? reflection.getType()->getQualifier().layoutSet : 0; + + if (set >= MAX_UNIFORM_SETS) { + if (r_error) { + *r_error = "On shader stage '" + String(shader_stage_names[p_stage]) + "', uniform '" + reflection.name.c_str() + "' uses a set (" + itos(set) + ") index larger than what is supported (" + itos(MAX_UNIFORM_SETS) + ")."; + } + return false; + } + + if (set >= limits.maxBoundDescriptorSets) { + if (r_error) { + *r_error = "On shader stage '" + String(shader_stage_names[p_stage]) + "', uniform '" + reflection.name.c_str() + "' uses a set (" + itos(set) + ") index larger than what is supported by the hardware (" + itos(limits.maxBoundDescriptorSets) + ")."; + } + return false; + } + + uint32_t binding = reflection.getType()->getQualifier().layoutBinding; + + if (set < (uint32_t)bindings.size()) { + //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 + 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.c_str() + "' trying to re-use location for set=" + itos(set) + ", binding=" + itos(binding) + " with different uniform type."; + } + return false; + } + + //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.c_str() + "' trying to re-use location for set=" + itos(set) + ", binding=" + itos(binding) + " with different uniform size."; + } + return false; + } + + //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; + } + } + } + layout_binding.binding = binding; + layout_binding.stageFlags = shader_stage_masks[p_stage]; + layout_binding.pImmutableSamplers = NULL; //no support for this yet + + info.stages = 1 << p_stage; + info.binding = binding; + + if (set >= (uint32_t)bindings.size()) { + bindings.resize(set + 1); + uniform_infos.resize(set + 1); + } +#if 0 + print_line("stage: " + String(shader_stage_names[p_stage]) + " set: " + itos(set) + " binding: " + itos(info.binding) + " type:" + shader_uniform_names[info.type] + " length: " + itos(info.length)); +#endif + bindings.write[set].push_back(layout_binding); + uniform_infos.write[set].push_back(info); + + return true; +} +#endif + +RID RenderingDeviceVulkan::shader_create(const Vector<ShaderStageData> &p_stages) { + + //descriptor layouts + Vector<Vector<VkDescriptorSetLayoutBinding> > set_bindings; + Vector<Vector<UniformInfo> > uniform_info; + Shader::PushConstant push_constant; + push_constant.push_constant_size = 0; + push_constant.push_constants_vk_stage = 0; + + uint32_t vertex_input_mask = 0; + + uint32_t fragment_outputs = 0; + + uint32_t stages_processed = 0; + + bool is_compute = false; + + for (int i = 0; i < p_stages.size(); i++) { + + if (p_stages[i].shader_stage == SHADER_STAGE_COMPUTE) { + is_compute = true; + ERR_FAIL_COND_V_MSG(p_stages.size() != 1, RID(), + "Compute shaders can only receive one stage, dedicated to compute."); + } + ERR_FAIL_COND_V_MSG(stages_processed & (1 << p_stages[i].shader_stage), RID(), + "Stage " + String(shader_stage_names[p_stages[i].shader_stage]) + " submitted more than once."); + + { + SpvReflectShaderModule module; + PoolVector<uint8_t>::Read spirv = p_stages[i].spir_v.read(); + SpvReflectResult result = spvReflectCreateShaderModule(p_stages[i].spir_v.size(), spirv.ptr(), &module); + ERR_FAIL_COND_V_MSG(result != SPV_REFLECT_RESULT_SUCCESS, RID(), + "Reflection of SPIR-V shader stage '" + String(shader_stage_names[p_stages[i].shader_stage]) + "' failed parsing shader."); + + uint32_t binding_count = 0; + result = spvReflectEnumerateDescriptorBindings(&module, &binding_count, NULL); + ERR_FAIL_COND_V_MSG(result != SPV_REFLECT_RESULT_SUCCESS, RID(), + "Reflection of SPIR-V shader stage '" + String(shader_stage_names[p_stages[i].shader_stage]) + "' failed enumerating descriptor bindings."); + + uint32_t stage = p_stages[i].shader_stage; + + if (binding_count > 0) { + + //Parse bindings + + Vector<SpvReflectDescriptorBinding *> bindings; + bindings.resize(binding_count); + result = spvReflectEnumerateDescriptorBindings(&module, &binding_count, bindings.ptrw()); + + ERR_FAIL_COND_V_MSG(result != SPV_REFLECT_RESULT_SUCCESS, RID(), + "Reflection of SPIR-V shader stage '" + String(shader_stage_names[p_stages[i].shader_stage]) + "' failed getting descriptor bindings."); + + for (uint32_t j = 0; j < binding_count; j++) { + const SpvReflectDescriptorBinding &binding = *bindings[j]; + + VkDescriptorSetLayoutBinding layout_binding; + UniformInfo info; + + bool need_array_dimensions = false; + bool need_block_size = false; + + switch (binding.descriptor_type) { + case SPV_REFLECT_DESCRIPTOR_TYPE_SAMPLER: { + layout_binding.descriptorType = VK_DESCRIPTOR_TYPE_SAMPLER; + info.type = UNIFORM_TYPE_SAMPLER; + need_array_dimensions = true; + } break; + case SPV_REFLECT_DESCRIPTOR_TYPE_COMBINED_IMAGE_SAMPLER: { + layout_binding.descriptorType = VK_DESCRIPTOR_TYPE_COMBINED_IMAGE_SAMPLER; + info.type = UNIFORM_TYPE_SAMPLER_WITH_TEXTURE; + need_array_dimensions = true; + } break; + case SPV_REFLECT_DESCRIPTOR_TYPE_SAMPLED_IMAGE: { + layout_binding.descriptorType = VK_DESCRIPTOR_TYPE_SAMPLED_IMAGE; + info.type = UNIFORM_TYPE_TEXTURE; + need_array_dimensions = true; + } break; + case SPV_REFLECT_DESCRIPTOR_TYPE_STORAGE_IMAGE: { + layout_binding.descriptorType = VK_DESCRIPTOR_TYPE_STORAGE_IMAGE; + info.type = UNIFORM_TYPE_IMAGE; + need_array_dimensions = true; + } break; + case SPV_REFLECT_DESCRIPTOR_TYPE_UNIFORM_TEXEL_BUFFER: { + layout_binding.descriptorType = VK_DESCRIPTOR_TYPE_UNIFORM_TEXEL_BUFFER; + info.type = UNIFORM_TYPE_TEXTURE_BUFFER; + need_array_dimensions = true; + } break; + case SPV_REFLECT_DESCRIPTOR_TYPE_STORAGE_TEXEL_BUFFER: { + layout_binding.descriptorType = VK_DESCRIPTOR_TYPE_STORAGE_TEXEL_BUFFER; + info.type = UNIFORM_TYPE_IMAGE_BUFFER; + need_array_dimensions = true; + } break; + case SPV_REFLECT_DESCRIPTOR_TYPE_UNIFORM_BUFFER: { + layout_binding.descriptorType = VK_DESCRIPTOR_TYPE_UNIFORM_BUFFER; + info.type = UNIFORM_TYPE_UNIFORM_BUFFER; + need_block_size = true; + } break; + case SPV_REFLECT_DESCRIPTOR_TYPE_STORAGE_BUFFER: { + layout_binding.descriptorType = VK_DESCRIPTOR_TYPE_STORAGE_BUFFER; + info.type = UNIFORM_TYPE_STORAGE_BUFFER; + need_block_size = true; + } break; + case SPV_REFLECT_DESCRIPTOR_TYPE_UNIFORM_BUFFER_DYNAMIC: { + ERR_PRINT("Dynamic uniform buffer not supported."); + continue; + } break; + case SPV_REFLECT_DESCRIPTOR_TYPE_STORAGE_BUFFER_DYNAMIC: { + ERR_PRINT("Dynamic storage buffer not supported."); + continue; + } break; + case SPV_REFLECT_DESCRIPTOR_TYPE_INPUT_ATTACHMENT: { + layout_binding.descriptorType = VK_DESCRIPTOR_TYPE_INPUT_ATTACHMENT; + info.type = UNIFORM_TYPE_INPUT_ATTACHMENT; + } break; + } + + if (need_array_dimensions) { + if (binding.array.dims_count == 0) { + info.length = 1; + } else { + for (uint32_t k = 0; k < binding.array.dims_count; k++) { + if (k == 0) { + info.length = binding.array.dims[0]; + } else { + info.length *= binding.array.dims[k]; + } + } + } + + layout_binding.descriptorCount = info.length; + + } else if (need_block_size) { + info.length = binding.block.size; + layout_binding.descriptorCount = 1; + } else { + info.length = 0; + layout_binding.descriptorCount = 1; + } + + info.binding = binding.binding; + uint32_t set = binding.set; + + //print_line("Stage: " + String(shader_stage_names[stage]) + " set=" + itos(set) + " binding=" + itos(info.binding) + " type=" + shader_uniform_names[info.type] + " length=" + itos(info.length)); + + ERR_FAIL_COND_V_MSG(set >= MAX_UNIFORM_SETS, RID(), + "On shader stage '" + String(shader_stage_names[stage]) + "', uniform '" + binding.name + "' uses a set (" + itos(set) + ") index larger than what is supported (" + itos(MAX_UNIFORM_SETS) + ")."); + + ERR_FAIL_COND_V_MSG(set >= limits.maxBoundDescriptorSets, RID(), + "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)set_bindings.size()) { + //check if this already exists + bool exists = false; + for (int k = 0; k < set_bindings[set].size(); k++) { + if (set_bindings[set][k].binding == (uint32_t)info.binding) { + //already exists, verify that it's the same type + ERR_FAIL_COND_V_MSG(set_bindings[set][k].descriptorType != layout_binding.descriptorType, RID(), + "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 + ERR_FAIL_COND_V_MSG(set_bindings[set][k].descriptorCount != layout_binding.descriptorCount || uniform_info[set][k].length != info.length, RID(), + "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 + set_bindings.write[set].write[k].stageFlags |= shader_stage_masks[stage]; + uniform_info.write[set].write[k].stages |= 1 << stage; + exists = true; + } + } + + if (exists) { + continue; //merged + } + } + + layout_binding.binding = info.binding; + layout_binding.stageFlags = shader_stage_masks[stage]; + layout_binding.pImmutableSamplers = NULL; //no support for this yet + + info.stages = 1 << stage; + info.binding = info.binding; + + if (set >= (uint32_t)set_bindings.size()) { + set_bindings.resize(set + 1); + uniform_info.resize(set + 1); + } + + set_bindings.write[set].push_back(layout_binding); + uniform_info.write[set].push_back(info); + } + } + + if (stage == SHADER_STAGE_VERTEX) { + + uint32_t iv_count = 0; + result = spvReflectEnumerateInputVariables(&module, &iv_count, NULL); + ERR_FAIL_COND_V_MSG(result != SPV_REFLECT_RESULT_SUCCESS, RID(), + "Reflection of SPIR-V shader stage '" + String(shader_stage_names[p_stages[i].shader_stage]) + "' failed enumerating input variables."); + + if (iv_count) { + Vector<SpvReflectInterfaceVariable *> input_vars; + input_vars.resize(iv_count); + + result = spvReflectEnumerateInputVariables(&module, &iv_count, input_vars.ptrw()); + ERR_FAIL_COND_V_MSG(result != SPV_REFLECT_RESULT_SUCCESS, RID(), + "Reflection of SPIR-V shader stage '" + String(shader_stage_names[p_stages[i].shader_stage]) + "' failed obtaining input variables."); + + for (uint32_t j = 0; j < iv_count; j++) { + if (input_vars[j] && input_vars[j]->decoration_flags == 0) { //regular input + vertex_input_mask |= (1 << uint32_t(input_vars[j]->location)); + } + } + } + } + + if (stage == SHADER_STAGE_FRAGMENT) { + + uint32_t ov_count = 0; + result = spvReflectEnumerateOutputVariables(&module, &ov_count, NULL); + ERR_FAIL_COND_V_MSG(result != SPV_REFLECT_RESULT_SUCCESS, RID(), + "Reflection of SPIR-V shader stage '" + String(shader_stage_names[p_stages[i].shader_stage]) + "' failed enumerating output variables."); + + if (ov_count) { + Vector<SpvReflectInterfaceVariable *> output_vars; + output_vars.resize(ov_count); + + result = spvReflectEnumerateOutputVariables(&module, &ov_count, output_vars.ptrw()); + ERR_FAIL_COND_V_MSG(result != SPV_REFLECT_RESULT_SUCCESS, RID(), + "Reflection of SPIR-V shader stage '" + String(shader_stage_names[p_stages[i].shader_stage]) + "' failed obtaining output variables."); + + for (uint32_t j = 0; j < ov_count; j++) { + if (output_vars[j]) { + fragment_outputs = MAX(fragment_outputs, output_vars[j]->location + 1); + } + } + } + } + uint32_t pc_count = 0; + result = spvReflectEnumeratePushConstantBlocks(&module, &pc_count, NULL); + ERR_FAIL_COND_V_MSG(result != SPV_REFLECT_RESULT_SUCCESS, RID(), + "Reflection of SPIR-V shader stage '" + String(shader_stage_names[p_stages[i].shader_stage]) + "' failed enumerating push constants."); + + if (pc_count) { + ERR_FAIL_COND_V_MSG(pc_count > 1, RID(), + "Reflection of SPIR-V shader stage '" + String(shader_stage_names[p_stages[i].shader_stage]) + "': Only one push constant is supported, which should be the same across shader stages."); + + Vector<SpvReflectBlockVariable *> pconstants; + pconstants.resize(pc_count); + result = spvReflectEnumeratePushConstantBlocks(&module, &pc_count, pconstants.ptrw()); + ERR_FAIL_COND_V_MSG(result != SPV_REFLECT_RESULT_SUCCESS, RID(), + "Reflection of SPIR-V shader stage '" + String(shader_stage_names[p_stages[i].shader_stage]) + "' failed obtaining push constants."); +#if 0 + if (pconstants[0] == NULL) { + FileAccess *f = FileAccess::open("res://popo.spv", FileAccess::WRITE); + f->store_buffer((const uint8_t *)&SpirV[0], SpirV.size() * sizeof(uint32_t)); + memdelete(f); + } +#endif + + ERR_FAIL_COND_V_MSG(push_constant.push_constant_size && push_constant.push_constant_size != pconstants[0]->size, RID(), + "Reflection of SPIR-V shader stage '" + String(shader_stage_names[p_stages[i].shader_stage]) + "': Push constant block must be the same across shader stages."); + + push_constant.push_constant_size = pconstants[0]->size; + push_constant.push_constants_vk_stage |= shader_stage_masks[stage]; + + //print_line("Stage: " + String(shader_stage_names[stage]) + " push constant of size=" + itos(push_constant.push_constant_size)); + } + + // Destroy the reflection data when no longer required. + spvReflectDestroyShaderModule(&module); + } + + stages_processed |= (1 << p_stages[i].shader_stage); + } + + //all good, let's create modules + + _THREAD_SAFE_METHOD_ + + Shader shader; + + shader.vertex_input_mask = vertex_input_mask; + shader.fragment_outputs = fragment_outputs; + shader.push_constant = push_constant; + shader.is_compute = is_compute; + + String error_text; + + bool success = true; + for (int i = 0; i < p_stages.size(); i++) { + VkShaderModuleCreateInfo shader_module_create_info; + shader_module_create_info.sType = VK_STRUCTURE_TYPE_SHADER_MODULE_CREATE_INFO; + shader_module_create_info.pNext = NULL; + shader_module_create_info.flags = 0; + shader_module_create_info.codeSize = p_stages[i].spir_v.size(); + PoolVector<uint8_t>::Read r = p_stages[i].spir_v.read(); + + shader_module_create_info.pCode = (const uint32_t *)r.ptr(); + + VkShaderModule module; + VkResult res = vkCreateShaderModule(device, &shader_module_create_info, NULL, &module); + if (res) { + success = false; + error_text = "Error creating shader module for stage: " + String(shader_stage_names[p_stages[i].shader_stage]); + break; + } + + const VkShaderStageFlagBits shader_stage_bits[SHADER_STAGE_MAX] = { + VK_SHADER_STAGE_VERTEX_BIT, + VK_SHADER_STAGE_FRAGMENT_BIT, + VK_SHADER_STAGE_TESSELLATION_CONTROL_BIT, + VK_SHADER_STAGE_TESSELLATION_EVALUATION_BIT, + VK_SHADER_STAGE_COMPUTE_BIT, + }; + + VkPipelineShaderStageCreateInfo shader_stage; + shader_stage.sType = VK_STRUCTURE_TYPE_PIPELINE_SHADER_STAGE_CREATE_INFO; + shader_stage.pNext = NULL; + shader_stage.flags = 0; + shader_stage.stage = shader_stage_bits[p_stages[i].shader_stage]; + shader_stage.module = module; + shader_stage.pName = "main"; + shader_stage.pSpecializationInfo = NULL; + + shader.pipeline_stages.push_back(shader_stage); + } + //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) + VkDescriptorSetLayoutCreateInfo layout_create_info; + layout_create_info.sType = VK_STRUCTURE_TYPE_DESCRIPTOR_SET_LAYOUT_CREATE_INFO; + layout_create_info.pNext = NULL; + layout_create_info.flags = 0; + layout_create_info.bindingCount = set_bindings[i].size(); + layout_create_info.pBindings = set_bindings[i].ptr(); + + VkDescriptorSetLayout layout; + VkResult res = vkCreateDescriptorSetLayout(device, &layout_create_info, NULL, &layout); + if (res) { + error_text = "Error creating descriptor set layout for set " + itos(i); + success = false; + break; + } + + Shader::Set set; + set.descriptor_set_layout = layout; + set.uniform_info = uniform_info[i]; + //sort and hash + set.uniform_info.sort(); + + uint32_t format = 0; //no format, default + + if (set.uniform_info.size()) { + //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); + if (E) { + format = E->get(); + } else { + format = uniform_set_format_cache.size() + 1; + uniform_set_format_cache.insert(usformat, format); + } + } + + shader.sets.push_back(set); + shader.set_formats.push_back(format); + } + } + + if (success) { + //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 = NULL; + pipeline_layout_create_info.flags = 0; + pipeline_layout_create_info.setLayoutCount = shader.sets.size(); + + Vector<VkDescriptorSetLayout> layouts; + layouts.resize(shader.sets.size()); + + for (int i = 0; i < layouts.size(); i++) { + layouts.write[i] = shader.sets[i].descriptor_set_layout; + } + + pipeline_layout_create_info.pSetLayouts = layouts.ptr(); + if (push_constant.push_constant_size) { + VkPushConstantRange push_constant_range; + push_constant_range.stageFlags = push_constant.push_constants_vk_stage; + push_constant_range.offset = 0; + push_constant_range.size = push_constant.push_constant_size; + + pipeline_layout_create_info.pushConstantRangeCount = 1; + pipeline_layout_create_info.pPushConstantRanges = &push_constant_range; + } else { + pipeline_layout_create_info.pushConstantRangeCount = 0; + pipeline_layout_create_info.pPushConstantRanges = NULL; + } + + VkResult err = vkCreatePipelineLayout(device, &pipeline_layout_create_info, NULL, &shader.pipeline_layout); + + if (err) { + error_text = "Error creating pipeline layout."; + success = false; + } + } + + if (!success) { + //clean up if failed + for (int i = 0; i < shader.pipeline_stages.size(); i++) { + vkDestroyShaderModule(device, shader.pipeline_stages[i].module, NULL); + } + + for (int i = 0; i < shader.sets.size(); i++) { + vkDestroyDescriptorSetLayout(device, shader.sets[i].descriptor_set_layout, NULL); + } + + ERR_FAIL_V_MSG(RID(), error_text); + } + + return shader_owner.make_rid(shader); +} + +uint32_t RenderingDeviceVulkan::shader_get_vertex_input_attribute_mask(RID p_shader) { + _THREAD_SAFE_METHOD_ + + const Shader *shader = shader_owner.getornull(p_shader); + ERR_FAIL_COND_V(!shader, 0); + return shader->vertex_input_mask; +} + +/******************/ +/**** UNIFORMS ****/ +/******************/ + +RID RenderingDeviceVulkan::uniform_buffer_create(uint32_t p_size_bytes, const PoolVector<uint8_t> &p_data) { + + _THREAD_SAFE_METHOD_ + + ERR_FAIL_COND_V(p_data.size() && (uint32_t)p_data.size() != p_size_bytes, RID()); + + Buffer buffer; + Error err = _buffer_allocate(&buffer, p_size_bytes, VK_BUFFER_USAGE_TRANSFER_DST_BIT | VK_BUFFER_USAGE_UNIFORM_BUFFER_BIT, VMA_MEMORY_USAGE_GPU_ONLY); + ERR_FAIL_COND_V(err != OK, RID()); + if (p_data.size()) { + uint64_t data_size = p_data.size(); + PoolVector<uint8_t>::Read r = p_data.read(); + _buffer_update(&buffer, 0, r.ptr(), 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 RenderingDeviceVulkan::storage_buffer_create(uint32_t p_size_bytes, const PoolVector<uint8_t> &p_data) { + + _THREAD_SAFE_METHOD_ + + ERR_FAIL_COND_V(p_data.size() && (uint32_t)p_data.size() != p_size_bytes, RID()); + + Buffer buffer; + Error err = _buffer_allocate(&buffer, p_size_bytes, VK_BUFFER_USAGE_TRANSFER_SRC_BIT | VK_BUFFER_USAGE_TRANSFER_DST_BIT | VK_BUFFER_USAGE_STORAGE_BUFFER_BIT, VMA_MEMORY_USAGE_GPU_ONLY); + ERR_FAIL_COND_V(err != OK, RID()); + + if (p_data.size()) { + uint64_t data_size = p_data.size(); + PoolVector<uint8_t>::Read r = p_data.read(); + _buffer_update(&buffer, 0, r.ptr(), 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_SHADER_READ_BIT | VK_ACCESS_SHADER_WRITE_BIT, false); + } + return storage_buffer_owner.make_rid(buffer); +} + +RID RenderingDeviceVulkan::texture_buffer_create(uint32_t p_size_elements, DataFormat p_format, const PoolVector<uint8_t> &p_data) { + + _THREAD_SAFE_METHOD_ + + uint32_t element_size = get_format_vertex_size(p_format); + ERR_FAIL_COND_V_MSG(element_size == 0, RID(), "Format requested is not supported for texture buffers"); + uint64_t size_bytes = uint64_t(element_size) * p_size_elements; + + ERR_FAIL_COND_V(p_data.size() && (uint32_t)p_data.size() != size_bytes, RID()); + + TextureBuffer texture_buffer; + Error err = _buffer_allocate(&texture_buffer.buffer, size_bytes, VK_BUFFER_USAGE_TRANSFER_SRC_BIT | VK_BUFFER_USAGE_TRANSFER_DST_BIT | VK_BUFFER_USAGE_UNIFORM_TEXEL_BUFFER_BIT, VMA_MEMORY_USAGE_GPU_ONLY); + ERR_FAIL_COND_V(err != OK, RID()); + + if (p_data.size()) { + uint64_t data_size = p_data.size(); + PoolVector<uint8_t>::Read r = p_data.read(); + _buffer_update(&texture_buffer.buffer, 0, r.ptr(), data_size); + _buffer_memory_barrier(texture_buffer.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_SHADER_READ_BIT, false); + } + + VkBufferViewCreateInfo view_create_info; + view_create_info.sType = VK_STRUCTURE_TYPE_BUFFER_VIEW_CREATE_INFO; + view_create_info.pNext = NULL; + view_create_info.flags = 0; + view_create_info.buffer = texture_buffer.buffer.buffer; + view_create_info.format = vulkan_formats[p_format]; + view_create_info.offset = 0; + view_create_info.range = size_bytes; + + texture_buffer.view = VK_NULL_HANDLE; + + VkResult res = vkCreateBufferView(device, &view_create_info, NULL, &texture_buffer.view); + if (res) { + _buffer_free(&texture_buffer.buffer); + ERR_FAIL_V_MSG(RID(), "Unable to create buffer view"); + } + + //allocate the view + return texture_buffer_owner.make_rid(texture_buffer); +} + +RenderingDeviceVulkan::DescriptorPool *RenderingDeviceVulkan::_descriptor_pool_allocate(const DescriptorPoolKey &p_key) { + if (!descriptor_pools.has(p_key)) { + descriptor_pools[p_key] = Set<DescriptorPool *>(); + } + + DescriptorPool *pool = NULL; + + 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(); + break; + } + } + + if (!pool) { + //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 = NULL; + 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 + + if (p_key.uniform_type[UNIFORM_TYPE_SAMPLER]) { + VkDescriptorPoolSize s; + s.type = VK_DESCRIPTOR_TYPE_SAMPLER; + s.descriptorCount = p_key.uniform_type[UNIFORM_TYPE_SAMPLER] * max_descriptors_per_pool; + sizes.push_back(s); + } + if (p_key.uniform_type[UNIFORM_TYPE_SAMPLER_WITH_TEXTURE]) { + VkDescriptorPoolSize s; + s.type = VK_DESCRIPTOR_TYPE_COMBINED_IMAGE_SAMPLER; + s.descriptorCount = p_key.uniform_type[UNIFORM_TYPE_SAMPLER_WITH_TEXTURE] * max_descriptors_per_pool; + sizes.push_back(s); + } + if (p_key.uniform_type[UNIFORM_TYPE_TEXTURE]) { + VkDescriptorPoolSize s; + s.type = VK_DESCRIPTOR_TYPE_SAMPLED_IMAGE; + s.descriptorCount = p_key.uniform_type[UNIFORM_TYPE_TEXTURE] * max_descriptors_per_pool; + sizes.push_back(s); + } + if (p_key.uniform_type[UNIFORM_TYPE_IMAGE]) { + VkDescriptorPoolSize s; + s.type = VK_DESCRIPTOR_TYPE_STORAGE_IMAGE; + s.descriptorCount = p_key.uniform_type[UNIFORM_TYPE_IMAGE] * max_descriptors_per_pool; + sizes.push_back(s); + } + if (p_key.uniform_type[UNIFORM_TYPE_TEXTURE_BUFFER] || p_key.uniform_type[UNIFORM_TYPE_SAMPLER_WITH_TEXTURE_BUFFER]) { + VkDescriptorPoolSize s; + s.type = VK_DESCRIPTOR_TYPE_UNIFORM_TEXEL_BUFFER; + s.descriptorCount = (p_key.uniform_type[UNIFORM_TYPE_TEXTURE_BUFFER] + p_key.uniform_type[UNIFORM_TYPE_SAMPLER_WITH_TEXTURE_BUFFER]) * max_descriptors_per_pool; + sizes.push_back(s); + } + if (p_key.uniform_type[UNIFORM_TYPE_IMAGE_BUFFER]) { + VkDescriptorPoolSize s; + s.type = VK_DESCRIPTOR_TYPE_STORAGE_TEXEL_BUFFER; + s.descriptorCount = p_key.uniform_type[UNIFORM_TYPE_IMAGE_BUFFER] * max_descriptors_per_pool; + sizes.push_back(s); + } + if (p_key.uniform_type[UNIFORM_TYPE_UNIFORM_BUFFER]) { + VkDescriptorPoolSize s; + s.type = VK_DESCRIPTOR_TYPE_UNIFORM_BUFFER; + s.descriptorCount = p_key.uniform_type[UNIFORM_TYPE_UNIFORM_BUFFER] * max_descriptors_per_pool; + sizes.push_back(s); + } + + if (p_key.uniform_type[UNIFORM_TYPE_STORAGE_BUFFER]) { + VkDescriptorPoolSize s; + s.type = VK_DESCRIPTOR_TYPE_STORAGE_BUFFER; + s.descriptorCount = p_key.uniform_type[UNIFORM_TYPE_STORAGE_BUFFER] * max_descriptors_per_pool; + sizes.push_back(s); + } + + if (p_key.uniform_type[UNIFORM_TYPE_INPUT_ATTACHMENT]) { + VkDescriptorPoolSize s; + s.type = VK_DESCRIPTOR_TYPE_INPUT_ATTACHMENT; + s.descriptorCount = p_key.uniform_type[UNIFORM_TYPE_INPUT_ATTACHMENT] * max_descriptors_per_pool; + sizes.push_back(s); + } + + descriptor_pool_create_info.poolSizeCount = sizes.size(); + descriptor_pool_create_info.pPoolSizes = sizes.ptr(); + VkResult res = vkCreateDescriptorPool(device, &descriptor_pool_create_info, NULL, &pool->pool); + ERR_FAIL_COND_V(res, NULL); + descriptor_pools[p_key].insert(pool); + } + + pool->usage++; + + return pool; +} + +void RenderingDeviceVulkan::_descriptor_pool_free(const DescriptorPoolKey &p_key, DescriptorPool *p_pool) { +#ifdef DEBUG_ENABLED + ERR_FAIL_COND(!descriptor_pools[p_key].has(p_pool)); +#endif + ERR_FAIL_COND(p_pool->usage == 0); + p_pool->usage--; + if (p_pool->usage == 0) { + vkDestroyDescriptorPool(device, p_pool->pool, NULL); + descriptor_pools[p_key].erase(p_pool); + memdelete(p_pool); + if (descriptor_pools[p_key].empty()) { + descriptor_pools.erase(p_key); + } + } +} + +RID RenderingDeviceVulkan::uniform_set_create(const Vector<Uniform> &p_uniforms, RID p_shader, uint32_t p_shader_set) { + + _THREAD_SAFE_METHOD_ + + ERR_FAIL_COND_V(p_uniforms.size() == 0, RID()); + + Shader *shader = shader_owner.getornull(p_shader); + ERR_FAIL_COND_V(!shader, RID()); + + 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 + + const Shader::Set &set = shader->sets[p_shader_set]; + + uint32_t uniform_count = p_uniforms.size(); + const Uniform *uniforms = p_uniforms.ptr(); + + uint32_t set_uniform_count = set.uniform_info.size(); + const UniformInfo *set_uniforms = set.uniform_info.ptr(); + + Vector<VkWriteDescriptorSet> writes; + DescriptorPoolKey pool_key; + + //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 + Vector<RID> attachable_textures; + Vector<Texture *> mutable_sampled_textures; + Vector<Texture *> mutable_storage_textures; + + for (uint32_t i = 0; i < set_uniform_count; i++) { + const UniformInfo &set_uniform = set_uniforms[i]; + int uniform_idx = -1; + for (int j = 0; j < (int)uniform_count; j++) { + if (uniforms[j].binding == set_uniform.binding) { + uniform_idx = j; + } + } + ERR_FAIL_COND_V_MSG(uniform_idx == -1, RID(), + "All the shader bindings for the given set must be covered by the uniforms provided."); + + const Uniform &uniform = uniforms[uniform_idx]; + + ERR_FAIL_COND_V_MSG(uniform.type != set_uniform.type, RID(), + "Mismatch uniform type for binding (" + itos(set_uniform.binding) + "). Expected '" + shader_uniform_names[set_uniform.type] + "', supplied: '" + shader_uniform_names[uniform.type] + "'."); + + VkWriteDescriptorSet write; //common header + write.sType = VK_STRUCTURE_TYPE_WRITE_DESCRIPTOR_SET; + write.pNext = NULL; + write.dstSet = NULL; //will assign afterwards when everything is valid + write.dstBinding = set_uniform.binding; + uint32_t type_size = 1; + + switch (uniform.type) { + case UNIFORM_TYPE_SAMPLER: { + if (uniform.ids.size() != set_uniform.length) { + if (set_uniform.length > 1) { + ERR_FAIL_V_MSG(RID(), "Sampler (binding: " + itos(uniform.binding) + ") is an array of (" + itos(set_uniform.length) + ") sampler elements, so it should be provided equal number of sampler IDs to satisfy it (IDs provided: " + itos(uniform.ids.size()) + ")."); + } else { + ERR_FAIL_V_MSG(RID(), "Sampler (binding: " + itos(uniform.binding) + ") should provide one ID referencing a sampler (IDs provided: " + itos(uniform.ids.size()) + ")."); + } + } + + Vector<VkDescriptorImageInfo> image_info; + + for (int j = 0; j < uniform.ids.size(); j++) { + VkSampler *sampler = sampler_owner.getornull(uniform.ids[j]); + ERR_FAIL_COND_V_MSG(!sampler, RID(), "Sampler (binding: " + itos(uniform.binding) + ", index " + itos(j) + ") is not a valid sampler."); + + VkDescriptorImageInfo img_info; + img_info.sampler = *sampler; + img_info.imageView = VK_NULL_HANDLE; + img_info.imageLayout = VK_IMAGE_LAYOUT_UNDEFINED; + + image_info.push_back(img_info); + } + + write.dstArrayElement = 0; + write.descriptorCount = uniform.ids.size(); + write.descriptorType = VK_DESCRIPTOR_TYPE_SAMPLER; + write.pImageInfo = image_infos.push_back(image_info)->get().ptr(); + write.pBufferInfo = NULL; + write.pTexelBufferView = NULL; + + type_size = uniform.ids.size(); + + } break; + case UNIFORM_TYPE_SAMPLER_WITH_TEXTURE: { + + if (uniform.ids.size() != set_uniform.length * 2) { + if (set_uniform.length > 1) { + ERR_FAIL_V_MSG(RID(), "SamplerTexture (binding: " + itos(uniform.binding) + ") is an array of (" + itos(set_uniform.length) + ") sampler&texture elements, so it should provided twice the amount of IDs (sampler,texture pairs) to satisfy it (IDs provided: " + itos(uniform.ids.size()) + ")."); + } else { + ERR_FAIL_V_MSG(RID(), "SamplerTexture (binding: " + itos(uniform.binding) + ") should provide two IDs referencing a sampler and then a texture (IDs provided: " + itos(uniform.ids.size()) + ")."); + } + } + + Vector<VkDescriptorImageInfo> image_info; + + for (int j = 0; j < uniform.ids.size(); j += 2) { + VkSampler *sampler = sampler_owner.getornull(uniform.ids[j + 0]); + ERR_FAIL_COND_V_MSG(!sampler, RID(), "SamplerBuffer (binding: " + itos(uniform.binding) + ", index " + itos(j + 1) + ") is not a valid sampler."); + + Texture *texture = texture_owner.getornull(uniform.ids[j + 1]); + ERR_FAIL_COND_V_MSG(!texture, RID(), "Texture (binding: " + itos(uniform.binding) + ", index " + itos(j) + ") is not a valid texture."); + + ERR_FAIL_COND_V_MSG(!(texture->usage_flags & TEXTURE_USAGE_SAMPLING_BIT), RID(), + "Texture (binding: " + itos(uniform.binding) + ", index " + itos(j) + ") needs the TEXTURE_USAGE_SAMPLING_BIT usage flag set in order to be used as uniform."); + + VkDescriptorImageInfo img_info; + img_info.sampler = *sampler; + img_info.imageView = texture->view; + + if (texture->usage_flags & (TEXTURE_USAGE_COLOR_ATTACHMENT_BIT | TEXTURE_USAGE_DEPTH_STENCIL_ATTACHMENT_BIT | TEXTURE_USAGE_RESOLVE_ATTACHMENT_BIT)) { + attachable_textures.push_back(texture->owner.is_valid() ? texture->owner : uniform.ids[j + 1]); + } + + if (texture->owner.is_valid()) { + texture = texture_owner.getornull(texture->owner); + ERR_FAIL_COND_V(!texture, RID()); //bug, should never happen + } + + img_info.imageLayout = VK_IMAGE_LAYOUT_SHADER_READ_ONLY_OPTIMAL; + + image_info.push_back(img_info); + + if (texture->usage_flags & TEXTURE_USAGE_STORAGE_BIT) { + //can also be used as storage, add to mutable sampled + mutable_sampled_textures.push_back(texture); + } + } + + write.dstArrayElement = 0; + write.descriptorCount = uniform.ids.size() / 2; + write.descriptorType = VK_DESCRIPTOR_TYPE_COMBINED_IMAGE_SAMPLER; + write.pImageInfo = image_infos.push_back(image_info)->get().ptr(); + write.pBufferInfo = NULL; + write.pTexelBufferView = NULL; + + type_size = uniform.ids.size() / 2; + + } break; + case UNIFORM_TYPE_TEXTURE: { + + if (uniform.ids.size() != set_uniform.length) { + if (set_uniform.length > 1) { + ERR_FAIL_V_MSG(RID(), "Texture (binding: " + itos(uniform.binding) + ") is an array of (" + itos(set_uniform.length) + ") textures, so it should be provided equal number of texture IDs to satisfy it (IDs provided: " + itos(uniform.ids.size()) + ")."); + } else { + ERR_FAIL_V_MSG(RID(), "Texture (binding: " + itos(uniform.binding) + ") should provide one ID referencing a texture (IDs provided: " + itos(uniform.ids.size()) + ")."); + } + } + + Vector<VkDescriptorImageInfo> image_info; + + for (int j = 0; j < uniform.ids.size(); j++) { + Texture *texture = texture_owner.getornull(uniform.ids[j]); + ERR_FAIL_COND_V_MSG(!texture, RID(), "Texture (binding: " + itos(uniform.binding) + ", index " + itos(j) + ") is not a valid texture."); + + ERR_FAIL_COND_V_MSG(!(texture->usage_flags & TEXTURE_USAGE_SAMPLING_BIT), RID(), + "Texture (binding: " + itos(uniform.binding) + ", index " + itos(j) + ") needs the TEXTURE_USAGE_SAMPLING_BIT usage flag set in order to be used as uniform."); + + VkDescriptorImageInfo img_info; + img_info.sampler = NULL; + img_info.imageView = texture->view; + + if (texture->usage_flags & (TEXTURE_USAGE_COLOR_ATTACHMENT_BIT | TEXTURE_USAGE_DEPTH_STENCIL_ATTACHMENT_BIT | TEXTURE_USAGE_RESOLVE_ATTACHMENT_BIT)) { + attachable_textures.push_back(texture->owner.is_valid() ? texture->owner : uniform.ids[j]); + } + + if (texture->owner.is_valid()) { + texture = texture_owner.getornull(texture->owner); + ERR_FAIL_COND_V(!texture, RID()); //bug, should never happen + } + + img_info.imageLayout = VK_IMAGE_LAYOUT_SHADER_READ_ONLY_OPTIMAL; + + image_info.push_back(img_info); + + if (texture->usage_flags & TEXTURE_USAGE_STORAGE_BIT) { + //can also be used as storage, add to mutable sampled + mutable_sampled_textures.push_back(texture); + } + } + + write.dstArrayElement = 0; + write.descriptorCount = uniform.ids.size(); + write.descriptorType = VK_DESCRIPTOR_TYPE_SAMPLED_IMAGE; + write.pImageInfo = image_infos.push_back(image_info)->get().ptr(); + write.pBufferInfo = NULL; + write.pTexelBufferView = NULL; + + type_size = uniform.ids.size(); + } break; + case UNIFORM_TYPE_IMAGE: { + + if (uniform.ids.size() != set_uniform.length) { + if (set_uniform.length > 1) { + ERR_FAIL_V_MSG(RID(), "Image (binding: " + itos(uniform.binding) + ") is an array of (" + itos(set_uniform.length) + ") textures, so it should be provided equal number of texture IDs to satisfy it (IDs provided: " + itos(uniform.ids.size()) + ")."); + } else { + ERR_FAIL_V_MSG(RID(), "Image (binding: " + itos(uniform.binding) + ") should provide one ID referencing a texture (IDs provided: " + itos(uniform.ids.size()) + ")."); + } + } + + Vector<VkDescriptorImageInfo> image_info; + + for (int j = 0; j < uniform.ids.size(); j++) { + Texture *texture = texture_owner.getornull(uniform.ids[j]); + + ERR_FAIL_COND_V_MSG(!texture, RID(), + "Image (binding: " + itos(uniform.binding) + ", index " + itos(j) + ") is not a valid texture."); + + ERR_FAIL_COND_V_MSG(!(texture->usage_flags & TEXTURE_USAGE_STORAGE_BIT), RID(), + "Image (binding: " + itos(uniform.binding) + ", index " + itos(j) + ") needs the TEXTURE_USAGE_STORAGE_BIT usage flag set in order to be used as uniform."); + + VkDescriptorImageInfo img_info; + img_info.sampler = NULL; + img_info.imageView = texture->view; + + if (texture->owner.is_valid()) { + texture = texture_owner.getornull(texture->owner); + ERR_FAIL_COND_V(!texture, RID()); //bug, should never happen + } + + img_info.imageLayout = VK_IMAGE_LAYOUT_GENERAL; + + image_info.push_back(img_info); + + if (texture->usage_flags & TEXTURE_USAGE_SAMPLING_BIT) { + //can also be used as storage, add to mutable sampled + mutable_storage_textures.push_back(texture); + } + } + + write.dstArrayElement = 0; + write.descriptorCount = uniform.ids.size(); + write.descriptorType = VK_DESCRIPTOR_TYPE_STORAGE_IMAGE; + write.pImageInfo = image_infos.push_back(image_info)->get().ptr(); + write.pBufferInfo = NULL; + write.pTexelBufferView = NULL; + + type_size = uniform.ids.size(); + + } break; + case UNIFORM_TYPE_TEXTURE_BUFFER: { + if (uniform.ids.size() != set_uniform.length) { + if (set_uniform.length > 1) { + ERR_FAIL_V_MSG(RID(), "Buffer (binding: " + itos(uniform.binding) + ") is an array of (" + itos(set_uniform.length) + ") texture buffer elements, so it should be provided equal number of texture buffer IDs to satisfy it (IDs provided: " + itos(uniform.ids.size()) + ")."); + } else { + ERR_FAIL_V_MSG(RID(), "Buffer (binding: " + itos(uniform.binding) + ") should provide one ID referencing a texture buffer (IDs provided: " + itos(uniform.ids.size()) + ")."); + } + } + + Vector<VkDescriptorBufferInfo> buffer_info; + Vector<VkBufferView> buffer_view; + + for (int j = 0; j < uniform.ids.size(); j++) { + TextureBuffer *buffer = texture_buffer_owner.getornull(uniform.ids[j]); + ERR_FAIL_COND_V_MSG(!buffer, RID(), "Texture Buffer (binding: " + itos(uniform.binding) + ", index " + itos(j) + ") is not a valid texture buffer."); + + buffer_info.push_back(buffer->buffer.buffer_info); + buffer_view.push_back(buffer->view); + } + + write.dstArrayElement = 0; + write.descriptorCount = uniform.ids.size(); + write.descriptorType = VK_DESCRIPTOR_TYPE_UNIFORM_TEXEL_BUFFER; + write.pImageInfo = NULL; + write.pBufferInfo = buffer_infos.push_back(buffer_info)->get().ptr(); + write.pTexelBufferView = buffer_views.push_back(buffer_view)->get().ptr(); + + type_size = uniform.ids.size(); + + } break; + case UNIFORM_TYPE_SAMPLER_WITH_TEXTURE_BUFFER: { + + if (uniform.ids.size() != set_uniform.length * 2) { + if (set_uniform.length > 1) { + ERR_FAIL_V_MSG(RID(), "SamplerBuffer (binding: " + itos(uniform.binding) + ") is an array of (" + itos(set_uniform.length) + ") sampler buffer elements, so it should provided twice the amount of IDs (sampler,buffer pairs) to satisfy it (IDs provided: " + itos(uniform.ids.size()) + ")."); + } else { + ERR_FAIL_V_MSG(RID(), "SamplerBuffer (binding: " + itos(uniform.binding) + ") should provide two IDs referencing a sampler and then a texture buffer (IDs provided: " + itos(uniform.ids.size()) + ")."); + } + } + + Vector<VkDescriptorImageInfo> image_info; + Vector<VkDescriptorBufferInfo> buffer_info; + Vector<VkBufferView> buffer_view; + + for (int j = 0; j < uniform.ids.size(); j += 2) { + VkSampler *sampler = sampler_owner.getornull(uniform.ids[j + 0]); + ERR_FAIL_COND_V_MSG(!sampler, RID(), "SamplerBuffer (binding: " + itos(uniform.binding) + ", index " + itos(j + 1) + ") is not a valid sampler."); + + TextureBuffer *buffer = texture_buffer_owner.getornull(uniform.ids[j + 1]); + + VkDescriptorImageInfo img_info; + img_info.sampler = *sampler; + img_info.imageView = VK_NULL_HANDLE; + img_info.imageLayout = VK_IMAGE_LAYOUT_UNDEFINED; + + image_info.push_back(img_info); + + ERR_FAIL_COND_V_MSG(!buffer, RID(), "SamplerBuffer (binding: " + itos(uniform.binding) + ", index " + itos(j + 1) + ") is not a valid texture buffer."); + + buffer_info.push_back(buffer->buffer.buffer_info); + buffer_view.push_back(buffer->view); + } + + write.dstArrayElement = 0; + write.descriptorCount = uniform.ids.size() / 2; + write.descriptorType = VK_DESCRIPTOR_TYPE_UNIFORM_TEXEL_BUFFER; + write.pImageInfo = image_infos.push_back(image_info)->get().ptr(); + write.pBufferInfo = buffer_infos.push_back(buffer_info)->get().ptr(); + write.pTexelBufferView = buffer_views.push_back(buffer_view)->get().ptr(); + + type_size = uniform.ids.size() / 2; + } break; + case UNIFORM_TYPE_IMAGE_BUFFER: { + //todo + + } break; + case UNIFORM_TYPE_UNIFORM_BUFFER: { + ERR_FAIL_COND_V_MSG(uniform.ids.size() != 1, RID(), + "Uniform buffer supplied (binding: " + itos(uniform.binding) + ") must provide one ID (" + itos(uniform.ids.size()) + " provided)."); + + Buffer *buffer = uniform_buffer_owner.getornull(uniform.ids[0]); + ERR_FAIL_COND_V_MSG(!buffer, RID(), "Uniform buffer supplied (binding: " + itos(uniform.binding) + ") is invalid."); + + ERR_FAIL_COND_V_MSG(buffer->size != (uint32_t)set_uniform.length, RID(), + "Uniform buffer supplied (binding: " + itos(uniform.binding) + ") size (" + itos(buffer->size) + " does not match size of shader uniform: (" + itos(set_uniform.length) + ")."); + + write.dstArrayElement = 0; + write.descriptorCount = 1; + write.descriptorType = VK_DESCRIPTOR_TYPE_UNIFORM_BUFFER; + write.pImageInfo = NULL; + write.pBufferInfo = &buffer->buffer_info; + write.pTexelBufferView = NULL; + + } break; + case UNIFORM_TYPE_STORAGE_BUFFER: { + ERR_FAIL_COND_V_MSG(uniform.ids.size() != 1, RID(), + "Storage buffer supplied (binding: " + itos(uniform.binding) + ") must provide one ID (" + itos(uniform.ids.size()) + " provided)."); + + Buffer *buffer = storage_buffer_owner.getornull(uniform.ids[0]); + ERR_FAIL_COND_V_MSG(!buffer, RID(), "Storage buffer supplied (binding: " + itos(uniform.binding) + ") is invalid."); + + //if 0, then its 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) + ")."); + + write.dstArrayElement = 0; + write.descriptorCount = 1; + write.descriptorType = VK_DESCRIPTOR_TYPE_STORAGE_BUFFER; + write.pImageInfo = NULL; + write.pBufferInfo = &buffer->buffer_info; + write.pTexelBufferView = NULL; + } break; + case UNIFORM_TYPE_INPUT_ATTACHMENT: { + + } break; + default: { + } + } + + writes.push_back(write); + + ERR_FAIL_COND_V_MSG(pool_key.uniform_type[set_uniform.type] == MAX_DESCRIPTOR_POOL_ELEMENT, RID(), + "Uniform set reached the limit of bindings for the same type (" + itos(MAX_DESCRIPTOR_POOL_ELEMENT) + ")."); + pool_key.uniform_type[set_uniform.type] += type_size; + } + + //need a descriptor pool + DescriptorPool *pool = _descriptor_pool_allocate(pool_key); + + ERR_FAIL_COND_V(!pool, RID()); + + VkDescriptorSetAllocateInfo descriptor_set_allocate_info; + + descriptor_set_allocate_info.sType = VK_STRUCTURE_TYPE_DESCRIPTOR_SET_ALLOCATE_INFO; + descriptor_set_allocate_info.pNext = NULL; + descriptor_set_allocate_info.descriptorPool = pool->pool; + descriptor_set_allocate_info.descriptorSetCount = 1; + descriptor_set_allocate_info.pSetLayouts = &shader->sets[p_shader_set].descriptor_set_layout; + + VkDescriptorSet descriptor_set; + + VkResult res = vkAllocateDescriptorSets(device, &descriptor_set_allocate_info, &descriptor_set); + if (res) { + _descriptor_pool_free(pool_key, pool); // meh + ERR_FAIL_V_MSG(RID(), "Cannot allocate descriptor sets."); + } + + UniformSet uniform_set; + uniform_set.pool = pool; + uniform_set.pool_key = pool_key; + uniform_set.descriptor_set = descriptor_set; + uniform_set.format = shader->set_formats[p_shader_set]; + uniform_set.attachable_textures = attachable_textures; + uniform_set.mutable_sampled_textures = mutable_sampled_textures; + uniform_set.mutable_storage_textures = mutable_storage_textures; + uniform_set.shader_set = p_shader_set; + uniform_set.shader_id = p_shader; + + RID id = uniform_set_owner.make_rid(uniform_set); + //add dependencies + _add_dependency(id, p_shader); + for (uint32_t i = 0; i < uniform_count; i++) { + const Uniform &uniform = uniforms[i]; + int id_count = uniform.ids.size(); + const RID *ids = uniform.ids.ptr(); + for (int j = 0; j < id_count; j++) { + _add_dependency(id, ids[j]); + } + } + + //write the contents + if (writes.size()) { + for (int i = 0; i < writes.size(); i++) { + writes.write[i].dstSet = descriptor_set; + } + vkUpdateDescriptorSets(device, writes.size(), writes.ptr(), 0, NULL); + } + + return id; +} + +bool RenderingDeviceVulkan::uniform_set_is_valid(RID p_uniform_set) { + return uniform_set_owner.owns(p_uniform_set); +} + +Error RenderingDeviceVulkan::buffer_update(RID p_buffer, uint32_t p_offset, uint32_t p_size, const void *p_data, bool p_sync_with_draw) { + _THREAD_SAFE_METHOD_ + + ERR_FAIL_COND_V_MSG(draw_list && p_sync_with_draw, ERR_INVALID_PARAMETER, + "Updating buffers in 'sync to draw' mode is forbidden during creation of a draw list"); + + VkPipelineStageFlags dst_stage_mask; + VkAccessFlags dst_access; + + Buffer *buffer = NULL; + if (vertex_buffer_owner.owns(p_buffer)) { + dst_stage_mask = VK_PIPELINE_STAGE_VERTEX_INPUT_BIT; + dst_access = VK_ACCESS_VERTEX_ATTRIBUTE_READ_BIT; + buffer = vertex_buffer_owner.getornull(p_buffer); + } else if (index_buffer_owner.owns(p_buffer)) { + dst_stage_mask = VK_PIPELINE_STAGE_VERTEX_INPUT_BIT; + dst_access = VK_ACCESS_INDEX_READ_BIT; + buffer = index_buffer_owner.getornull(p_buffer); + } else if (uniform_buffer_owner.owns(p_buffer)) { + dst_stage_mask = VK_PIPELINE_STAGE_VERTEX_SHADER_BIT | VK_PIPELINE_STAGE_FRAGMENT_SHADER_BIT | VK_PIPELINE_STAGE_COMPUTE_SHADER_BIT; + dst_access = VK_ACCESS_UNIFORM_READ_BIT; + buffer = uniform_buffer_owner.getornull(p_buffer); + } else if (texture_buffer_owner.owns(p_buffer)) { + dst_stage_mask = VK_PIPELINE_STAGE_VERTEX_SHADER_BIT | VK_PIPELINE_STAGE_FRAGMENT_SHADER_BIT | VK_PIPELINE_STAGE_COMPUTE_SHADER_BIT; + dst_access = VK_ACCESS_SHADER_READ_BIT; + buffer = &texture_buffer_owner.getornull(p_buffer)->buffer; + } else if (storage_buffer_owner.owns(p_buffer)) { + dst_stage_mask = VK_PIPELINE_STAGE_VERTEX_SHADER_BIT | VK_PIPELINE_STAGE_FRAGMENT_SHADER_BIT | VK_PIPELINE_STAGE_COMPUTE_SHADER_BIT; + dst_access = VK_ACCESS_SHADER_READ_BIT | VK_ACCESS_SHADER_WRITE_BIT; + buffer = storage_buffer_owner.getornull(p_buffer); + } else { + ERR_FAIL_V_MSG(ERR_INVALID_PARAMETER, "Buffer argument is not a valid buffer of any type."); + } + + 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."); + + Error err = _buffer_update(buffer, p_offset, (uint8_t *)p_data, p_size, p_sync_with_draw); + if (err) { + return err; + } + + _buffer_memory_barrier(buffer->buffer, p_offset, p_size, VK_PIPELINE_STAGE_TRANSFER_BIT, dst_stage_mask, VK_ACCESS_TRANSFER_WRITE_BIT, dst_access, p_sync_with_draw); +#ifdef FORCE_FULL_BARRIER + _full_barrier(p_sync_with_draw); +#else + _buffer_memory_barrier(buffer->buffer, p_offset, p_size, VK_PIPELINE_STAGE_TRANSFER_BIT, dst_stage_mask, VK_ACCESS_TRANSFER_WRITE_BIT, dst_access, p_sync_with_draw); +#endif + return err; +} + +PoolVector<uint8_t> RenderingDeviceVulkan::buffer_get_data(RID p_buffer) { + + _THREAD_SAFE_METHOD_ + + Buffer *buffer = NULL; + if (vertex_buffer_owner.owns(p_buffer)) { + buffer = vertex_buffer_owner.getornull(p_buffer); + } else if (index_buffer_owner.owns(p_buffer)) { + buffer = index_buffer_owner.getornull(p_buffer); + } else if (texture_buffer_owner.owns(p_buffer)) { + buffer = &texture_buffer_owner.getornull(p_buffer)->buffer; + } else if (storage_buffer_owner.owns(p_buffer)) { + buffer = storage_buffer_owner.getornull(p_buffer); + } else { + ERR_FAIL_V_MSG(PoolVector<uint8_t>(), "Buffer is either invalid or this type of buffer can't be retrieved. Only Index and Vertex buffers allow retrieving."); + } + + VkCommandBuffer command_buffer = frames[frame].setup_command_buffer; + Buffer tmp_buffer; + _buffer_allocate(&tmp_buffer, buffer->size, VK_BUFFER_USAGE_TRANSFER_DST_BIT, VMA_MEMORY_USAGE_CPU_ONLY); + VkBufferCopy region; + 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 + _flush(true); + + void *buffer_mem; + VkResult vkerr = vmaMapMemory(allocator, tmp_buffer.allocation, &buffer_mem); + if (vkerr) { + ERR_FAIL_V(PoolVector<uint8_t>()); + } + + PoolVector<uint8_t> buffer_data; + { + + buffer_data.resize(buffer->size); + PoolVector<uint8_t>::Write w = buffer_data.write(); + copymem(w.ptr(), buffer_mem, buffer->size); + } + + vmaUnmapMemory(allocator, tmp_buffer.allocation); + + _buffer_free(&tmp_buffer); + + return buffer_data; +} + +/*************************/ +/**** RENDER PIPELINE ****/ +/*************************/ + +RID RenderingDeviceVulkan::render_pipeline_create(RID p_shader, FramebufferFormatID p_framebuffer_format, VertexFormatID p_vertex_format, RenderPrimitive p_render_primitive, const PipelineRasterizationState &p_rasterization_state, const PipelineMultisampleState &p_multisample_state, const PipelineDepthStencilState &p_depth_stencil_state, const PipelineColorBlendState &p_blend_state, int p_dynamic_state_flags) { + + _THREAD_SAFE_METHOD_ + + //needs a shader + Shader *shader = shader_owner.getornull(p_shader); + ERR_FAIL_COND_V(!shader, RID()); + + ERR_FAIL_COND_V_MSG(shader->is_compute, RID(), + "Compute shaders can't be used in render pipelines"); + + if (p_framebuffer_format == INVALID_ID) { + //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 + + ERR_FAIL_COND_V_MSG(shader->fragment_outputs != fb_format.color_attachments, RID(), + "Mismatch fragment output bindings (" + itos(shader->fragment_outputs) + ") and framebuffer color buffers (" + itos(fb_format.color_attachments) + ") when binding both in render pipeline."); + } + //vertex + VkPipelineVertexInputStateCreateInfo pipeline_vertex_input_state_create_info; + + if (p_vertex_format != INVALID_ID) { + //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 + for (uint32_t i = 0; i < 32; i++) { + if (!(shader->vertex_input_mask & (1 << i))) { + continue; + } + bool found = false; + for (int j = 0; j < vd.vertex_formats.size(); j++) { + if (vd.vertex_formats[j].location == i) { + found = true; + } + } + + ERR_FAIL_COND_V_MSG(!found, RID(), + "Shader vertex input location (" + itos(i) + ") not provided in vertex input description for pipeline creation."); + } + + } else { + //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 = NULL; + pipeline_vertex_input_state_create_info.flags = 0; + pipeline_vertex_input_state_create_info.vertexBindingDescriptionCount = 0; + pipeline_vertex_input_state_create_info.pVertexBindingDescriptions = NULL; + pipeline_vertex_input_state_create_info.vertexAttributeDescriptionCount = 0; + pipeline_vertex_input_state_create_info.pVertexAttributeDescriptions = NULL; + + 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 + + ERR_FAIL_INDEX_V(p_render_primitive, RENDER_PRIMITIVE_MAX, RID()); + + VkPipelineInputAssemblyStateCreateInfo input_assembly_create_info; + input_assembly_create_info.sType = VK_STRUCTURE_TYPE_PIPELINE_INPUT_ASSEMBLY_STATE_CREATE_INFO; + input_assembly_create_info.pNext = NULL; + input_assembly_create_info.flags = 0; + + static const VkPrimitiveTopology topology_list[RENDER_PRIMITIVE_MAX] = { + VK_PRIMITIVE_TOPOLOGY_POINT_LIST, + VK_PRIMITIVE_TOPOLOGY_LINE_LIST, + VK_PRIMITIVE_TOPOLOGY_LINE_LIST_WITH_ADJACENCY, + VK_PRIMITIVE_TOPOLOGY_LINE_STRIP, + VK_PRIMITIVE_TOPOLOGY_LINE_STRIP_WITH_ADJACENCY, + VK_PRIMITIVE_TOPOLOGY_TRIANGLE_LIST, + VK_PRIMITIVE_TOPOLOGY_TRIANGLE_LIST_WITH_ADJACENCY, + VK_PRIMITIVE_TOPOLOGY_TRIANGLE_STRIP, + VK_PRIMITIVE_TOPOLOGY_TRIANGLE_STRIP_WITH_ADJACENCY, + VK_PRIMITIVE_TOPOLOGY_TRIANGLE_STRIP, + VK_PRIMITIVE_TOPOLOGY_PATCH_LIST + }; + + 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); + + //tesselation + VkPipelineTessellationStateCreateInfo tesselation_create_info; + tesselation_create_info.sType = VK_STRUCTURE_TYPE_PIPELINE_TESSELLATION_STATE_CREATE_INFO; + tesselation_create_info.pNext = NULL; + tesselation_create_info.flags = 0; + ERR_FAIL_COND_V(p_rasterization_state.patch_control_points < 1 || p_rasterization_state.patch_control_points > limits.maxTessellationPatchSize, RID()); + tesselation_create_info.patchControlPoints = p_rasterization_state.patch_control_points; + + VkPipelineViewportStateCreateInfo viewport_state_create_info; + viewport_state_create_info.sType = VK_STRUCTURE_TYPE_PIPELINE_VIEWPORT_STATE_CREATE_INFO; + viewport_state_create_info.pNext = NULL; + 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.pViewports = NULL; + viewport_state_create_info.scissorCount = 1; + viewport_state_create_info.pScissors = NULL; + + //rasterization + VkPipelineRasterizationStateCreateInfo rasterization_state_create_info; + rasterization_state_create_info.sType = VK_STRUCTURE_TYPE_PIPELINE_RASTERIZATION_STATE_CREATE_INFO; + rasterization_state_create_info.pNext = NULL; + rasterization_state_create_info.flags = 0; + rasterization_state_create_info.depthClampEnable = p_rasterization_state.enable_depth_clamp; + rasterization_state_create_info.rasterizerDiscardEnable = p_rasterization_state.discard_primitives; + rasterization_state_create_info.polygonMode = (p_rasterization_state.wireframe ? VK_POLYGON_MODE_LINE : VK_POLYGON_MODE_FILL); + static VkCullModeFlags cull_mode[3] = { + VK_CULL_MODE_NONE, + VK_CULL_MODE_FRONT_BIT, + VK_CULL_MODE_BACK_BIT + }; + + ERR_FAIL_INDEX_V(p_rasterization_state.cull_mode, 3, RID()); + rasterization_state_create_info.cullMode = cull_mode[p_rasterization_state.cull_mode]; + rasterization_state_create_info.frontFace = (p_rasterization_state.front_face == POLYGON_FRONT_FACE_CLOCKWISE ? VK_FRONT_FACE_CLOCKWISE : VK_FRONT_FACE_COUNTER_CLOCKWISE); + rasterization_state_create_info.depthBiasEnable = p_rasterization_state.depth_bias_enable; + rasterization_state_create_info.depthBiasConstantFactor = p_rasterization_state.depth_bias_constant_factor; + rasterization_state_create_info.depthBiasClamp = p_rasterization_state.depth_bias_clamp; + rasterization_state_create_info.depthBiasSlopeFactor = p_rasterization_state.depth_bias_slope_factor; + rasterization_state_create_info.lineWidth = p_rasterization_state.line_width; + + //multisample + VkPipelineMultisampleStateCreateInfo multisample_state_create_info; + multisample_state_create_info.sType = VK_STRUCTURE_TYPE_PIPELINE_MULTISAMPLE_STATE_CREATE_INFO; + multisample_state_create_info.pNext = NULL; + multisample_state_create_info.flags = 0; + + multisample_state_create_info.rasterizationSamples = rasterization_sample_count[p_multisample_state.sample_count]; + multisample_state_create_info.sampleShadingEnable = p_multisample_state.enable_sample_shading; + 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 + int rasterization_sample_mask_expected_size[TEXTURE_SAMPLES_MAX] = { + 1, 2, 4, 8, 16, 32, 64 + }; + ERR_FAIL_COND_V(rasterization_sample_mask_expected_size[p_multisample_state.sample_count] != p_multisample_state.sample_mask.size(), RID()); + sample_mask.resize(p_multisample_state.sample_mask.size()); + for (int i = 0; i < p_multisample_state.sample_mask.size(); i++) { + VkSampleMask mask = p_multisample_state.sample_mask[i]; + sample_mask.push_back(mask); + } + multisample_state_create_info.pSampleMask = sample_mask.ptr(); + } else { + multisample_state_create_info.pSampleMask = NULL; + } + + 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 + + VkPipelineDepthStencilStateCreateInfo depth_stencil_state_create_info; + depth_stencil_state_create_info.sType = VK_STRUCTURE_TYPE_PIPELINE_DEPTH_STENCIL_STATE_CREATE_INFO; + depth_stencil_state_create_info.pNext = NULL; + depth_stencil_state_create_info.flags = 0; + depth_stencil_state_create_info.depthTestEnable = p_depth_stencil_state.enable_depth_test; + depth_stencil_state_create_info.depthWriteEnable = p_depth_stencil_state.enable_depth_write; + ERR_FAIL_INDEX_V(p_depth_stencil_state.depth_compare_operator, COMPARE_OP_MAX, RID()); + depth_stencil_state_create_info.depthCompareOp = compare_operators[p_depth_stencil_state.depth_compare_operator]; + depth_stencil_state_create_info.depthBoundsTestEnable = p_depth_stencil_state.enable_depth_range; + depth_stencil_state_create_info.stencilTestEnable = p_depth_stencil_state.enable_stencil; + + ERR_FAIL_INDEX_V(p_depth_stencil_state.stencil_operation_front.fail, STENCIL_OP_MAX, RID()); + depth_stencil_state_create_info.front.failOp = stencil_operations[p_depth_stencil_state.stencil_operation_front.fail]; + ERR_FAIL_INDEX_V(p_depth_stencil_state.stencil_operation_front.pass, STENCIL_OP_MAX, RID()); + depth_stencil_state_create_info.front.passOp = stencil_operations[p_depth_stencil_state.stencil_operation_front.pass]; + ERR_FAIL_INDEX_V(p_depth_stencil_state.stencil_operation_front.depth_fail, STENCIL_OP_MAX, RID()); + depth_stencil_state_create_info.front.depthFailOp = stencil_operations[p_depth_stencil_state.stencil_operation_front.depth_fail]; + ERR_FAIL_INDEX_V(p_depth_stencil_state.stencil_operation_front.compare, COMPARE_OP_MAX, RID()); + depth_stencil_state_create_info.front.compareOp = compare_operators[p_depth_stencil_state.stencil_operation_front.compare]; + depth_stencil_state_create_info.front.compareMask = p_depth_stencil_state.stencil_operation_front.compare_mask; + depth_stencil_state_create_info.front.writeMask = p_depth_stencil_state.stencil_operation_front.write_mask; + depth_stencil_state_create_info.front.reference = p_depth_stencil_state.stencil_operation_front.reference; + + ERR_FAIL_INDEX_V(p_depth_stencil_state.stencil_operation_back.fail, STENCIL_OP_MAX, RID()); + depth_stencil_state_create_info.back.failOp = stencil_operations[p_depth_stencil_state.stencil_operation_back.fail]; + ERR_FAIL_INDEX_V(p_depth_stencil_state.stencil_operation_back.pass, STENCIL_OP_MAX, RID()); + depth_stencil_state_create_info.back.passOp = stencil_operations[p_depth_stencil_state.stencil_operation_back.pass]; + ERR_FAIL_INDEX_V(p_depth_stencil_state.stencil_operation_back.depth_fail, STENCIL_OP_MAX, RID()); + depth_stencil_state_create_info.back.depthFailOp = stencil_operations[p_depth_stencil_state.stencil_operation_back.depth_fail]; + ERR_FAIL_INDEX_V(p_depth_stencil_state.stencil_operation_back.compare, COMPARE_OP_MAX, RID()); + depth_stencil_state_create_info.back.compareOp = compare_operators[p_depth_stencil_state.stencil_operation_back.compare]; + depth_stencil_state_create_info.back.compareMask = p_depth_stencil_state.stencil_operation_back.compare_mask; + depth_stencil_state_create_info.back.writeMask = p_depth_stencil_state.stencil_operation_back.write_mask; + depth_stencil_state_create_info.back.reference = p_depth_stencil_state.stencil_operation_back.reference; + + 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 + 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 = NULL; + color_blend_state_create_info.flags = 0; + color_blend_state_create_info.logicOpEnable = p_blend_state.enable_logic_op; + ERR_FAIL_INDEX_V(p_blend_state.logic_op, LOGIC_OP_MAX, RID()); + color_blend_state_create_info.logicOp = logic_operations[p_blend_state.logic_op]; + + ERR_FAIL_COND_V(fb_format.color_attachments != p_blend_state.attachments.size(), RID()); + + Vector<VkPipelineColorBlendAttachmentState> attachment_states; + + for (int i = 0; i < p_blend_state.attachments.size(); i++) { + VkPipelineColorBlendAttachmentState state; + state.blendEnable = p_blend_state.attachments[i].enable_blend; + + ERR_FAIL_INDEX_V(p_blend_state.attachments[i].src_color_blend_factor, BLEND_FACTOR_MAX, RID()); + state.srcColorBlendFactor = blend_factors[p_blend_state.attachments[i].src_color_blend_factor]; + ERR_FAIL_INDEX_V(p_blend_state.attachments[i].dst_color_blend_factor, BLEND_FACTOR_MAX, RID()); + state.dstColorBlendFactor = blend_factors[p_blend_state.attachments[i].dst_color_blend_factor]; + ERR_FAIL_INDEX_V(p_blend_state.attachments[i].color_blend_op, BLEND_OP_MAX, RID()); + state.colorBlendOp = blend_operations[p_blend_state.attachments[i].color_blend_op]; + + ERR_FAIL_INDEX_V(p_blend_state.attachments[i].src_alpha_blend_factor, BLEND_FACTOR_MAX, RID()); + state.srcAlphaBlendFactor = blend_factors[p_blend_state.attachments[i].src_alpha_blend_factor]; + ERR_FAIL_INDEX_V(p_blend_state.attachments[i].dst_alpha_blend_factor, BLEND_FACTOR_MAX, RID()); + state.dstAlphaBlendFactor = blend_factors[p_blend_state.attachments[i].dst_alpha_blend_factor]; + ERR_FAIL_INDEX_V(p_blend_state.attachments[i].alpha_blend_op, BLEND_OP_MAX, RID()); + state.alphaBlendOp = blend_operations[p_blend_state.attachments[i].alpha_blend_op]; + + state.colorWriteMask = 0; + if (p_blend_state.attachments[i].write_r) { + state.colorWriteMask |= VK_COLOR_COMPONENT_R_BIT; + } + if (p_blend_state.attachments[i].write_g) { + state.colorWriteMask |= VK_COLOR_COMPONENT_G_BIT; + } + if (p_blend_state.attachments[i].write_b) { + state.colorWriteMask |= VK_COLOR_COMPONENT_B_BIT; + } + if (p_blend_state.attachments[i].write_a) { + state.colorWriteMask |= VK_COLOR_COMPONENT_A_BIT; + } + + attachment_states.push_back(state); + }; + + color_blend_state_create_info.attachmentCount = attachment_states.size(); + color_blend_state_create_info.pAttachments = attachment_states.ptr(); + + color_blend_state_create_info.blendConstants[0] = p_blend_state.blend_constant.r; + color_blend_state_create_info.blendConstants[1] = p_blend_state.blend_constant.g; + 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 + + VkPipelineDynamicStateCreateInfo dynamic_state_create_info; + dynamic_state_create_info.sType = VK_STRUCTURE_TYPE_PIPELINE_DYNAMIC_STATE_CREATE_INFO; + dynamic_state_create_info.pNext = NULL; + dynamic_state_create_info.flags = 0; + 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_SCISSOR); + + if (p_dynamic_state_flags & DYNAMIC_STATE_LINE_WIDTH) { + dynamic_states.push_back(VK_DYNAMIC_STATE_LINE_WIDTH); + } + + if (p_dynamic_state_flags & DYNAMIC_STATE_DEPTH_BIAS) { + dynamic_states.push_back(VK_DYNAMIC_STATE_DEPTH_BIAS); + } + + if (p_dynamic_state_flags & DYNAMIC_STATE_BLEND_CONSTANTS) { + dynamic_states.push_back(VK_DYNAMIC_STATE_BLEND_CONSTANTS); + } + + if (p_dynamic_state_flags & DYNAMIC_STATE_DEPTH_BOUNDS) { + dynamic_states.push_back(VK_DYNAMIC_STATE_DEPTH_BOUNDS); + } + + if (p_dynamic_state_flags & DYNAMIC_STATE_STENCIL_COMPARE_MASK) { + dynamic_states.push_back(VK_DYNAMIC_STATE_STENCIL_COMPARE_MASK); + } + + if (p_dynamic_state_flags & DYNAMIC_STATE_STENCIL_WRITE_MASK) { + dynamic_states.push_back(VK_DYNAMIC_STATE_STENCIL_WRITE_MASK); + } + + if (p_dynamic_state_flags & DYNAMIC_STATE_STENCIL_REFERENCE) { + dynamic_states.push_back(VK_DYNAMIC_STATE_STENCIL_REFERENCE); + } + + dynamic_state_create_info.dynamicStateCount = dynamic_states.size(); + dynamic_state_create_info.pDynamicStates = dynamic_states.ptr(); + + //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 = NULL; + graphics_pipeline_create_info.flags = 0; + + graphics_pipeline_create_info.stageCount = shader->pipeline_stages.size(); + graphics_pipeline_create_info.pStages = shader->pipeline_stages.ptr(); + graphics_pipeline_create_info.pVertexInputState = &pipeline_vertex_input_state_create_info; + graphics_pipeline_create_info.pInputAssemblyState = &input_assembly_create_info; + graphics_pipeline_create_info.pTessellationState = &tesselation_create_info; + graphics_pipeline_create_info.pViewportState = &viewport_state_create_info; + graphics_pipeline_create_info.pRasterizationState = &rasterization_state_create_info; + graphics_pipeline_create_info.pMultisampleState = &multisample_state_create_info; + graphics_pipeline_create_info.pDepthStencilState = &depth_stencil_state_create_info; + graphics_pipeline_create_info.pColorBlendState = &color_blend_state_create_info; + graphics_pipeline_create_info.pDynamicState = &dynamic_state_create_info; + graphics_pipeline_create_info.layout = shader->pipeline_layout; + graphics_pipeline_create_info.renderPass = fb_format.render_pass; + + graphics_pipeline_create_info.subpass = 0; + graphics_pipeline_create_info.basePipelineHandle = NULL; + graphics_pipeline_create_info.basePipelineIndex = 0; + + RenderPipeline pipeline; + VkResult err = vkCreateGraphicsPipelines(device, NULL, 1, &graphics_pipeline_create_info, NULL, &pipeline.pipeline); + ERR_FAIL_COND_V(err, RID()); + + pipeline.set_formats = shader->set_formats; + pipeline.push_constant_stages = shader->push_constant.push_constants_vk_stage; + pipeline.pipeline_layout = shader->pipeline_layout; + pipeline.shader = p_shader; + pipeline.push_constant_size = shader->push_constant.push_constant_size; + +#ifdef DEBUG_ENABLED + pipeline.validation.dynamic_state = p_dynamic_state_flags; + pipeline.validation.framebuffer_format = p_framebuffer_format; + pipeline.validation.vertex_format = p_vertex_format; + pipeline.validation.uses_restart_indices = input_assembly_create_info.primitiveRestartEnable; + + static const uint32_t primitive_divisor[RENDER_PRIMITIVE_MAX] = { + 1, 2, 1, 1, 1, 3, 1, 1, 1, 1, 1 + }; + pipeline.validation.primitive_divisor = primitive_divisor[p_render_primitive]; + static const uint32_t primitive_minimum[RENDER_PRIMITIVE_MAX] = { + 1, + 2, + 2, + 2, + 2, + 3, + 3, + 3, + 3, + 3, + 1, + }; + pipeline.validation.primitive_minimum = primitive_minimum[p_render_primitive]; +#endif + //create ID to associate with this pipeline + RID id = render_pipeline_owner.make_rid(pipeline); + //now add aall the dependencies + _add_dependency(id, p_shader); + return id; +} + +bool RenderingDeviceVulkan::render_pipeline_is_valid(RID p_pipeline) { + _THREAD_SAFE_METHOD_ + return render_pipeline_owner.owns(p_pipeline); +} + +/**************************/ +/**** COMPUTE PIPELINE ****/ +/**************************/ + +RID RenderingDeviceVulkan::compute_pipeline_create(RID p_shader) { + _THREAD_SAFE_METHOD_ + + //needs a shader + Shader *shader = shader_owner.getornull(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 + VkComputePipelineCreateInfo compute_pipeline_create_info; + + compute_pipeline_create_info.sType = VK_STRUCTURE_TYPE_COMPUTE_PIPELINE_CREATE_INFO; + compute_pipeline_create_info.pNext = NULL; + compute_pipeline_create_info.flags = 0; + + compute_pipeline_create_info.stage = shader->pipeline_stages[0]; + compute_pipeline_create_info.layout = shader->pipeline_layout; + compute_pipeline_create_info.basePipelineHandle = NULL; + compute_pipeline_create_info.basePipelineIndex = 0; + + ComputePipeline pipeline; + VkResult err = vkCreateComputePipelines(device, NULL, 1, &compute_pipeline_create_info, NULL, &pipeline.pipeline); + ERR_FAIL_COND_V(err, RID()); + + pipeline.set_formats = shader->set_formats; + pipeline.push_constant_stages = shader->push_constant.push_constants_vk_stage; + pipeline.pipeline_layout = shader->pipeline_layout; + pipeline.shader = p_shader; + pipeline.push_constant_size = shader->push_constant.push_constant_size; + + //create ID to associate with this pipeline + RID id = compute_pipeline_owner.make_rid(pipeline); + //now add aall the dependencies + _add_dependency(id, p_shader); + return id; +} + +bool RenderingDeviceVulkan::compute_pipeline_is_valid(RID p_pipeline) { + + return compute_pipeline_owner.owns(p_pipeline); +} + +/****************/ +/**** SCREEN ****/ +/****************/ + +int RenderingDeviceVulkan::screen_get_width(int p_screen) const { + _THREAD_SAFE_METHOD_ + + return context->window_get_width(p_screen); +} +int RenderingDeviceVulkan::screen_get_height(int p_screen) const { + _THREAD_SAFE_METHOD_ + + return context->window_get_height(p_screen); +} +RenderingDevice::FramebufferFormatID RenderingDeviceVulkan::screen_get_framebuffer_format() const { + + _THREAD_SAFE_METHOD_ + + //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++) { + if (vkformat == vulkan_formats[i]) { + format = DataFormat(i); + break; + } + } + + ERR_FAIL_COND_V(format == DATA_FORMAT_MAX, INVALID_ID); + + AttachmentFormat attachment; + attachment.format = format; + attachment.samples = TEXTURE_SAMPLES_1; + attachment.usage_flags = TEXTURE_USAGE_COLOR_ATTACHMENT_BIT; + Vector<AttachmentFormat> screen_attachment; + screen_attachment.push_back(attachment); + return const_cast<RenderingDeviceVulkan *>(this)->framebuffer_format_create(screen_attachment); +} + +/*******************/ +/**** DRAW LIST ****/ +/*******************/ + +RenderingDevice::DrawListID RenderingDeviceVulkan::draw_list_begin_for_screen(int p_screen, const Color &p_clear_color) { + + _THREAD_SAFE_METHOD_ + + ERR_FAIL_COND_V_MSG(draw_list != NULL, INVALID_ID, "Only one draw list can be active at the same time."); + ERR_FAIL_COND_V_MSG(compute_list != NULL, INVALID_ID, "Only one draw/compute list can be active at the same time."); + + VkCommandBuffer command_buffer = frames[frame].draw_command_buffer; + draw_list = memnew(DrawList); + draw_list->command_buffer = command_buffer; +#ifdef DEBUG_ENABLED + draw_list->validation.framebuffer_format = screen_get_framebuffer_format(); +#endif + draw_list_count = 0; + draw_list_split = false; + + VkRenderPassBeginInfo render_pass_begin; + render_pass_begin.sType = VK_STRUCTURE_TYPE_RENDER_PASS_BEGIN_INFO; + render_pass_begin.pNext = NULL; + render_pass_begin.renderPass = context->window_get_render_pass(p_screen); + render_pass_begin.framebuffer = context->window_get_framebuffer(p_screen); + + render_pass_begin.renderArea.extent.width = context->window_get_width(p_screen); + render_pass_begin.renderArea.extent.height = context->window_get_height(p_screen); + render_pass_begin.renderArea.offset.x = 0; + render_pass_begin.renderArea.offset.y = 0; + + render_pass_begin.clearValueCount = 1; + + VkClearValue clear_value; + clear_value.color.float32[0] = p_clear_color.r; + clear_value.color.float32[1] = p_clear_color.g; + clear_value.color.float32[2] = p_clear_color.b; + clear_value.color.float32[3] = p_clear_color.a; + + render_pass_begin.pClearValues = &clear_value; + + vkCmdBeginRenderPass(command_buffer, &render_pass_begin, VK_SUBPASS_CONTENTS_INLINE); + + uint32_t size_x = screen_get_width(p_screen); + uint32_t size_y = screen_get_height(p_screen); + + VkViewport viewport; + viewport.x = 0; + viewport.y = 0; + viewport.width = size_x; + viewport.height = size_y; + viewport.minDepth = 0; + viewport.maxDepth = 1.0; + + vkCmdSetViewport(command_buffer, 0, 1, &viewport); + + VkRect2D scissor; + scissor.offset.x = 0; + scissor.offset.y = 0; + scissor.extent.width = size_x; + scissor.extent.height = size_y; + + vkCmdSetScissor(command_buffer, 0, 1, &scissor); + + return ID_TYPE_DRAW_LIST; +} + +Error RenderingDeviceVulkan::_draw_list_setup_framebuffer(Framebuffer *p_framebuffer, InitialAction p_initial_color_action, FinalAction p_final_color_action, InitialAction p_initial_depth_action, FinalAction p_final_depth_action, VkFramebuffer *r_framebuffer, VkRenderPass *r_render_pass) { + + Framebuffer::VersionKey vk; + vk.initial_color_action = p_initial_color_action; + vk.final_color_action = p_final_color_action; + vk.initial_depth_action = p_initial_depth_action; + vk.final_depth_action = p_final_depth_action; + + if (!p_framebuffer->framebuffers.has(vk)) { + //need to create this version + Framebuffer::Version version; + + version.render_pass = _render_pass_create(framebuffer_formats[p_framebuffer->format_id].E->key().attachments, p_initial_color_action, p_final_color_action, p_initial_depth_action, p_final_depth_action); + + VkFramebufferCreateInfo framebuffer_create_info; + framebuffer_create_info.sType = VK_STRUCTURE_TYPE_FRAMEBUFFER_CREATE_INFO; + framebuffer_create_info.pNext = NULL; + framebuffer_create_info.flags = 0; + framebuffer_create_info.renderPass = version.render_pass; + Vector<VkImageView> attachments; + for (int i = 0; i < p_framebuffer->texture_ids.size(); i++) { + Texture *texture = texture_owner.getornull(p_framebuffer->texture_ids[i]); + ERR_FAIL_COND_V(!texture, ERR_BUG); + 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); + } + framebuffer_create_info.attachmentCount = attachments.size(); + framebuffer_create_info.pAttachments = attachments.ptr(); + framebuffer_create_info.width = p_framebuffer->size.width; + framebuffer_create_info.height = p_framebuffer->size.height; + framebuffer_create_info.layers = 1; + + VkResult err = vkCreateFramebuffer(device, &framebuffer_create_info, NULL, &version.framebuffer); + ERR_FAIL_COND_V(err, ERR_CANT_CREATE); + + p_framebuffer->framebuffers.insert(vk, version); + } + const Framebuffer::Version &version = p_framebuffer->framebuffers[vk]; + *r_framebuffer = version.framebuffer; + *r_render_pass = version.render_pass; + + return OK; +} + +Error RenderingDeviceVulkan::_draw_list_render_pass_begin(Framebuffer *framebuffer, InitialAction p_initial_color_action, FinalAction p_final_color_action, InitialAction p_initial_depth_action, FinalAction p_final_depth_action, const Vector<Color> &p_clear_colors, float p_clear_depth, uint32_t p_clear_stencil, Point2i viewport_offset, Point2i viewport_size, VkFramebuffer vkframebuffer, VkRenderPass render_pass, VkCommandBuffer command_buffer, VkSubpassContents subpass_contents) { + + VkRenderPassBeginInfo render_pass_begin; + render_pass_begin.sType = VK_STRUCTURE_TYPE_RENDER_PASS_BEGIN_INFO; + render_pass_begin.pNext = NULL; + render_pass_begin.renderPass = render_pass; + render_pass_begin.framebuffer = vkframebuffer; + + render_pass_begin.renderArea.extent.width = viewport_size.width; + render_pass_begin.renderArea.extent.height = viewport_size.height; + render_pass_begin.renderArea.offset.x = viewport_offset.x; + render_pass_begin.renderArea.offset.y = viewport_offset.y; + + Vector<VkClearValue> clear_values; + clear_values.resize(framebuffer->texture_ids.size()); + + { + int color_index = 0; + for (int i = 0; i < framebuffer->texture_ids.size(); i++) { + Texture *texture = texture_owner.getornull(framebuffer->texture_ids[i]); + VkClearValue clear_value; + + 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 + Color clear_color = p_clear_colors[color_index]; + clear_value.color.float32[0] = clear_color.r; + clear_value.color.float32[1] = clear_color.g; + clear_value.color.float32[2] = clear_color.b; + clear_value.color.float32[3] = clear_color.a; + color_index++; + } else if (texture->usage_flags & TEXTURE_USAGE_DEPTH_STENCIL_ATTACHMENT_BIT) { + clear_value.depthStencil.depth = p_clear_depth; + clear_value.depthStencil.stencil = p_clear_stencil; + } else { + clear_value.color.float32[0] = 0; + clear_value.color.float32[1] = 0; + clear_value.color.float32[2] = 0; + clear_value.color.float32[3] = 0; + } + clear_values.write[i] = clear_value; + } + } + + render_pass_begin.clearValueCount = clear_values.size(); + render_pass_begin.pClearValues = clear_values.ptr(); + + vkCmdBeginRenderPass(command_buffer, &render_pass_begin, subpass_contents); + + //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; + + for (int i = 0; i < framebuffer->texture_ids.size(); i++) { + Texture *texture = texture_owner.getornull(framebuffer->texture_ids[i]); + texture->bound = true; + draw_list_bound_textures.push_back(framebuffer->texture_ids[i]); + } + + return OK; +} + +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) { + Vector<VkClearAttachment> clear_attachments; + int color_index = 0; + for (int i = 0; i < framebuffer->texture_ids.size(); i++) { + Texture *texture = texture_owner.getornull(framebuffer->texture_ids[i]); + VkClearAttachment clear_at; + if (p_clear_color && texture->usage_flags & TEXTURE_USAGE_COLOR_ATTACHMENT_BIT) { + ERR_FAIL_INDEX(color_index, p_clear_colors.size()); //a bug + Color clear_color = p_clear_colors[color_index]; + clear_at.clearValue.color.float32[0] = clear_color.r; + clear_at.clearValue.color.float32[1] = clear_color.g; + clear_at.clearValue.color.float32[2] = clear_color.b; + clear_at.clearValue.color.float32[3] = clear_color.a; + clear_at.colorAttachment = color_index++; + clear_at.aspectMask = VK_IMAGE_ASPECT_COLOR_BIT; + } else if (p_clear_depth && texture->usage_flags & TEXTURE_USAGE_DEPTH_STENCIL_ATTACHMENT_BIT) { + + clear_at.clearValue.depthStencil.depth = p_depth; + clear_at.clearValue.depthStencil.stencil = p_stencil; + clear_at.colorAttachment = 0; + clear_at.aspectMask = VK_IMAGE_ASPECT_DEPTH_BIT; + if (format_has_stencil(texture->format)) { + clear_at.aspectMask |= VK_IMAGE_ASPECT_STENCIL_BIT; + } + } else { + ERR_CONTINUE(true); + } + clear_attachments.push_back(clear_at); + } + + VkClearRect cr; + cr.baseArrayLayer = 0; + cr.layerCount = 1; + cr.rect.offset.x = viewport_offset.x; + cr.rect.offset.y = viewport_offset.y; + cr.rect.extent.width = viewport_size.width; + cr.rect.extent.height = viewport_size.height; + + vkCmdClearAttachments(draw_list->command_buffer, clear_attachments.size(), clear_attachments.ptr(), 1, &cr); +} + +RenderingDevice::DrawListID RenderingDeviceVulkan::draw_list_begin(RID p_framebuffer, InitialAction p_initial_color_action, FinalAction p_final_color_action, InitialAction p_initial_depth_action, FinalAction p_final_depth_action, const Vector<Color> &p_clear_color_values, float p_clear_depth, uint32_t p_clear_stencil, const Rect2 &p_region) { + + _THREAD_SAFE_METHOD_ + + ERR_FAIL_COND_V_MSG(draw_list != NULL, INVALID_ID, "Only one draw list can be active at the same time."); + ERR_FAIL_COND_V_MSG(compute_list != NULL, INVALID_ID, "Only one draw/compute list can be active at the same time."); + + Framebuffer *framebuffer = framebuffer_owner.getornull(p_framebuffer); + ERR_FAIL_COND_V(!framebuffer, INVALID_ID); + + Point2i viewport_offset; + Point2i viewport_size = framebuffer->size; + bool needs_clear_color = false; + bool needs_clear_depth = false; + + 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) && + ((regioni.position.x + regioni.size.x) <= (viewport.position.x + viewport.size.x)) && + ((regioni.position.y + regioni.size.y) <= (viewport.position.y + viewport.size.y))) { + ERR_FAIL_V_MSG(INVALID_ID, "When supplying a custom region, it must be contained within the framebuffer rectangle"); + } + + viewport_offset = regioni.position; + viewport_size = regioni.size; + + if (p_initial_color_action == INITIAL_ACTION_CLEAR) { + needs_clear_color = true; + p_initial_color_action = INITIAL_ACTION_KEEP; + } + if (p_initial_depth_action == INITIAL_ACTION_CLEAR) { + needs_clear_depth = true; + p_initial_depth_action = INITIAL_ACTION_KEEP; + } + } + + if (p_initial_color_action == INITIAL_ACTION_CLEAR) { //check clear values + + int color_attachments = framebuffer_formats[framebuffer->format_id].color_attachments; + ERR_FAIL_COND_V_MSG(p_clear_color_values.size() != color_attachments, INVALID_ID, + "Clear color values supplied (" + itos(p_clear_color_values.size()) + ") differ from the amount required for framebuffer (" + itos(color_attachments) + ")."); + } + + VkFramebuffer vkframebuffer; + VkRenderPass render_pass; + + Error err = _draw_list_setup_framebuffer(framebuffer, p_initial_color_action, p_final_color_action, p_initial_depth_action, p_final_depth_action, &vkframebuffer, &render_pass); + ERR_FAIL_COND_V(err != OK, INVALID_ID); + + VkCommandBuffer command_buffer = frames[frame].draw_command_buffer; + err = _draw_list_render_pass_begin(framebuffer, p_initial_color_action, p_final_color_action, p_initial_depth_action, p_final_depth_action, p_clear_color_values, p_clear_depth, p_clear_stencil, viewport_offset, viewport_size, vkframebuffer, render_pass, command_buffer, VK_SUBPASS_CONTENTS_INLINE); + + if (err != OK) { + return INVALID_ID; + } + + draw_list = memnew(DrawList); + draw_list->command_buffer = command_buffer; +#ifdef DEBUG_ENABLED + draw_list->validation.framebuffer_format = framebuffer->format_id; +#endif + draw_list_count = 0; + draw_list_split = false; + + if (needs_clear_color || needs_clear_depth) { + _draw_list_insert_clear_region(draw_list, framebuffer, viewport_offset, viewport_size, needs_clear_color, p_clear_color_values, needs_clear_depth, p_clear_depth, p_clear_stencil); + } + + VkViewport viewport; + viewport.x = viewport_offset.x; + viewport.y = viewport_offset.y; + viewport.width = viewport_size.width; + viewport.height = viewport_size.height; + viewport.minDepth = 0; + viewport.maxDepth = 1.0; + + vkCmdSetViewport(command_buffer, 0, 1, &viewport); + + VkRect2D scissor; + scissor.offset.x = viewport_offset.x; + scissor.offset.y = viewport_offset.y; + scissor.extent.width = viewport_size.width; + scissor.extent.height = viewport_size.height; + + vkCmdSetScissor(command_buffer, 0, 1, &scissor); + + draw_list->viewport = Rect2i(viewport_offset, viewport_size); + return ID_TYPE_DRAW_LIST; +} + +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) { + + _THREAD_SAFE_METHOD_ + + ERR_FAIL_COND_V(p_splits < 1, ERR_INVALID_DECLARATION); + + Framebuffer *framebuffer = framebuffer_owner.getornull(p_framebuffer); + ERR_FAIL_COND_V(!framebuffer, ERR_INVALID_DECLARATION); + + Point2i viewport_offset; + Point2i viewport_size = framebuffer->size; + + bool needs_clear_color = false; + bool needs_clear_depth = false; + + 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) && + ((regioni.position.x + regioni.size.x) <= (viewport.position.x + viewport.size.x)) && + ((regioni.position.y + regioni.size.y) <= (viewport.position.y + viewport.size.y))) { + ERR_FAIL_V_MSG(ERR_INVALID_PARAMETER, "When supplying a custom region, it must be contained within the framebuffer rectangle"); + } + + viewport_offset = regioni.position; + viewport_size = regioni.size; + + if (p_initial_color_action == INITIAL_ACTION_CLEAR) { + needs_clear_color = true; + p_initial_color_action = INITIAL_ACTION_KEEP; + } + if (p_initial_depth_action == INITIAL_ACTION_CLEAR) { + needs_clear_depth = true; + p_initial_depth_action = INITIAL_ACTION_KEEP; + } + } + + if (p_initial_color_action == INITIAL_ACTION_CLEAR) { //check clear values + + int color_attachments = framebuffer_formats[framebuffer->format_id].color_attachments; + ERR_FAIL_COND_V_MSG(p_clear_color_values.size() != color_attachments, ERR_INVALID_PARAMETER, + "Clear color values supplied (" + itos(p_clear_color_values.size()) + ") differ from the amount required for framebuffer (" + itos(color_attachments) + ")."); + } + + if (p_splits > (uint32_t)split_draw_list_allocators.size()) { + uint32_t from = split_draw_list_allocators.size(); + split_draw_list_allocators.resize(p_splits); + for (uint32_t i = from; i < p_splits; i++) { + + VkCommandPoolCreateInfo cmd_pool_info; + cmd_pool_info.sType = VK_STRUCTURE_TYPE_COMMAND_POOL_CREATE_INFO; + cmd_pool_info.pNext = NULL; + cmd_pool_info.queueFamilyIndex = context->get_graphics_queue(); + cmd_pool_info.flags = VK_COMMAND_POOL_CREATE_RESET_COMMAND_BUFFER_BIT; + + VkResult res = vkCreateCommandPool(device, &cmd_pool_info, NULL, &split_draw_list_allocators.write[i].command_pool); + ERR_FAIL_COND_V(res, ERR_CANT_CREATE); + + for (int j = 0; j < frame_count; j++) { + + VkCommandBuffer command_buffer; + + VkCommandBufferAllocateInfo cmdbuf; + //no command buffer exists, create it. + cmdbuf.sType = VK_STRUCTURE_TYPE_COMMAND_BUFFER_ALLOCATE_INFO; + cmdbuf.pNext = NULL; + cmdbuf.commandPool = split_draw_list_allocators[i].command_pool; + cmdbuf.level = VK_COMMAND_BUFFER_LEVEL_SECONDARY; + cmdbuf.commandBufferCount = 1; + + VkResult err = vkAllocateCommandBuffers(device, &cmdbuf, &command_buffer); + ERR_FAIL_COND_V(err, ERR_CANT_CREATE); + + split_draw_list_allocators.write[i].command_buffers.push_back(command_buffer); + } + } + } + + VkFramebuffer vkframebuffer; + VkRenderPass render_pass; + + Error err = _draw_list_setup_framebuffer(framebuffer, p_initial_color_action, p_final_color_action, p_initial_depth_action, p_final_depth_action, &vkframebuffer, &render_pass); + ERR_FAIL_COND_V(err != OK, ERR_CANT_CREATE); + + VkCommandBuffer frame_command_buffer = frames[frame].draw_command_buffer; + err = _draw_list_render_pass_begin(framebuffer, p_initial_color_action, p_final_color_action, p_initial_depth_action, p_final_depth_action, p_clear_color_values, p_clear_depth, p_clear_stencil, viewport_offset, viewport_size, vkframebuffer, render_pass, frame_command_buffer, VK_SUBPASS_CONTENTS_SECONDARY_COMMAND_BUFFERS); + + if (err != OK) { + return ERR_CANT_CREATE; + } + + draw_list = memnew_arr(DrawList, p_splits); + draw_list_count = p_splits; + draw_list_split = true; + + for (uint32_t i = 0; i < p_splits; i++) { + + //take a command buffer and initialize it + VkCommandBuffer command_buffer = split_draw_list_allocators[p_splits].command_buffers[frame]; + + VkCommandBufferInheritanceInfo inheritance_info; + inheritance_info.sType = VK_STRUCTURE_TYPE_COMMAND_BUFFER_INHERITANCE_INFO; + inheritance_info.pNext = NULL; + inheritance_info.renderPass = render_pass; + inheritance_info.subpass = 0; + inheritance_info.framebuffer = vkframebuffer; + inheritance_info.occlusionQueryEnable = false; + inheritance_info.queryFlags = 0; //? + inheritance_info.pipelineStatistics = 0; + + VkCommandBufferBeginInfo cmdbuf_begin; + cmdbuf_begin.sType = VK_STRUCTURE_TYPE_COMMAND_BUFFER_BEGIN_INFO; + cmdbuf_begin.pNext = NULL; + cmdbuf_begin.flags = VK_COMMAND_BUFFER_USAGE_SIMULTANEOUS_USE_BIT | VK_COMMAND_BUFFER_USAGE_RENDER_PASS_CONTINUE_BIT; + cmdbuf_begin.pInheritanceInfo = &inheritance_info; + + VkResult res = vkResetCommandBuffer(command_buffer, 0); + if (res) { + memdelete_arr(draw_list); + draw_list = NULL; + ERR_FAIL_V(ERR_CANT_CREATE); + } + + res = vkBeginCommandBuffer(command_buffer, &cmdbuf_begin); + if (res) { + memdelete_arr(draw_list); + draw_list = NULL; + ERR_FAIL_V(ERR_CANT_CREATE); + } + + draw_list[i].command_buffer = command_buffer; +#ifdef DEBUG_ENABLED + draw_list[i].validation.framebuffer_format = framebuffer->format_id; +#endif + + if (i == 0 && (needs_clear_color || needs_clear_depth)) { + _draw_list_insert_clear_region(draw_list, framebuffer, viewport_offset, viewport_size, needs_clear_color, p_clear_color_values, needs_clear_depth, p_clear_depth, p_clear_stencil); + } + + VkViewport viewport; + viewport.x = viewport_offset.x; + viewport.y = viewport_offset.y; + viewport.width = viewport_size.width; + viewport.height = viewport_size.height; + viewport.minDepth = 0; + viewport.maxDepth = 1.0; + + vkCmdSetViewport(command_buffer, 0, 1, &viewport); + + VkRect2D scissor; + scissor.offset.x = viewport_offset.x; + scissor.offset.y = viewport_offset.y; + scissor.extent.width = viewport_size.width; + scissor.extent.height = viewport_size.height; + + vkCmdSetScissor(command_buffer, 0, 1, &scissor); + r_split_ids[i] = (DrawListID(1) << DrawListID(ID_TYPE_SPLIT_DRAW_LIST)) + i; + + draw_list[i].viewport = Rect2i(viewport_offset, viewport_size); + } + + return OK; +} + +RenderingDeviceVulkan::DrawList *RenderingDeviceVulkan::_get_draw_list_ptr(DrawListID p_id) { + + if (p_id < 0) { + return NULL; + } + + if (!draw_list) { + return NULL; + } else if (p_id == ID_TYPE_DRAW_LIST) { + if (draw_list_split) { + return NULL; + } + return draw_list; + } else if (p_id >> DrawListID(ID_BASE_SHIFT) == ID_TYPE_SPLIT_DRAW_LIST) { + if (!draw_list_split) { + return NULL; + } + + uint64_t index = p_id & ((DrawListID(1) << DrawListID(ID_BASE_SHIFT)) - 1); //mask + + if (index >= draw_list_count) { + return NULL; + } + + return &draw_list[index]; + } else { + return NULL; + } +} + +void RenderingDeviceVulkan::draw_list_bind_render_pipeline(DrawListID p_list, RID p_render_pipeline) { + + DrawList *dl = _get_draw_list_ptr(p_list); + ERR_FAIL_COND(!dl); +#ifdef DEBUG_ENABLED + ERR_FAIL_COND_MSG(!dl->validation.active, "Submitted Draw Lists can no longer be modified."); +#endif + + const RenderPipeline *pipeline = render_pipeline_owner.getornull(p_render_pipeline); + ERR_FAIL_COND(!pipeline); +#ifdef DEBUG_ENABLED + ERR_FAIL_COND(pipeline->validation.framebuffer_format != dl->validation.framebuffer_format); +#endif + + if (p_render_pipeline == dl->state.pipeline) { + return; //redundant state, return. + } + + dl->state.pipeline = p_render_pipeline; + dl->state.pipeline_layout = pipeline->pipeline_layout; + + 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. + + //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 + dl->state.set_count = MAX(dl->state.set_count, pcount); + const uint32_t *pformats = pipeline->set_formats.ptr(); //pipeline set formats + + 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 (!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]; + sets_valid = false; + } + } + + for (uint32_t i = pcount; i < dl->state.set_count; i++) { + //unbind the ones above (not used) if exist + dl->state.sets[i].bound = false; + } + + dl->state.set_count = pcount; //update set count + + if (pipeline->push_constant_size) { + dl->state.pipeline_push_constant_stages = pipeline->push_constant_stages; +#ifdef DEBUG_ENABLED + dl->validation.pipeline_push_constant_suppplied = false; +#endif + } + + dl->state.pipeline_shader = pipeline->shader; + } + +#ifdef DEBUG_ENABLED + //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; + dl->validation.pipeline_uses_restart_indices = pipeline->validation.uses_restart_indices; + dl->validation.pipeline_primitive_divisor = pipeline->validation.primitive_divisor; + dl->validation.pipeline_primitive_minimum = pipeline->validation.primitive_minimum; + dl->validation.pipeline_push_constant_size = pipeline->push_constant_size; +#endif +} + +void RenderingDeviceVulkan::draw_list_bind_uniform_set(DrawListID p_list, RID p_uniform_set, uint32_t p_index) { + +#ifdef DEBUG_ENABLED + ERR_FAIL_COND_MSG(p_index >= limits.maxBoundDescriptorSets || p_index > MAX_UNIFORM_SETS, + "Attempting to bind a descriptor set (" + itos(p_index) + ") greater than what the hardware supports (" + itos(limits.maxBoundDescriptorSets) + ")."); +#endif + DrawList *dl = _get_draw_list_ptr(p_list); + ERR_FAIL_COND(!dl); + +#ifdef DEBUG_ENABLED + ERR_FAIL_COND_MSG(!dl->validation.active, "Submitted Draw Lists can no longer be modified."); +#endif + + const UniformSet *uniform_set = uniform_set_owner.getornull(p_uniform_set); + ERR_FAIL_COND(!uniform_set); + + if (p_index > dl->state.set_count) { + 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].uniform_set_format = uniform_set->format; + dl->state.sets[p_index].uniform_set = p_uniform_set; + +#ifdef DEBUG_ENABLED + { //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(); + const RID *bound_ptr = draw_list_bound_textures.ptr(); + for (uint32_t i = 0; i < attachable_count; i++) { + for (uint32_t j = 0; j < bound_count; j++) { + ERR_FAIL_COND_MSG(attachable_ptr[i] == bound_ptr[j], + "Attempted to use the same texture in framebuffer attachment and a uniform set, this is not allowed."); + } + } + } +#endif +} + +void RenderingDeviceVulkan::draw_list_bind_vertex_array(DrawListID p_list, RID p_vertex_array) { + DrawList *dl = _get_draw_list_ptr(p_list); + ERR_FAIL_COND(!dl); +#ifdef DEBUG_ENABLED + ERR_FAIL_COND_MSG(!dl->validation.active, "Submitted Draw Lists can no longer be modified."); +#endif + + const VertexArray *vertex_array = vertex_array_owner.getornull(p_vertex_array); + ERR_FAIL_COND(!vertex_array); + + if (dl->state.vertex_array == p_vertex_array) { + return; //already set + } + + dl->state.vertex_array = p_vertex_array; + +#ifdef DEBUG_ENABLED + dl->validation.vertex_format = vertex_array->description; + dl->validation.vertex_max_instances_allowed = vertex_array->max_instances_allowed; +#endif + dl->validation.vertex_array_size = vertex_array->vertex_count; + vkCmdBindVertexBuffers(dl->command_buffer, 0, vertex_array->buffers.size(), vertex_array->buffers.ptr(), vertex_array->offsets.ptr()); +} +void RenderingDeviceVulkan::draw_list_bind_index_array(DrawListID p_list, RID p_index_array) { + + DrawList *dl = _get_draw_list_ptr(p_list); + ERR_FAIL_COND(!dl); +#ifdef DEBUG_ENABLED + ERR_FAIL_COND_MSG(!dl->validation.active, "Submitted Draw Lists can no longer be modified."); +#endif + + const IndexArray *index_array = index_array_owner.getornull(p_index_array); + ERR_FAIL_COND(!index_array); + + if (dl->state.index_array == p_index_array) { + return; //already set + } + + dl->state.index_array = p_index_array; +#ifdef DEBUG_ENABLED + dl->validation.index_array_max_index = index_array->max_index; +#endif + dl->validation.index_array_size = index_array->indices; + dl->validation.index_array_offset = index_array->offset; + + vkCmdBindIndexBuffer(dl->command_buffer, index_array->buffer, index_array->offset, index_array->index_type); +} + +void RenderingDeviceVulkan::draw_list_set_line_width(DrawListID p_list, float p_width) { + + DrawList *dl = _get_draw_list_ptr(p_list); + ERR_FAIL_COND(!dl); +#ifdef DEBUG_ENABLED + ERR_FAIL_COND_MSG(!dl->validation.active, "Submitted Draw Lists can no longer be modified."); +#endif + + vkCmdSetLineWidth(dl->command_buffer, p_width); +} + +void RenderingDeviceVulkan::draw_list_set_push_constant(DrawListID p_list, void *p_data, uint32_t p_data_size) { + DrawList *dl = _get_draw_list_ptr(p_list); + ERR_FAIL_COND(!dl); + +#ifdef DEBUG_ENABLED + ERR_FAIL_COND_MSG(!dl->validation.active, "Submitted Draw Lists can no longer be modified."); +#endif + +#ifdef DEBUG_ENABLED + ERR_FAIL_COND_MSG(p_data_size != dl->validation.pipeline_push_constant_size, + "This render pipeline requires (" + itos(dl->validation.pipeline_push_constant_size) + ") bytes of push constant data, supplied: (" + itos(p_data_size) + ")"); +#endif + vkCmdPushConstants(dl->command_buffer, dl->state.pipeline_layout, dl->state.pipeline_push_constant_stages, 0, p_data_size, p_data); +#ifdef DEBUG_ENABLED + dl->validation.pipeline_push_constant_suppplied = true; +#endif +} + +void RenderingDeviceVulkan::draw_list_draw(DrawListID p_list, bool p_use_indices, uint32_t p_instances, uint32_t p_procedural_vertices) { + + DrawList *dl = _get_draw_list_ptr(p_list); + ERR_FAIL_COND(!dl); +#ifdef DEBUG_ENABLED + ERR_FAIL_COND_MSG(!dl->validation.active, "Submitted Draw Lists can no longer be modified."); +#endif + +#ifdef DEBUG_ENABLED + 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 + 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 + 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 amount of instances is valid + ERR_FAIL_COND_MSG(p_instances > dl->validation.vertex_max_instances_allowed, + "Amount of instances requested (" + itos(p_instances) + " is larger than the maximum amount suported 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 + ERR_FAIL_COND_MSG(!dl->validation.pipeline_push_constant_suppplied, + "The shader in this pipeline requires a push constant to be set before drawing, but it's not present."); + } + +#endif + + //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 + } +#ifdef DEBUG_ENABLED + if (dl->state.sets[i].pipeline_expected_format != dl->state.sets[i].uniform_set_format) { + + if (dl->state.sets[i].uniform_set_format == 0) { + ERR_FAIL_MSG("Uniforms were never supplied for set (" + itos(i) + ") at the time of drawing, which are required by the pipeline"); + } else if (uniform_set_owner.owns(dl->state.sets[i].uniform_set)) { + UniformSet *us = uniform_set_owner.getornull(dl->state.sets[i].uniform_set); + ERR_FAIL_MSG("Uniforms supplied for set (" + itos(i) + "):\n" + _shader_uniform_debug(us->shader_id, us->shader_set) + "\nare not the same format as required by the pipeline shader. Pipeline shader requires the following bindings:\n" + _shader_uniform_debug(dl->state.pipeline_shader)); + } else { + ERR_FAIL_MSG("Uniforms supplied for set (" + itos(i) + ", which was was just freed) are not the same format as required by the pipeline shader. Pipeline shader requires the following bindings:\n" + _shader_uniform_debug(dl->state.pipeline_shader)); + } + } +#endif + if (!dl->state.sets[i].bound) { + //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, NULL); + dl->state.sets[i].bound = true; + } + } + + if (p_use_indices) { + +#ifdef DEBUG_ENABLED + ERR_FAIL_COND_MSG(p_procedural_vertices > 0, + "Procedural vertices can't be used together with 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 + uint32_t to_draw = dl->validation.index_array_size; + +#ifdef DEBUG_ENABLED + ERR_FAIL_COND_MSG(to_draw < dl->validation.pipeline_primitive_minimum, + "Too few indices (" + itos(to_draw) + ") for the render primitive set in the render pipeline (" + itos(dl->validation.pipeline_primitive_minimum) + ")."); + + ERR_FAIL_COND_MSG((to_draw % dl->validation.pipeline_primitive_divisor) != 0, + "Index amount (" + itos(to_draw) + ") must be a multiple of the amount of indices required by the render primitive (" + itos(dl->validation.pipeline_primitive_divisor) + ")."); +#endif + vkCmdDrawIndexed(dl->command_buffer, to_draw, p_instances, dl->validation.index_array_offset, 0, 0); + } else { + + uint32_t to_draw; + + if (p_procedural_vertices > 0) { +#ifdef DEBUG_ENABLED + ERR_FAIL_COND_MSG(dl->validation.pipeline_vertex_format == INVALID_ID, + "Procedural vertices requested, but pipeline expects a vertex array."); +#endif + to_draw = p_procedural_vertices; + } else { + +#ifdef DEBUG_ENABLED + ERR_FAIL_COND_MSG(dl->validation.pipeline_vertex_format == INVALID_ID, + "Draw command lacks indices, but pipeline format does not use vertices."); +#endif + to_draw = dl->validation.vertex_array_size; + } + +#ifdef DEBUG_ENABLED + ERR_FAIL_COND_MSG(to_draw < dl->validation.pipeline_primitive_minimum, + "Too few vertices (" + itos(to_draw) + ") for the render primitive set in the render pipeline (" + itos(dl->validation.pipeline_primitive_minimum) + ")."); + + ERR_FAIL_COND_MSG((to_draw % dl->validation.pipeline_primitive_divisor) != 0, + "Vertex amount (" + itos(to_draw) + ") must be a multiple of the amount of vertices required by the render primitive (" + itos(dl->validation.pipeline_primitive_divisor) + ")."); +#endif + + vkCmdDraw(dl->command_buffer, to_draw, p_instances, 0, 0); + } +} + +void RenderingDeviceVulkan::draw_list_enable_scissor(DrawListID p_list, const Rect2 &p_rect) { + DrawList *dl = _get_draw_list_ptr(p_list); + + ERR_FAIL_COND(!dl); +#ifdef DEBUG_ENABLED + ERR_FAIL_COND_MSG(!dl->validation.active, "Submitted Draw Lists can no longer be modified."); +#endif + Rect2i rect = p_rect; + rect.position += dl->viewport.position; + + rect = dl->viewport.clip(rect); + + if (rect.get_area() == 0) { + return; + } + VkRect2D scissor; + scissor.offset.x = rect.position.x; + scissor.offset.y = rect.position.y; + scissor.extent.width = rect.size.width; + scissor.extent.height = rect.size.height; + + vkCmdSetScissor(dl->command_buffer, 0, 1, &scissor); +} +void RenderingDeviceVulkan::draw_list_disable_scissor(DrawListID p_list) { + DrawList *dl = _get_draw_list_ptr(p_list); + ERR_FAIL_COND(!dl); +#ifdef DEBUG_ENABLED + ERR_FAIL_COND_MSG(!dl->validation.active, "Submitted Draw Lists can no longer be modified."); +#endif + + VkRect2D scissor; + scissor.offset.x = dl->viewport.position.x; + scissor.offset.y = dl->viewport.position.y; + scissor.extent.width = dl->viewport.size.width; + scissor.extent.height = dl->viewport.size.height; + vkCmdSetScissor(dl->command_buffer, 0, 1, &scissor); +} + +void RenderingDeviceVulkan::draw_list_end() { + + _THREAD_SAFE_METHOD_ + + ERR_FAIL_COND_MSG(!draw_list, "Immediate draw list is already inactive."); + + if (draw_list_split) { + //send all command buffers + VkCommandBuffer *command_buffers = (VkCommandBuffer *)alloca(sizeof(VkCommandBuffer) * draw_list_count); + for (uint32_t i = 0; i < draw_list_count; i++) { + vkEndCommandBuffer(draw_list->command_buffer); + command_buffers[i] = draw_list->command_buffer; + } + + vkCmdExecuteCommands(frames[frame].draw_command_buffer, draw_list_count, command_buffers); + vkCmdEndRenderPass(frames[frame].draw_command_buffer); + memdelete_arr(draw_list); + draw_list = NULL; + + } else { + //just end the list + vkCmdEndRenderPass(draw_list->command_buffer); + memdelete(draw_list); + draw_list = NULL; + } + + for (int i = 0; i < draw_list_bound_textures.size(); i++) { + Texture *texture = texture_owner.getornull(draw_list_bound_textures[i]); + ERR_CONTINUE(!texture); //wtf + if (draw_list_unbind_color_textures && (texture->usage_flags & TEXTURE_USAGE_COLOR_ATTACHMENT_BIT)) { + texture->bound = false; + } + if (draw_list_unbind_depth_textures && (texture->usage_flags & TEXTURE_USAGE_DEPTH_STENCIL_ATTACHMENT_BIT)) { + texture->bound = false; + } + } + + draw_list_bound_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 + _memory_barrier(VK_PIPELINE_STAGE_COLOR_ATTACHMENT_OUTPUT_BIT | VK_PIPELINE_STAGE_LATE_FRAGMENT_TESTS_BIT, VK_PIPELINE_STAGE_VERTEX_INPUT_BIT | VK_PIPELINE_STAGE_COMPUTE_SHADER_BIT | VK_PIPELINE_STAGE_TRANSFER_BIT, VK_ACCESS_DEPTH_STENCIL_ATTACHMENT_WRITE_BIT | VK_ACCESS_COLOR_ATTACHMENT_WRITE_BIT, VK_ACCESS_INDEX_READ_BIT | VK_ACCESS_VERTEX_ATTRIBUTE_READ_BIT | VK_ACCESS_TRANSFER_READ_BIT | VK_ACCESS_SHADER_READ_BIT | VK_ACCESS_SHADER_WRITE_BIT, true); +#endif +} + +/***********************/ +/**** COMPUTE LISTS ****/ +/***********************/ + +RenderingDevice::ComputeListID RenderingDeviceVulkan::compute_list_begin() { + + ERR_FAIL_COND_V_MSG(draw_list != NULL, INVALID_ID, "Only one draw list can be active at the same time."); + ERR_FAIL_COND_V_MSG(compute_list != NULL, INVALID_ID, "Only one draw/compute list can be active at the same time."); + + compute_list = memnew(ComputeList); + compute_list->command_buffer = frames[frame].draw_command_buffer; + + return ID_TYPE_COMPUTE_LIST; +} + +void RenderingDeviceVulkan::compute_list_bind_compute_pipeline(ComputeListID p_list, RID p_compute_pipeline) { + ERR_FAIL_COND(p_list != ID_TYPE_COMPUTE_LIST); + ERR_FAIL_COND(!compute_list); + + ComputeList *cl = compute_list; + + const ComputePipeline *pipeline = compute_pipeline_owner.getornull(p_compute_pipeline); + ERR_FAIL_COND(!pipeline); + + if (p_compute_pipeline == cl->state.pipeline) { + return; //redundant state, return. + } + + cl->state.pipeline = p_compute_pipeline; + cl->state.pipeline_layout = pipeline->pipeline_layout; + + 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. + + //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 + cl->state.set_count = MAX(cl->state.set_count, pcount); + const uint32_t *pformats = pipeline->set_formats.ptr(); //pipeline set formats + + 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 (!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]; + sets_valid = false; + } + } + + for (uint32_t i = pcount; i < cl->state.set_count; i++) { + //unbind the ones above (not used) if exist + cl->state.sets[i].bound = false; + } + + cl->state.set_count = pcount; //update set count + + if (pipeline->push_constant_size) { + cl->state.pipeline_push_constant_stages = pipeline->push_constant_stages; +#ifdef DEBUG_ENABLED + cl->validation.pipeline_push_constant_suppplied = false; +#endif + } + + cl->state.pipeline_shader = pipeline->shader; + } + +#ifdef DEBUG_ENABLED + //update compute pass pipeline info + cl->validation.pipeline_active = true; + cl->validation.pipeline_push_constant_size = pipeline->push_constant_size; +#endif +} +void RenderingDeviceVulkan::compute_list_bind_uniform_set(ComputeListID p_list, RID p_uniform_set, uint32_t p_index) { + ERR_FAIL_COND(p_list != ID_TYPE_COMPUTE_LIST); + ERR_FAIL_COND(!compute_list); + + ComputeList *cl = compute_list; + +#ifdef DEBUG_ENABLED + ERR_FAIL_COND_MSG(p_index >= limits.maxBoundDescriptorSets || p_index > MAX_UNIFORM_SETS, + "Attempting to bind a descriptor set (" + itos(p_index) + ") greater than what the hardware supports (" + itos(limits.maxBoundDescriptorSets) + ")."); +#endif + +#ifdef DEBUG_ENABLED + ERR_FAIL_COND_MSG(!cl->validation.active, "Submitted Compute Lists can no longer be modified."); +#endif + + UniformSet *uniform_set = uniform_set_owner.getornull(p_uniform_set); + ERR_FAIL_COND(!uniform_set); + + if (p_index > cl->state.set_count) { + 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].uniform_set_format = uniform_set->format; + cl->state.sets[p_index].uniform_set = p_uniform_set; + + uint32_t textures_to_sampled_count = uniform_set->mutable_sampled_textures.size(); + Texture **textures_to_sampled = uniform_set->mutable_sampled_textures.ptrw(); + + for (uint32_t i = 0; i < textures_to_sampled_count; i++) { + if (textures_to_sampled[i]->layout != VK_IMAGE_LAYOUT_SHADER_READ_ONLY_OPTIMAL) { + + VkImageMemoryBarrier image_memory_barrier; + image_memory_barrier.sType = VK_STRUCTURE_TYPE_IMAGE_MEMORY_BARRIER; + image_memory_barrier.pNext = NULL; + image_memory_barrier.srcAccessMask = VK_ACCESS_SHADER_READ_BIT | VK_ACCESS_SHADER_WRITE_BIT; + image_memory_barrier.dstAccessMask = VK_ACCESS_SHADER_READ_BIT | VK_ACCESS_SHADER_WRITE_BIT; + image_memory_barrier.oldLayout = textures_to_sampled[i]->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 = textures_to_sampled[i]->image; + image_memory_barrier.subresourceRange.aspectMask = textures_to_sampled[i]->read_aspect_mask; + image_memory_barrier.subresourceRange.baseMipLevel = 0; + image_memory_barrier.subresourceRange.levelCount = textures_to_sampled[i]->mipmaps; + image_memory_barrier.subresourceRange.baseArrayLayer = 0; + image_memory_barrier.subresourceRange.layerCount = textures_to_sampled[i]->layers; + + vkCmdPipelineBarrier(cl->command_buffer, VK_PIPELINE_STAGE_VERTEX_SHADER_BIT | VK_PIPELINE_STAGE_COMPUTE_SHADER_BIT, VK_PIPELINE_STAGE_COMPUTE_SHADER_BIT, 0, 0, NULL, 0, NULL, 1, &image_memory_barrier); + + textures_to_sampled[i]->layout = VK_IMAGE_LAYOUT_SHADER_READ_ONLY_OPTIMAL; + + cl->state.textures_to_sampled_layout.erase(textures_to_sampled[i]); + } + } + + uint32_t textures_to_storage_count = uniform_set->mutable_storage_textures.size(); + Texture **textures_to_storage = uniform_set->mutable_storage_textures.ptrw(); + + for (uint32_t i = 0; i < textures_to_storage_count; i++) { + if (textures_to_storage[i]->layout != VK_IMAGE_LAYOUT_GENERAL) { + + VkImageMemoryBarrier image_memory_barrier; + image_memory_barrier.sType = VK_STRUCTURE_TYPE_IMAGE_MEMORY_BARRIER; + image_memory_barrier.pNext = NULL; + image_memory_barrier.srcAccessMask = VK_ACCESS_SHADER_READ_BIT | VK_ACCESS_SHADER_WRITE_BIT; + image_memory_barrier.dstAccessMask = VK_ACCESS_SHADER_READ_BIT | VK_ACCESS_SHADER_WRITE_BIT; + image_memory_barrier.oldLayout = textures_to_storage[i]->layout; + image_memory_barrier.newLayout = VK_IMAGE_LAYOUT_GENERAL; + + image_memory_barrier.srcQueueFamilyIndex = VK_QUEUE_FAMILY_IGNORED; + image_memory_barrier.dstQueueFamilyIndex = VK_QUEUE_FAMILY_IGNORED; + image_memory_barrier.image = textures_to_storage[i]->image; + image_memory_barrier.subresourceRange.aspectMask = textures_to_storage[i]->read_aspect_mask; + image_memory_barrier.subresourceRange.baseMipLevel = 0; + image_memory_barrier.subresourceRange.levelCount = textures_to_storage[i]->mipmaps; + image_memory_barrier.subresourceRange.baseArrayLayer = 0; + image_memory_barrier.subresourceRange.layerCount = textures_to_storage[i]->layers; + + vkCmdPipelineBarrier(cl->command_buffer, VK_PIPELINE_STAGE_COMPUTE_SHADER_BIT | VK_PIPELINE_STAGE_FRAGMENT_SHADER_BIT, VK_PIPELINE_STAGE_COMPUTE_SHADER_BIT, 0, 0, NULL, 0, NULL, 1, &image_memory_barrier); + + 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 + } + } + +#if 0 + { //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(); + const RID *bound_ptr = draw_list_bound_textures.ptr(); + for (uint32_t i = 0; i < attachable_count; i++) { + for (uint32_t j = 0; j < bound_count; j++) { + ERR_FAIL_COND_MSG(attachable_ptr[i] == bound_ptr[j], + "Attempted to use the same texture in framebuffer attachment and a uniform set, this is not allowed."); + } + } + } +#endif +} + +void RenderingDeviceVulkan::compute_list_set_push_constant(ComputeListID p_list, void *p_data, uint32_t p_data_size) { + ERR_FAIL_COND(p_list != ID_TYPE_COMPUTE_LIST); + ERR_FAIL_COND(!compute_list); + + ComputeList *cl = compute_list; + +#ifdef DEBUG_ENABLED + ERR_FAIL_COND_MSG(!cl->validation.active, "Submitted Compute Lists can no longer be modified."); +#endif + +#ifdef DEBUG_ENABLED + ERR_FAIL_COND_MSG(p_data_size != cl->validation.pipeline_push_constant_size, + "This compute pipeline requires (" + itos(cl->validation.pipeline_push_constant_size) + ") bytes of push constant data, supplied: (" + itos(p_data_size) + ")"); +#endif + vkCmdPushConstants(cl->command_buffer, cl->state.pipeline_layout, cl->state.pipeline_push_constant_stages, 0, p_data_size, p_data); +#ifdef DEBUG_ENABLED + cl->validation.pipeline_push_constant_suppplied = true; +#endif +} +void RenderingDeviceVulkan::compute_list_dispatch(ComputeListID p_list, uint32_t p_x_groups, uint32_t p_y_groups, uint32_t p_z_groups) { + ERR_FAIL_COND(p_list != ID_TYPE_COMPUTE_LIST); + ERR_FAIL_COND(!compute_list); + + ComputeList *cl = compute_list; + +#ifdef DEBUG_ENABLED + ERR_FAIL_COND_MSG(p_x_groups > limits.maxComputeWorkGroupCount[0], + "Dispatch amount of X compute groups (" + itos(p_x_groups) + ") is larger than device limit (" + itos(limits.maxComputeWorkGroupCount[0]) + ")"); + ERR_FAIL_COND_MSG(p_y_groups > limits.maxComputeWorkGroupCount[1], + "Dispatch amount of Y compute groups (" + itos(p_x_groups) + ") is larger than device limit (" + itos(limits.maxComputeWorkGroupCount[0]) + ")"); + ERR_FAIL_COND_MSG(p_z_groups > limits.maxComputeWorkGroupCount[2], + "Dispatch amount of Z compute groups (" + itos(p_x_groups) + ") is larger than device limit (" + itos(limits.maxComputeWorkGroupCount[0]) + ")"); + + ERR_FAIL_COND_MSG(!cl->validation.active, "Submitted Compute Lists can no longer be modified."); +#endif + +#ifdef DEBUG_ENABLED + + 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 + ERR_FAIL_COND_MSG(!cl->validation.pipeline_push_constant_suppplied, + "The shader in this pipeline requires a push constant to be set before drawing, but it's not present."); + } + +#endif + + //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 + } +#ifdef DEBUG_ENABLED + if (cl->state.sets[i].pipeline_expected_format != cl->state.sets[i].uniform_set_format) { + + if (cl->state.sets[i].uniform_set_format == 0) { + ERR_FAIL_MSG("Uniforms were never supplied for set (" + itos(i) + ") at the time of drawing, which are required by the pipeline"); + } else if (uniform_set_owner.owns(cl->state.sets[i].uniform_set)) { + UniformSet *us = uniform_set_owner.getornull(cl->state.sets[i].uniform_set); + ERR_FAIL_MSG("Uniforms supplied for set (" + itos(i) + "):\n" + _shader_uniform_debug(us->shader_id, us->shader_set) + "\nare not the same format as required by the pipeline shader. Pipeline shader requires the following bindings:\n" + _shader_uniform_debug(cl->state.pipeline_shader)); + } else { + ERR_FAIL_MSG("Uniforms supplied for set (" + itos(i) + ", which was was just freed) are not the same format as required by the pipeline shader. Pipeline shader requires the following bindings:\n" + _shader_uniform_debug(cl->state.pipeline_shader)); + } + } +#endif + if (!cl->state.sets[i].bound) { + //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, NULL); + cl->state.sets[i].bound = true; + } + } + + vkCmdDispatch(cl->command_buffer, p_x_groups, p_y_groups, p_z_groups); +} + +void RenderingDeviceVulkan::compute_list_add_barrier(ComputeListID p_list) { +#ifdef FORCE_FULL_BARRIER + _full_barrier(true); +#else + _memory_barrier(VK_PIPELINE_STAGE_COMPUTE_SHADER_BIT, VK_PIPELINE_STAGE_COMPUTE_SHADER_BIT, VK_ACCESS_SHADER_WRITE_BIT, VK_ACCESS_SHADER_READ_BIT, true); +#endif +} + +void RenderingDeviceVulkan::compute_list_end() { + ERR_FAIL_COND(!compute_list); + + for (Set<Texture *>::Element *E = compute_list->state.textures_to_sampled_layout.front(); E; E = E->next()) { + + VkImageMemoryBarrier image_memory_barrier; + image_memory_barrier.sType = VK_STRUCTURE_TYPE_IMAGE_MEMORY_BARRIER; + image_memory_barrier.pNext = NULL; + image_memory_barrier.srcAccessMask = VK_ACCESS_SHADER_READ_BIT | VK_ACCESS_SHADER_WRITE_BIT; + image_memory_barrier.dstAccessMask = VK_ACCESS_SHADER_READ_BIT; + image_memory_barrier.oldLayout = E->get()->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 = 0; + image_memory_barrier.subresourceRange.levelCount = E->get()->mipmaps; + image_memory_barrier.subresourceRange.baseArrayLayer = 0; + image_memory_barrier.subresourceRange.layerCount = E->get()->layers; + + vkCmdPipelineBarrier(compute_list->command_buffer, VK_PIPELINE_STAGE_COMPUTE_SHADER_BIT, VK_PIPELINE_STAGE_VERTEX_SHADER_BIT | VK_PIPELINE_STAGE_COMPUTE_SHADER_BIT, 0, 0, NULL, 0, NULL, 1, &image_memory_barrier); + + E->get()->layout = VK_IMAGE_LAYOUT_SHADER_READ_ONLY_OPTIMAL; + } + + memdelete(compute_list); + compute_list = NULL; +#ifdef FORCE_FULL_BARRIER + _full_barrier(true); +#else + _memory_barrier(VK_PIPELINE_STAGE_COMPUTE_SHADER_BIT, VK_PIPELINE_STAGE_VERTEX_INPUT_BIT | VK_PIPELINE_STAGE_TRANSFER_BIT, VK_ACCESS_SHADER_WRITE_BIT, VK_PIPELINE_STAGE_COMPUTE_SHADER_BIT | VK_ACCESS_INDEX_READ_BIT | VK_ACCESS_VERTEX_ATTRIBUTE_READ_BIT | VK_ACCESS_TRANSFER_READ_BIT, true); +#endif +} + +#if 0 +void RenderingDeviceVulkan::draw_list_render_secondary_to_framebuffer(ID p_framebuffer, ID *p_draw_lists, uint32_t p_draw_list_count, InitialAction p_initial_action, FinalAction p_final_action, const Vector<Variant> &p_clear_colors) { + + VkCommandBuffer frame_cmdbuf = frames[frame].frame_buffer; + ERR_FAIL_COND(!frame_cmdbuf); + + VkRenderPassBeginInfo render_pass_begin; + render_pass_begin.sType = VK_STRUCTURE_TYPE_RENDER_PASS_BEGIN_INFO; + render_pass_begin.pNext = NULL; + render_pass_begin.renderPass = context->get_render_pass(); + render_pass_begin.framebuffer = context->get_frame_framebuffer(frame); + + render_pass_begin.renderArea.extent.width = context->get_screen_width(p_screen); + render_pass_begin.renderArea.extent.height = context->get_screen_height(p_screen); + render_pass_begin.renderArea.offset.x = 0; + render_pass_begin.renderArea.offset.y = 0; + + render_pass_begin.clearValueCount = 1; + + VkClearValue clear_value; + clear_value.color.float32[0] = p_clear_color.r; + clear_value.color.float32[1] = p_clear_color.g; + clear_value.color.float32[2] = p_clear_color.b; + clear_value.color.float32[3] = p_clear_color.a; + + render_pass_begin.pClearValues = &clear_value; + + vkCmdBeginRenderPass(frame_cmdbuf, &render_pass_begin, VK_SUBPASS_CONTENTS_SECONDARY_COMMAND_BUFFERS); + + ID screen_format = screen_get_framebuffer_format(); + { + + VkCommandBuffer *command_buffers = (VkCommandBuffer *)alloca(sizeof(VkCommandBuffer) * p_draw_list_count); + uint32_t command_buffer_count = 0; + + for (uint32_t i = 0; i < p_draw_list_count; i++) { + DrawList *dl = _get_draw_list_ptr(p_draw_lists[i]); + ERR_CONTINUE_MSG(!dl, "Draw list index (" + itos(i) + ") is not a valid draw list ID."); + ERR_CONTINUE_MSG(dl->validation.framebuffer_format != p_format_check, + "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. + vkEndCommandBuffer(dl->command_buffer); + dl->validation.active = false; + } + + command_buffers[command_buffer_count++] = dl->command_buffer; + } + + print_line("to draw: " + itos(command_buffer_count)); + vkCmdExecuteCommands(p_primary, command_buffer_count, command_buffers); + } + + vkCmdEndRenderPass(frame_cmdbuf); + +} +#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) + if (texture_owner.owns(p_id)) { + Texture *texture = texture_owner.getornull(p_id); + frames[frame].textures_to_dispose_of.push_back(*texture); + texture_owner.free(p_id); + } else if (framebuffer_owner.owns(p_id)) { + Framebuffer *framebuffer = framebuffer_owner.getornull(p_id); + frames[frame].framebuffers_to_dispose_of.push_back(*framebuffer); + framebuffer_owner.free(p_id); + } else if (sampler_owner.owns(p_id)) { + VkSampler *sampler = sampler_owner.getornull(p_id); + frames[frame].samplers_to_dispose_of.push_back(*sampler); + sampler_owner.free(p_id); + } else if (vertex_buffer_owner.owns(p_id)) { + Buffer *vertex_buffer = vertex_buffer_owner.getornull(p_id); + frames[frame].buffers_to_dispose_of.push_back(*vertex_buffer); + vertex_buffer_owner.free(p_id); + } else if (vertex_array_owner.owns(p_id)) { + vertex_array_owner.free(p_id); + } else if (index_buffer_owner.owns(p_id)) { + IndexBuffer *index_buffer = index_buffer_owner.getornull(p_id); + Buffer b; + b.allocation = index_buffer->allocation; + b.buffer = index_buffer->buffer; + b.size = index_buffer->size; + frames[frame].buffers_to_dispose_of.push_back(b); + index_buffer_owner.free(p_id); + } else if (index_array_owner.owns(p_id)) { + index_array_owner.free(p_id); + } else if (shader_owner.owns(p_id)) { + Shader *shader = shader_owner.getornull(p_id); + frames[frame].shaders_to_dispose_of.push_back(*shader); + shader_owner.free(p_id); + } else if (uniform_buffer_owner.owns(p_id)) { + Buffer *uniform_buffer = uniform_buffer_owner.getornull(p_id); + frames[frame].buffers_to_dispose_of.push_back(*uniform_buffer); + uniform_buffer_owner.free(p_id); + } else if (texture_buffer_owner.owns(p_id)) { + TextureBuffer *texture_buffer = texture_buffer_owner.getornull(p_id); + frames[frame].buffers_to_dispose_of.push_back(texture_buffer->buffer); + frames[frame].buffer_views_to_dispose_of.push_back(texture_buffer->view); + texture_buffer_owner.free(p_id); + } else if (storage_buffer_owner.owns(p_id)) { + Buffer *storage_buffer = storage_buffer_owner.getornull(p_id); + frames[frame].buffers_to_dispose_of.push_back(*storage_buffer); + storage_buffer_owner.free(p_id); + } else if (uniform_set_owner.owns(p_id)) { + UniformSet *uniform_set = uniform_set_owner.getornull(p_id); + frames[frame].uniform_sets_to_dispose_of.push_back(*uniform_set); + uniform_set_owner.free(p_id); + } else if (render_pipeline_owner.owns(p_id)) { + RenderPipeline *pipeline = render_pipeline_owner.getornull(p_id); + frames[frame].render_pipelines_to_dispose_of.push_back(*pipeline); + render_pipeline_owner.free(p_id); + } else if (compute_pipeline_owner.owns(p_id)) { + ComputePipeline *pipeline = compute_pipeline_owner.getornull(p_id); + frames[frame].compute_pipelines_to_dispose_of.push_back(*pipeline); + compute_pipeline_owner.free(p_id); + } else { + ERR_PRINT("Attempted to free invalid ID: " + itos(p_id.get_id())); + } +} +void RenderingDeviceVulkan::free(RID p_id) { + + _THREAD_SAFE_METHOD_ + + _free_dependencies(p_id); //recursively erase dependencies first, to avoid potential API problems + _free_internal(p_id); +} +void RenderingDeviceVulkan::swap_buffers() { + + _THREAD_SAFE_METHOD_ + + { //finalize frame + + if (draw_list) { + ERR_PRINT("Found open draw list at the end of the frame, this should never happen (further drawing will likely not work)."); + } + + if (compute_list) { + 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) + vkEndCommandBuffer(frames[frame].setup_command_buffer); + vkEndCommandBuffer(frames[frame].draw_command_buffer); + } + screen_prepared = false; + } + + //swap buffers + context->swap_buffers(); + + { //advance frame + + frame = (frame + 1) % frame_count; + + //erase pending resources + _free_pending_resources(frame); + + //create setup command buffer and set as the setup buffer + + { + VkCommandBufferBeginInfo cmdbuf_begin; + cmdbuf_begin.sType = VK_STRUCTURE_TYPE_COMMAND_BUFFER_BEGIN_INFO; + cmdbuf_begin.pNext = NULL; + cmdbuf_begin.flags = VK_COMMAND_BUFFER_USAGE_ONE_TIME_SUBMIT_BIT; + cmdbuf_begin.pInheritanceInfo = NULL; + + VkResult err = vkResetCommandBuffer(frames[frame].setup_command_buffer, 0); + ERR_FAIL_COND(err); + + err = vkBeginCommandBuffer(frames[frame].setup_command_buffer, &cmdbuf_begin); + ERR_FAIL_COND(err); + context->set_setup_buffer(frames[frame].setup_command_buffer); //append now so it's added before everything else + err = vkBeginCommandBuffer(frames[frame].draw_command_buffer, &cmdbuf_begin); + ERR_FAIL_COND(err); + context->append_command_buffer(frames[frame].draw_command_buffer); + } + + //advance current frame + frames_drawn++; + //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); + SWAP(frames[frame].timestamp_names, frames[frame].timestamp_result_names); + SWAP(frames[frame].timestamp_cpu_values, frames[frame].timestamp_cpu_result_values); + } + + frames[frame].timestamp_result_count = frames[frame].timestamp_count; + frames[frame].timestamp_count = 0; + frames[frame].index = Engine::get_singleton()->get_frames_drawn(); + } +} + +void RenderingDeviceVulkan::_free_pending_resources(int p_frame) { + //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(); + + vkDestroyPipeline(device, pipeline->pipeline, NULL); + + frames[p_frame].render_pipelines_to_dispose_of.pop_front(); + } + + while (frames[p_frame].compute_pipelines_to_dispose_of.front()) { + ComputePipeline *pipeline = &frames[p_frame].compute_pipelines_to_dispose_of.front()->get(); + + vkDestroyPipeline(device, pipeline->pipeline, NULL); + + frames[p_frame].compute_pipelines_to_dispose_of.pop_front(); + } + + //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(); + + vkFreeDescriptorSets(device, uniform_set->pool->pool, 1, &uniform_set->descriptor_set); + _descriptor_pool_free(uniform_set->pool_key, uniform_set->pool); + + frames[p_frame].uniform_sets_to_dispose_of.pop_front(); + } + + //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(); + + vkDestroyBufferView(device, buffer_view, NULL); + + frames[p_frame].buffer_views_to_dispose_of.pop_front(); + } + + //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 + for (int i = 0; i < shader->sets.size(); i++) { + vkDestroyDescriptorSetLayout(device, shader->sets[i].descriptor_set_layout, NULL); + } + + //pipeline layout + vkDestroyPipelineLayout(device, shader->pipeline_layout, NULL); + + //shaders themselves + for (int i = 0; i < shader->pipeline_stages.size(); i++) { + vkDestroyShaderModule(device, shader->pipeline_stages[i].module, NULL); + } + + frames[p_frame].shaders_to_dispose_of.pop_front(); + } + + //samplers + while (frames[p_frame].samplers_to_dispose_of.front()) { + VkSampler sampler = frames[p_frame].samplers_to_dispose_of.front()->get(); + + vkDestroySampler(device, sampler, NULL); + + frames[p_frame].samplers_to_dispose_of.pop_front(); + } + + //framebuffers + while (frames[p_frame].framebuffers_to_dispose_of.front()) { + Framebuffer *framebuffer = &frames[p_frame].framebuffers_to_dispose_of.front()->get(); + + for (Map<Framebuffer::VersionKey, Framebuffer::Version>::Element *E = framebuffer->framebuffers.front(); E; E = E->next()) { + //first framebuffer, then render pass because it depends on it + vkDestroyFramebuffer(device, E->get().framebuffer, NULL); + vkDestroyRenderPass(device, E->get().render_pass, NULL); + } + + frames[p_frame].framebuffers_to_dispose_of.pop_front(); + } + + //textures + while (frames[p_frame].textures_to_dispose_of.front()) { + Texture *texture = &frames[p_frame].textures_to_dispose_of.front()->get(); + + if (texture->bound) { + WARN_PRINT("Deleted a texture while it was bound.."); + } + vkDestroyImageView(device, texture->view, NULL); + if (texture->owner.is_null()) { + //actually owns the image and the allocation too + vmaDestroyImage(allocator, texture->image, texture->allocation); + } + frames[p_frame].textures_to_dispose_of.pop_front(); + } + + //buffers + while (frames[p_frame].buffers_to_dispose_of.front()) { + + _buffer_free(&frames[p_frame].buffers_to_dispose_of.front()->get()); + + frames[p_frame].buffers_to_dispose_of.pop_front(); + } +} + +void RenderingDeviceVulkan::prepare_screen_for_drawing() { + _THREAD_SAFE_METHOD_ + context->prepare_buffers(); + screen_prepared = true; +} + +uint32_t RenderingDeviceVulkan::get_frame_delay() const { + return frame_count; +} + +void RenderingDeviceVulkan::_flush(bool p_current_frame) { + + //not doing this crashes RADV (undefined behavior) + if (p_current_frame) { + vkEndCommandBuffer(frames[frame].setup_command_buffer); + vkEndCommandBuffer(frames[frame].draw_command_buffer); + } + context->flush(p_current_frame, p_current_frame); + //re-create the setup command + if (p_current_frame) { + VkCommandBufferBeginInfo cmdbuf_begin; + cmdbuf_begin.sType = VK_STRUCTURE_TYPE_COMMAND_BUFFER_BEGIN_INFO; + cmdbuf_begin.pNext = NULL; + cmdbuf_begin.flags = VK_COMMAND_BUFFER_USAGE_ONE_TIME_SUBMIT_BIT; + cmdbuf_begin.pInheritanceInfo = NULL; + + VkResult err = vkBeginCommandBuffer(frames[frame].setup_command_buffer, &cmdbuf_begin); + ERR_FAIL_COND(err); + context->set_setup_buffer(frames[frame].setup_command_buffer); //append now so it's added before everything else + } + + if (p_current_frame) { + VkCommandBufferBeginInfo cmdbuf_begin; + cmdbuf_begin.sType = VK_STRUCTURE_TYPE_COMMAND_BUFFER_BEGIN_INFO; + cmdbuf_begin.pNext = NULL; + cmdbuf_begin.flags = VK_COMMAND_BUFFER_USAGE_ONE_TIME_SUBMIT_BIT; + cmdbuf_begin.pInheritanceInfo = NULL; + + VkResult err = vkBeginCommandBuffer(frames[frame].draw_command_buffer, &cmdbuf_begin); + ERR_FAIL_COND(err); + context->append_command_buffer(frames[frame].draw_command_buffer); + } +} + +void RenderingDeviceVulkan::initialize(VulkanContext *p_context) { + + context = p_context; + device = p_context->get_device(); + 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 + + VmaAllocatorCreateInfo allocatorInfo; + memset(&allocatorInfo, 0, sizeof(VmaAllocatorCreateInfo)); + allocatorInfo.physicalDevice = p_context->get_physical_device(); + allocatorInfo.device = device; + vmaCreateAllocator(&allocatorInfo, &allocator); + } + + frames = memnew_arr(Frame, frame_count); + frame = 0; + //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 + VkCommandPoolCreateInfo cmd_pool_info; + cmd_pool_info.sType = VK_STRUCTURE_TYPE_COMMAND_POOL_CREATE_INFO; + cmd_pool_info.pNext = NULL; + cmd_pool_info.queueFamilyIndex = p_context->get_graphics_queue(); + cmd_pool_info.flags = VK_COMMAND_POOL_CREATE_RESET_COMMAND_BUFFER_BIT; + + VkResult res = vkCreateCommandPool(device, &cmd_pool_info, NULL, &frames[i].command_pool); + ERR_FAIL_COND(res); + } + + { //create command buffers + + VkCommandBufferAllocateInfo cmdbuf; + //no command buffer exists, create it. + cmdbuf.sType = VK_STRUCTURE_TYPE_COMMAND_BUFFER_ALLOCATE_INFO; + cmdbuf.pNext = NULL; + cmdbuf.commandPool = frames[i].command_pool; + cmdbuf.level = VK_COMMAND_BUFFER_LEVEL_PRIMARY; + cmdbuf.commandBufferCount = 1; + + VkResult err = vkAllocateCommandBuffers(device, &cmdbuf, &frames[i].setup_command_buffer); + ERR_CONTINUE(err); + + err = vkAllocateCommandBuffers(device, &cmdbuf, &frames[i].draw_command_buffer); + ERR_CONTINUE(err); + } + + { + //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; + query_pool_create_info.pNext = NULL; + query_pool_create_info.queryType = VK_QUERY_TYPE_TIMESTAMP; + query_pool_create_info.queryCount = max_timestamp_query_elements; + query_pool_create_info.pipelineStatistics = 0; + + vkCreateQueryPool(device, &query_pool_create_info, NULL, &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_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_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. + VkCommandBufferBeginInfo cmdbuf_begin; + cmdbuf_begin.sType = VK_STRUCTURE_TYPE_COMMAND_BUFFER_BEGIN_INFO; + cmdbuf_begin.pNext = NULL; + cmdbuf_begin.flags = VK_COMMAND_BUFFER_USAGE_ONE_TIME_SUBMIT_BIT; + cmdbuf_begin.pInheritanceInfo = NULL; + + VkResult err = vkBeginCommandBuffer(frames[0].setup_command_buffer, &cmdbuf_begin); + ERR_FAIL_COND(err); + context->set_setup_buffer(frames[0].setup_command_buffer); //append now so it's added before everything else + + err = vkBeginCommandBuffer(frames[0].draw_command_buffer, &cmdbuf_begin); + ERR_FAIL_COND(err); + context->append_command_buffer(frames[0].draw_command_buffer); + } + + staging_buffer_block_size = GLOBAL_DEF("rendering/vulkan/staging_buffer/block_size_kb", 256); + staging_buffer_block_size = MAX(4, staging_buffer_block_size); + 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(1, staging_buffer_max_size); + staging_buffer_max_size *= 1024 * 1024; + + if (staging_buffer_max_size < staging_buffer_block_size * 4) { + //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 + + //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 + 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 + ERR_FAIL_COND(sizeof(uint64_t) * 3 < UNIFORM_TYPE_MAX * sizeof(uint16_t)); + + draw_list = NULL; + draw_list_count = 0; + draw_list_split = false; + + compute_list = NULL; +} + +template <class T> +void RenderingDeviceVulkan::_free_rids(T &p_owner, const char *p_type) { + List<RID> owned; + p_owner.get_owned_list(&owned); + if (owned.size()) { + WARN_PRINT(itos(owned.size()) + " RIDs of type '" + p_type + "' were leaked."); + for (List<RID>::Element *E = owned.front(); E; E = E->next()) { + free(E->get()); + } + } +} + +void RenderingDeviceVulkan::capture_timestamp(const String &p_name, bool p_sync_to_draw) { + + ERR_FAIL_COND(frames[frame].timestamp_count >= max_timestamp_query_elements); + + { + VkMemoryBarrier memoryBarrier; + + memoryBarrier.sType = VK_STRUCTURE_TYPE_MEMORY_BARRIER; + memoryBarrier.pNext = NULL; + memoryBarrier.srcAccessMask = VK_ACCESS_INDIRECT_COMMAND_READ_BIT | + VK_ACCESS_INDEX_READ_BIT | + VK_ACCESS_VERTEX_ATTRIBUTE_READ_BIT | + VK_ACCESS_UNIFORM_READ_BIT | + VK_ACCESS_INPUT_ATTACHMENT_READ_BIT | + VK_ACCESS_SHADER_READ_BIT | + VK_ACCESS_SHADER_WRITE_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 | + VK_ACCESS_TRANSFER_READ_BIT | + VK_ACCESS_TRANSFER_WRITE_BIT | + VK_ACCESS_HOST_READ_BIT | + VK_ACCESS_HOST_WRITE_BIT; + memoryBarrier.dstAccessMask = VK_ACCESS_INDIRECT_COMMAND_READ_BIT | + VK_ACCESS_INDEX_READ_BIT | + VK_ACCESS_VERTEX_ATTRIBUTE_READ_BIT | + VK_ACCESS_UNIFORM_READ_BIT | + VK_ACCESS_INPUT_ATTACHMENT_READ_BIT | + VK_ACCESS_SHADER_READ_BIT | + VK_ACCESS_SHADER_WRITE_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 | + VK_ACCESS_TRANSFER_READ_BIT | + VK_ACCESS_TRANSFER_WRITE_BIT | + VK_ACCESS_HOST_READ_BIT | + VK_ACCESS_HOST_WRITE_BIT; + + vkCmdPipelineBarrier(p_sync_to_draw ? frames[frame].draw_command_buffer : frames[frame].setup_command_buffer, VK_PIPELINE_STAGE_ALL_COMMANDS_BIT, VK_PIPELINE_STAGE_ALL_COMMANDS_BIT, 0, 1, &memoryBarrier, 0, NULL, 0, NULL); + } + vkCmdWriteTimestamp(p_sync_to_draw ? frames[frame].draw_command_buffer : frames[frame].setup_command_buffer, VK_PIPELINE_STAGE_BOTTOM_OF_PIPE_BIT, frames[frame].timestamp_pool, frames[frame].timestamp_count); + frames[frame].timestamp_names[frames[frame].timestamp_count] = p_name; + frames[frame].timestamp_cpu_values[frames[frame].timestamp_count] = OS::get_singleton()->get_ticks_usec(); + frames[frame].timestamp_count++; +} + +uint32_t RenderingDeviceVulkan::get_captured_timestamps_count() const { + return frames[frame].timestamp_result_count; +} + +uint64_t RenderingDeviceVulkan::get_captured_timestamps_frame() const { + return frames[frame].index; +} + +uint64_t RenderingDeviceVulkan::get_captured_timestamp_gpu_time(uint32_t p_index) const { + ERR_FAIL_INDEX_V(p_index, frames[frame].timestamp_result_count, 0); + return frames[frame].timestamp_result_values[p_index] * limits.timestampPeriod; +} +uint64_t RenderingDeviceVulkan::get_captured_timestamp_cpu_time(uint32_t p_index) const { + ERR_FAIL_INDEX_V(p_index, frames[frame].timestamp_result_count, 0); + return frames[frame].timestamp_cpu_result_values[p_index]; +} +String RenderingDeviceVulkan::get_captured_timestamp_name(uint32_t p_index) const { + ERR_FAIL_INDEX_V(p_index, frames[frame].timestamp_result_count, String()); + return frames[frame].timestamp_result_names[p_index]; +} + +int RenderingDeviceVulkan::limit_get(Limit p_limit) { + switch (p_limit) { + case LIMIT_MAX_BOUND_UNIFORM_SETS: return limits.maxBoundDescriptorSets; + case LIMIT_MAX_FRAMEBUFFER_COLOR_ATTACHMENTS: return limits.maxColorAttachments; + case LIMIT_MAX_TEXTURES_PER_UNIFORM_SET: return limits.maxDescriptorSetSampledImages; + case LIMIT_MAX_SAMPLERS_PER_UNIFORM_SET: return limits.maxDescriptorSetSamplers; + case LIMIT_MAX_STORAGE_BUFFERS_PER_UNIFORM_SET: return limits.maxDescriptorSetStorageBuffers; + case LIMIT_MAX_STORAGE_IMAGES_PER_UNIFORM_SET: return limits.maxDescriptorSetStorageImages; + case LIMIT_MAX_UNIFORM_BUFFERS_PER_UNIFORM_SET: return limits.maxDescriptorSetUniformBuffers; + case LIMIT_MAX_DRAW_INDEXED_INDEX: return limits.maxDrawIndexedIndexValue; + case LIMIT_MAX_FRAMEBUFFER_HEIGHT: return limits.maxFramebufferHeight; + case LIMIT_MAX_FRAMEBUFFER_WIDTH: return limits.maxFramebufferWidth; + case LIMIT_MAX_TEXTURE_ARRAY_LAYERS: return limits.maxImageArrayLayers; + case LIMIT_MAX_TEXTURE_SIZE_1D: return limits.maxImageDimension1D; + case LIMIT_MAX_TEXTURE_SIZE_2D: return limits.maxImageDimension2D; + case LIMIT_MAX_TEXTURE_SIZE_3D: return limits.maxImageDimension3D; + case LIMIT_MAX_TEXTURE_SIZE_CUBE: return limits.maxImageDimensionCube; + case LIMIT_MAX_TEXTURES_PER_SHADER_STAGE: return limits.maxPerStageDescriptorSampledImages; + case LIMIT_MAX_SAMPLERS_PER_SHADER_STAGE: return limits.maxPerStageDescriptorSamplers; + case LIMIT_MAX_STORAGE_BUFFERS_PER_SHADER_STAGE: return limits.maxPerStageDescriptorStorageBuffers; + case LIMIT_MAX_STORAGE_IMAGES_PER_SHADER_STAGE: return limits.maxPerStageDescriptorStorageImages; + case LIMIT_MAX_UNIFORM_BUFFERS_PER_SHADER_STAGE: return limits.maxPerStageDescriptorUniformBuffers; + case LIMIT_MAX_PUSH_CONSTANT_SIZE: return limits.maxPushConstantsSize; + case LIMIT_MAX_UNIFORM_BUFFER_SIZE: return limits.maxUniformBufferRange; + case LIMIT_MAX_VERTEX_INPUT_ATTRIBUTE_OFFSET: return limits.maxVertexInputAttributeOffset; + case LIMIT_MAX_VERTEX_INPUT_ATTRIBUTES: return limits.maxVertexInputAttributes; + case LIMIT_MAX_VERTEX_INPUT_BINDINGS: return limits.maxVertexInputBindings; + case LIMIT_MAX_VERTEX_INPUT_BINDING_STRIDE: return limits.maxVertexInputBindingStride; + case LIMIT_MIN_UNIFORM_BUFFER_OFFSET_ALIGNMENT: return limits.minUniformBufferOffsetAlignment; + case LIMIT_MAX_COMPUTE_WORKGROUP_COUNT_X: return limits.maxComputeWorkGroupCount[0]; + case LIMIT_MAX_COMPUTE_WORKGROUP_COUNT_Y: return limits.maxComputeWorkGroupCount[1]; + case LIMIT_MAX_COMPUTE_WORKGROUP_COUNT_Z: return limits.maxComputeWorkGroupCount[2]; + case LIMIT_MAX_COMPUTE_WORKGROUP_INVOCATIONS: return limits.maxComputeWorkGroupInvocations; + case LIMIT_MAX_COMPUTE_WORKGROUP_SIZE_X: return limits.maxComputeWorkGroupSize[0]; + case LIMIT_MAX_COMPUTE_WORKGROUP_SIZE_Y: return limits.maxComputeWorkGroupSize[1]; + case LIMIT_MAX_COMPUTE_WORKGROUP_SIZE_Z: return limits.maxComputeWorkGroupSize[2]; + + default: ERR_FAIL_V(0); + } + + return 0; +} + +void RenderingDeviceVulkan::finalize() { + + //free all resources + + _flush(false); + + _free_rids(render_pipeline_owner, "Pipeline"); + _free_rids(compute_pipeline_owner, "Compute"); + _free_rids(uniform_set_owner, "UniformSet"); + _free_rids(texture_buffer_owner, "TextureBuffer"); + _free_rids(storage_buffer_owner, "StorageBuffer"); + _free_rids(uniform_buffer_owner, "UniformBuffer"); + _free_rids(shader_owner, "Shader"); + _free_rids(index_array_owner, "IndexArray"); + _free_rids(index_buffer_owner, "IndexBuffer"); + _free_rids(vertex_array_owner, "VertexArray"); + _free_rids(vertex_buffer_owner, "VertexBuffer"); + _free_rids(framebuffer_owner, "Framebuffer"); + _free_rids(sampler_owner, "Sampler"); + { + //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()) { + WARN_PRINT(itos(owned.size()) + " RIDs of type 'Texture' were leaked."); + //free shared first + for (List<RID>::Element *E = owned.front(); E;) { + + List<RID>::Element *N = E->next(); + if (texture_is_shared(E->get())) { + free(E->get()); + owned.erase(E->get()); + } + E = N; + } + //free non shared second, this will avoid an error trying to free unexisting textures due to dependencies. + for (List<RID>::Element *E = owned.front(); E; E = E->next()) { + free(E->get()); + } + } + } + + //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, NULL); + vkDestroyQueryPool(device, frames[i].timestamp_pool, NULL); + 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, NULL); + } + + memdelete_arr(frames); + + for (int i = 0; i < staging_buffer_blocks.size(); i++) { + vmaDestroyBuffer(allocator, staging_buffer_blocks[i].buffer, staging_buffer_blocks[i].allocation); + } + + //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()); +} + +RenderingDeviceVulkan::RenderingDeviceVulkan() { + screen_prepared = false; +} |