diff options
Diffstat (limited to 'drivers/vulkan/rendering_device_vulkan.cpp')
-rw-r--r-- | drivers/vulkan/rendering_device_vulkan.cpp | 5164 |
1 files changed, 5164 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..bcd103b67b --- /dev/null +++ b/drivers/vulkan/rendering_device_vulkan.cpp @@ -0,0 +1,5164 @@ +#include "rendering_device_vulkan.h" +#include "drivers/vulkan/vulkan_context.h" + +#include "core/hashfuncs.h" +#include "core/project_settings.h" +#include "thirdparty/glslang/SPIRV/GlslangToSpv.h" +#include "thirdparty/glslang/glslang/Include/Types.h" + +void RenderingDeviceVulkan::_add_dependency(ID p_id, ID p_depends_on) { + + if (!dependency_map.has(p_depends_on)) { + dependency_map[p_depends_on] = Set<ID>(); + } + + dependency_map[p_depends_on].insert(p_id); + + if (!reverse_dependency_map.has(p_id)) { + reverse_dependency_map[p_id] = Set<ID>(); + } + + reverse_dependency_map[p_id].insert(p_depends_on); +} + +void RenderingDeviceVulkan::_free_dependencies(ID p_id) { + + //direct dependencies must be freed + List<ID> to_free; + Map<ID, Set<ID> >::Element *E = dependency_map.find(p_id); + if (E) { + + for (Set<ID>::Element *F = E->get().front(); F; F = F->next()) { + to_free.push_back(F->get()); + } + + dependency_map.erase(E); + + while (to_free.front()) { + free(to_free.front()->get()); + to_free.pop_front(); + } + } + + //reverse depenencies must be unreferenced + E = reverse_dependency_map.find(p_id); + + if (E) { + + for (Set<ID>::Element *F = E->get().front(); F; F = F->next()) { + Map<ID, Set<ID> >::Element *G = dependency_map.find(F->get()); + if (G) { + 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; +} + +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_mipmap, uint32_t *r_blockw, uint32_t *r_blockh) { + + 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_mipmap; i++) { + uint32_t bw = w % blockw != 0 ? w + (blockw - w % blockw) : w; + uint32_t bh = h % blockh != 0 ? h + (blockh - h % blockh) : h; + + print_line("bw " + itos(bw) + " bh " + itos(bh) + " pixsize " + itos(pixel_size) + " shift " + itos(pixel_rshift)); + 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; + } + 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 +}; +/***************************/ +/**** 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(err, ERR_CANT_CREATE); + 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); + vmaFreeMemory(allocator, 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 + context->flush(true, p_on_draw_command_buffer); + //re-create the setup command + { + 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_V(err, ERR_CANT_CREATE); + context->set_setup_buffer(frames[frame].setup_command_buffer); //append now so it's added before everything else + + if (p_on_draw_command_buffer) { + + err = vkBeginCommandBuffer(frames[frame].draw_command_buffer, &cmdbuf_begin); + ERR_FAIL_COND_V(err, ERR_CANT_CREATE); + context->append_command_buffer(frames[frame].draw_command_buffer); + } + } + + //clear the whole staging buffer + for (int i = 0; i < staging_buffer_blocks.size(); i++) { + staging_buffer_blocks.write[i].frame_used = 0; + staging_buffer_blocks.write[i].fill_amount = 0; + } + //claim current + 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 { + + context->flush(false); // flush previous frames (but don't touch setup command, so this frame) + + 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; +} + +/*****************/ +/**** TEXTURE ****/ +/*****************/ + +RenderingDevice::ID 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; + + 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; + }*/ + + const VkImageType image_type[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_3D + }; + + ERR_FAIL_INDEX_V(p_format.type, TEXTURE_TYPE_MAX, INVALID_ID); + + image_create_info.imageType = image_type[p_format.type]; + + ERR_FAIL_COND_V_MSG(p_format.width < 1, INVALID_ID, "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, INVALID_ID, "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, INVALID_ID, "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, INVALID_ID); + + image_create_info.mipLevels = p_format.mipmaps; + + uint32_t array_layer_multiplier = 1; + if (p_format.type == TEXTURE_TYPE_CUBE_ARRAY || p_format.type == TEXTURE_TYPE_CUBE) { + array_layer_multiplier = 6; + } + 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, INVALID_ID, + "Amount of layers must be equal or greater than 1 for arrays and cubemaps."); + if ((p_format.type == TEXTURE_TYPE_CUBE_ARRAY || p_format.type == TEXTURE_TYPE_CUBE) && (p_format.array_layers % 6) != 0) { + ERR_FAIL_V_MSG(INVALID_ID, "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; + } + + image_create_info.arrayLayers = p_format.array_layers; + + ERR_FAIL_INDEX_V(p_format.samples, TEXTURE_SAMPLES_MAX, INVALID_ID); + + 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_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; + } + + 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, INVALID_ID, + "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), INVALID_ID, + "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.mipLevels * image_create_info.arrayLayers * array_layer_multiplier; + ERR_FAIL_COND_V_MSG(p_data.size() != expected_images, INVALID_ID, + "Default supplied data for image format is of invalid length (" + itos(p_data.size()) + "), should be (" + itos(expected_images) + ")."); + + int idx = 0; + for (uint32_t i = 0; i < image_create_info.arrayLayers * array_layer_multiplier; i++) { + for (uint32_t j = 0; j < image_create_info.mipLevels; j++) { + print_line("computed size from " + Vector3(image_create_info.extent.width, image_create_info.extent.height, image_create_info.extent.depth)); + 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, j); + ERR_FAIL_COND_V_MSG((uint32_t)p_data[idx].size() != required_size, INVALID_ID, + "Data for slice index " + itos(idx) + " (mapped to layer " + itos(i) + ", mipmap " + itos(j) + ") differs in size (supplied: " + itos(p_data[idx].size()) + ") than what is required by the format (" + itos(required_size) + ")."); + idx++; + } + } + } + + { + //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(INVALID_ID, "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(INVALID_ID, "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)) { + ERR_FAIL_V_MSG(INVALID_ID, "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(INVALID_ID, "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(INVALID_ID, "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, INVALID_ID); + } + ERR_FAIL_INDEX_V(p_view.swizzle_r, TEXTURE_SWIZZLE_MAX, INVALID_ID); + ERR_FAIL_INDEX_V(p_view.swizzle_g, TEXTURE_SWIZZLE_MAX, INVALID_ID); + ERR_FAIL_INDEX_V(p_view.swizzle_b, TEXTURE_SWIZZLE_MAX, INVALID_ID); + ERR_FAIL_INDEX_V(p_view.swizzle_a, TEXTURE_SWIZZLE_MAX, INVALID_ID); + + //allocate memory + + VmaAllocationCreateInfo allocInfo; + allocInfo.flags = 0; + allocInfo.usage = 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, ERR_CANT_CREATE); + + 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; + + if (p_format.usage_bits & TEXTURE_USAGE_DEPTH_STENCIL_ATTACHMENT_BIT) { + texture.aspect_mask = TEXTURE_USAGE_DEPTH_STENCIL_ATTACHMENT_BIT; + texture.reading_layout = VK_IMAGE_LAYOUT_DEPTH_READ_ONLY_STENCIL_ATTACHMENT_OPTIMAL; + texture.bound_layout = VK_IMAGE_LAYOUT_DEPTH_STENCIL_ATTACHMENT_OPTIMAL; + } else { + texture.aspect_mask = VK_IMAGE_ASPECT_COLOR_BIT; + texture.reading_layout = VK_IMAGE_LAYOUT_SHADER_READ_ONLY_OPTIMAL; + texture.bound_layout = VK_IMAGE_LAYOUT_COLOR_ATTACHMENT_OPTIMAL; + } + + texture.bound = false; + texture.owner = INVALID_ID; + + //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 = array_layer_multiplier * 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); + vmaFreeMemory(allocator, texture.allocation); + ERR_FAIL_V(INVALID_ID); + } + + //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.reading_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.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 * array_layer_multiplier; + + vkCmdPipelineBarrier(frames[frame].setup_command_buffer, VK_PIPELINE_STAGE_TOP_OF_PIPE_BIT, VK_PIPELINE_STAGE_FRAGMENT_SHADER_BIT, 0, 0, NULL, 0, NULL, 1, &image_memory_barrier); + } + + ID id = texture_owner.make_id(texture); + + if (p_data.size()) { + + for (uint32_t i = 0; i < image_create_info.arrayLayers; i++) { + for (uint32_t j = 0; j < image_create_info.mipLevels; j++) { + texture_update(id, j, i, p_data[i * image_create_info.mipLevels + j], true); + } + } + } + return id; +} + +RenderingDevice::ID RenderingDeviceVulkan::texture_create_shared(const TextureView &p_view, ID p_with_texture) { + + Texture *src_texture = texture_owner.getornull(p_with_texture); + ERR_FAIL_COND_V(!src_texture, INVALID_ID); + + if (src_texture->owner != INVALID_ID) { //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, INVALID_ID); //this is a bug + } + + //create view + + Texture texture = *src_texture; + + uint32_t array_layer_multiplier = 1; + if (texture.type == TEXTURE_TYPE_CUBE_ARRAY || texture.type == TEXTURE_TYPE_CUBE) { + array_layer_multiplier = 6; + } + + 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) { + image_view_create_info.format = vulkan_formats[texture.format]; + } else { + ERR_FAIL_INDEX_V(p_view.format_override, DATA_FORMAT_MAX, INVALID_ID); + 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 = array_layer_multiplier * 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(INVALID_ID); + } + + texture.owner = p_with_texture; + ID id = texture_owner.make_id(texture); + _add_dependency(id, p_with_texture); + + return id; +} + +Error RenderingDeviceVulkan::texture_update(ID p_texture, uint32_t p_mipmap, uint32_t p_layer, const PoolVector<uint8_t> &p_data, bool p_sync_with_draw) { + + _THREAD_SAFE_METHOD_ + + Texture *texture = texture_owner.getornull(p_texture); + ERR_FAIL_COND_V(!texture, ERR_INVALID_PARAMETER); + + if (texture->owner != INVALID_ID) { + 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."); + + ERR_FAIL_COND_V(p_mipmap >= texture->mipmaps, ERR_INVALID_PARAMETER); + 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, 1, p_mipmap, &width, &height); + uint32_t required_size = image_size * texture->depth; + + 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->reading_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->aspect_mask; + image_memory_barrier.subresourceRange.baseMipLevel = p_mipmap; + image_memory_barrier.subresourceRange.levelCount = 1; + 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); + } + + for (uint32_t z = 0; z < texture->depth; z++) { //for 3D textures, depth may be > 0 + + const uint8_t *read_ptr = r.ptr(); + read_ptr += image_size * z; + + 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, 32, 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->aspect_mask; + buffer_image_copy.imageSubresource.baseArrayLayer = p_layer; + buffer_image_copy.imageSubresource.mipLevel = p_mipmap; + 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; + } + } + } + + //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->reading_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->aspect_mask; + image_memory_barrier.subresourceRange.baseMipLevel = p_mipmap; + image_memory_barrier.subresourceRange.levelCount = 1; + image_memory_barrier.subresourceRange.baseArrayLayer = p_layer; + image_memory_barrier.subresourceRange.layerCount = 1; + + vkCmdPipelineBarrier(command_buffer, VK_ACCESS_TRANSFER_WRITE_BIT, VK_PIPELINE_STAGE_FRAGMENT_SHADER_BIT, 0, 0, NULL, 0, NULL, 1, &image_memory_barrier); + } + + return OK; +} + +bool RenderingDeviceVulkan::texture_is_format_supported_for_usage(DataFormat p_format, TextureUsageBits 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_action, FinalAction p_final_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 + + switch (p_initial_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_COLOR: { + if (p_format[i].usage_flags & TEXTURE_USAGE_COLOR_ATTACHMENT_BIT) { + description.loadOp = VK_ATTACHMENT_LOAD_OP_LOAD; + description.initialLayout = VK_IMAGE_LAYOUT_SHADER_READ_ONLY_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_KEEP_COLOR_AND_DEPTH: { + + if (p_format[i].usage_flags & TEXTURE_USAGE_COLOR_ATTACHMENT_BIT) { + description.loadOp = VK_ATTACHMENT_LOAD_OP_LOAD; + description.initialLayout = VK_IMAGE_LAYOUT_SHADER_READ_ONLY_OPTIMAL; + } 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_READ_ONLY_STENCIL_ATTACHMENT_OPTIMAL; //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 (p_final_action) { + case FINAL_ACTION_READ_COLOR_AND_DEPTH: { + 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_SHADER_READ_ONLY_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_READ_ONLY_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_READ_COLOR_DISCARD_DEPTH: { + 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_SHADER_READ_ONLY_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 = VK_IMAGE_LAYOUT_DEPTH_READ_ONLY_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 = VK_IMAGE_LAYOUT_SHADER_READ_ONLY_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 = VK_IMAGE_LAYOUT_DEPTH_READ_ONLY_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(depth_stencil_references.size() > 1, VK_NULL_HANDLE); + ERR_FAIL_COND_V(resolve_references.size() > 1, VK_NULL_HANDLE); + + 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::ID RenderingDeviceVulkan::framebuffer_format_create(const Vector<AttachmentFormat> &p_format) { + + _THREAD_SAFE_METHOD_ + + FramebufferFormatKey key; + key.attachments = p_format; + + const Map<FramebufferFormatKey, ID>::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, &color_references); //actions don't matter for this use case + + ID id = ID(framebuffer_format_cache.size()) | (ID(ID_TYPE_FRAMEBUFFER_FORMAT) << ID(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; + framebuffer_formats[id] = fb_format; + return id; +} + +/***********************/ +/**** RENDER TARGET ****/ +/***********************/ + +RenderingDevice::ID RenderingDeviceVulkan::framebuffer_create(const Vector<ID> &p_texture_attachments, ID 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, INVALID_ID, "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, INVALID_ID, + "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); + } + + ID format_id = framebuffer_format_create(attachments); + if (format_id == INVALID_ID) { + return INVALID_ID; + } + + ERR_FAIL_COND_V_MSG(p_format_check != INVALID_ID && format_id != p_format_check, INVALID_ID, + "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; + + ID id = framebuffer_owner.make_id(framebuffer); + + for (int i = 0; i < p_texture_attachments.size(); i++) { + _add_dependency(id, p_texture_attachments[i]); + } + + return id; +} + +RenderingDevice::ID RenderingDeviceVulkan::framebuffer_get_format(ID p_framebuffer) { + + _THREAD_SAFE_METHOD_ + + Framebuffer *framebuffer = framebuffer_owner.getornull(p_framebuffer); + ERR_FAIL_COND_V(!framebuffer, INVALID_ID); + + return framebuffer->format_id; +} + +/*****************/ +/**** SAMPLER ****/ +/*****************/ + +RenderingDevice::ID 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, INVALID_ID); + sampler_create_info.addressModeU = address_modes[p_state.repeat_u]; + ERR_FAIL_INDEX_V(p_state.repeat_v, SAMPLER_REPEAT_MODE_MAX, INVALID_ID); + sampler_create_info.addressModeV = address_modes[p_state.repeat_v]; + ERR_FAIL_INDEX_V(p_state.repeat_w, SAMPLER_REPEAT_MODE_MAX, INVALID_ID); + 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, INVALID_ID); + 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, INVALID_ID); + 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, INVALID_ID); + + return sampler_owner.make_id(sampler); +} + +/**********************/ +/**** VERTEX ARRAY ****/ +/**********************/ + +RenderingDevice::ID 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, INVALID_ID); + + Buffer buffer; + _buffer_allocate(&buffer, p_size_bytes, 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); + } + return vertex_buffer_owner.make_id(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::ID RenderingDeviceVulkan::vertex_description_create(const Vector<VertexDescription> &p_vertex_descriptions) { + + _THREAD_SAFE_METHOD_ + + VertexDescriptionKey key; + key.vertex_descriptions = p_vertex_descriptions; + const Map<VertexDescriptionKey, ID>::Element *E = vertex_description_cache.find(key); + if (E) { + return E->get(); + } + //does not exist, create one and cache it + VertexDescriptionCache vdcache; + vdcache.bindings = memnew_arr(VkVertexInputBindingDescription, p_vertex_descriptions.size()); + vdcache.attributes = memnew_arr(VkVertexInputAttributeDescription, p_vertex_descriptions.size()); + Set<int> used_locations; + for (int i = 0; i < p_vertex_descriptions.size(); i++) { + ERR_CONTINUE(p_vertex_descriptions[i].format >= DATA_FORMAT_MAX); + ERR_FAIL_COND_V(used_locations.has(p_vertex_descriptions[i].location), INVALID_ID); + + ERR_FAIL_COND_V_MSG(get_format_vertex_size(p_vertex_descriptions[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_descriptions[i].stride; + vdcache.bindings[i].inputRate = p_vertex_descriptions[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_descriptions[i].location; + vdcache.attributes[i].format = vulkan_formats[p_vertex_descriptions[i].format]; + vdcache.attributes[i].offset = p_vertex_descriptions[i].offset; + used_locations.insert(p_vertex_descriptions[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_descriptions.size(); + vdcache.create_info.pVertexAttributeDescriptions = vdcache.attributes; + + vdcache.create_info.vertexBindingDescriptionCount = p_vertex_descriptions.size(); + vdcache.create_info.pVertexBindingDescriptions = vdcache.bindings; + + ID id = ID(vertex_description_cache.size()) | (ID(ID_TYPE_VERTEX_DESCRIPTION) << ID_BASE_SHIFT); + vdcache.E = vertex_description_cache.insert(key, id); + vertex_descriptions[id] = vdcache; + return id; +} + +RenderingDevice::ID RenderingDeviceVulkan::vertex_array_create(uint32_t p_vertex_count, ID p_vertex_description, const Vector<ID> &p_src_buffers) { + + _THREAD_SAFE_METHOD_ + + ERR_FAIL_COND_V(!vertex_descriptions.has(p_vertex_description), INVALID_ID); + const VertexDescriptionCache &vd = vertex_descriptions[p_vertex_description]; + + ERR_FAIL_COND_V(vd.E->key().vertex_descriptions.size() != p_src_buffers.size(), INVALID_ID); + + for (int i = 0; i < p_src_buffers.size(); i++) { + ERR_FAIL_COND_V(!vertex_buffer_owner.owns(p_src_buffers[i]), INVALID_ID); + } + + VertexArray vertex_array; + + vertex_array.vertex_count = p_vertex_count; + vertex_array.description = p_vertex_description; + 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.E->key().vertex_descriptions[i]; + uint32_t element_size = get_format_vertex_size(atf.format); + ERR_FAIL_COND_V(element_size == 0, INVALID_ID); //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, INVALID_ID, + "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, INVALID_ID, + "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 + } + + ID id = vertex_array_owner.make_id(vertex_array); + for (int i = 0; i < p_src_buffers.size(); i++) { + _add_dependency(id, p_src_buffers[i]); + } + + return id; +} + +RenderingDevice::ID 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, INVALID_ID); + + 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, INVALID_ID, + "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_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); + } + return index_buffer_owner.make_id(index_buffer); +} + +RenderingDevice::ID RenderingDeviceVulkan::index_array_create(ID 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), INVALID_ID); + + IndexBuffer *index_buffer = index_buffer_owner.getornull(p_index_buffer); + + ERR_FAIL_COND_V(p_index_count == 0, INVALID_ID); + ERR_FAIL_COND_V(p_index_offset + p_index_count > index_buffer->index_count, INVALID_ID); + + 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; + + ID id = index_array_owner.make_id(index_array); + _add_dependency(id, p_index_buffer); + return id; +} + +/****************/ +/**** SHADER ****/ +/****************/ + +static const TBuiltInResource default_builtin_resource = { + .maxLights = 32, + .maxClipPlanes = 6, + .maxTextureUnits = 32, + .maxTextureCoords = 32, + .maxVertexAttribs = 64, + .maxVertexUniformComponents = 4096, + .maxVaryingFloats = 64, + .maxVertexTextureImageUnits = 32, + .maxCombinedTextureImageUnits = 80, + .maxTextureImageUnits = 32, + .maxFragmentUniformComponents = 4096, + .maxDrawBuffers = 32, + .maxVertexUniformVectors = 128, + .maxVaryingVectors = 8, + .maxFragmentUniformVectors = 16, + .maxVertexOutputVectors = 16, + .maxFragmentInputVectors = 15, + .minProgramTexelOffset = -8, + .maxProgramTexelOffset = 7, + .maxClipDistances = 8, + .maxComputeWorkGroupCountX = 65535, + .maxComputeWorkGroupCountY = 65535, + .maxComputeWorkGroupCountZ = 65535, + .maxComputeWorkGroupSizeX = 1024, + .maxComputeWorkGroupSizeY = 1024, + .maxComputeWorkGroupSizeZ = 64, + .maxComputeUniformComponents = 1024, + .maxComputeTextureImageUnits = 16, + .maxComputeImageUniforms = 8, + .maxComputeAtomicCounters = 8, + .maxComputeAtomicCounterBuffers = 1, + .maxVaryingComponents = 60, + .maxVertexOutputComponents = 64, + .maxGeometryInputComponents = 64, + .maxGeometryOutputComponents = 128, + .maxFragmentInputComponents = 128, + .maxImageUnits = 8, + .maxCombinedImageUnitsAndFragmentOutputs = 8, + .maxCombinedShaderOutputResources = 8, + .maxImageSamples = 0, + .maxVertexImageUniforms = 0, + .maxTessControlImageUniforms = 0, + .maxTessEvaluationImageUniforms = 0, + .maxGeometryImageUniforms = 0, + .maxFragmentImageUniforms = 8, + .maxCombinedImageUniforms = 8, + .maxGeometryTextureImageUnits = 16, + .maxGeometryOutputVertices = 256, + .maxGeometryTotalOutputComponents = 1024, + .maxGeometryUniformComponents = 1024, + .maxGeometryVaryingComponents = 64, + .maxTessControlInputComponents = 128, + .maxTessControlOutputComponents = 128, + .maxTessControlTextureImageUnits = 16, + .maxTessControlUniformComponents = 1024, + .maxTessControlTotalOutputComponents = 4096, + .maxTessEvaluationInputComponents = 128, + .maxTessEvaluationOutputComponents = 128, + .maxTessEvaluationTextureImageUnits = 16, + .maxTessEvaluationUniformComponents = 1024, + .maxTessPatchComponents = 120, + .maxPatchVertices = 32, + .maxTessGenLevel = 64, + .maxViewports = 16, + .maxVertexAtomicCounters = 0, + .maxTessControlAtomicCounters = 0, + .maxTessEvaluationAtomicCounters = 0, + .maxGeometryAtomicCounters = 0, + .maxFragmentAtomicCounters = 8, + .maxCombinedAtomicCounters = 8, + .maxAtomicCounterBindings = 1, + .maxVertexAtomicCounterBuffers = 0, + .maxTessControlAtomicCounterBuffers = 0, + .maxTessEvaluationAtomicCounterBuffers = 0, + .maxGeometryAtomicCounterBuffers = 0, + .maxFragmentAtomicCounterBuffers = 1, + .maxCombinedAtomicCounterBuffers = 1, + .maxAtomicCounterBufferSize = 16384, + .maxTransformFeedbackBuffers = 4, + .maxTransformFeedbackInterleavedComponents = 64, + .maxCullDistances = 8, + .maxCombinedClipAndCullDistances = 8, + .maxSamples = 4, + .limits = { + .nonInductiveForLoops = 1, + .whileLoops = 1, + .doWhileLoops = 1, + .generalUniformIndexing = 1, + .generalAttributeMatrixVectorIndexing = 1, + .generalVaryingIndexing = 1, + .generalSamplerIndexing = 1, + .generalVariableIndexing = 1, + .generalConstantMatrixVectorIndexing = 1, + } +}; + +static const char *shader_stage_names[RenderingDevice::SHADER_STAGE_MAX] = { + "Vertex", + "Fragment", + "TesselationControl", + "TesselationEvaluation", + "Compute" +}; + +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, +}; + +bool RenderingDeviceVulkan::_uniform_add_binding(Vector<Vector<VkDescriptorSetLayoutBinding> > &bindings, Vector<Vector<Shader::UniformInfo> > &uniform_infos, const glslang::TObjectReflection &reflection, RenderingDevice::ShaderStage p_stage, String *r_error) { + + VkDescriptorSetLayoutBinding layout_binding; + Shader::UniformInfo info; + + print_line("*** Stage " + itos(p_stage) + " uniform: " + reflection.name.c_str()); + + 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: 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: 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: 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: combined"); + } else if (reflection.getType()->getSampler().isPureSampler()) { + layout_binding.descriptorType = VK_DESCRIPTOR_TYPE_SAMPLER; + info.type = UNIFORM_TYPE_SAMPLER; + print_line("DEBUG: sampler"); + } else if (reflection.getType()->getSampler().isTexture()) { + layout_binding.descriptorType = VK_DESCRIPTOR_TYPE_SAMPLED_IMAGE; + info.type = UNIFORM_TYPE_TEXTURE; + print_line("DEBUG: image"); + } else if (reflection.getType()->getSampler().isImage()) { + layout_binding.descriptorType = VK_DESCRIPTOR_TYPE_STORAGE_IMAGE; + info.type = UNIFORM_TYPE_IMAGE; + print_line("DEBUG: 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) { + 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."; + } + 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()) { + //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 >= 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); + } + + bindings.write[set].push_back(layout_binding); + uniform_infos.write[set].push_back(info); + + return true; +} + +RenderingDevice::ID RenderingDeviceVulkan::shader_create_from_source(const Vector<ShaderStageSource> &p_stages, String *r_error, bool p_allow_cache) { + + _THREAD_SAFE_METHOD_ + + // initialize in case it's not initialized. This is done once per thread + // and it's safe to call multiple times + glslang::InitializeProcess(); + EShLanguage stages[SHADER_STAGE_MAX] = { + EShLangVertex, + EShLangFragment, + EShLangTessControl, + EShLangTessEvaluation, + EShLangCompute + }; + + int ClientInputSemanticsVersion = 100; // maps to, say, #define VULKAN 100 + glslang::EShTargetClientVersion VulkanClientVersion = glslang::EShTargetVulkan_1_0; + glslang::EShTargetLanguageVersion TargetVersion = glslang::EShTargetSpv_1_0; + + Vector<std::vector<unsigned int> > spirv_code; + + glslang::TShader::ForbidIncluder includer; + + //descriptor layouts + Vector<Vector<VkDescriptorSetLayoutBinding> > bindings; + Vector<Vector<Shader::UniformInfo> > uniform_info; + Vector<int> vertex_input_locations; + int fragment_outputs = 0; + + uint32_t stages_processed = 0; + + for (int i = 0; i < p_stages.size(); i++) { + + if (stages_processed & (1 << p_stages[i].shader_stage)) { + if (r_error) { + (*r_error) = "Stage " + String(shader_stage_names[p_stages[i].shader_stage]) + " submitted more than once."; + } + return false; + } + glslang::TShader shader(stages[p_stages[i].shader_stage]); + CharString cs = p_stages[i].shader_source.utf8(); + const char *cs_strings = cs.get_data(); + shader.setStrings(&cs_strings, 1); + shader.setEnvInput(glslang::EShSourceGlsl, stages[p_stages[i].shader_stage], glslang::EShClientVulkan, ClientInputSemanticsVersion); + shader.setEnvClient(glslang::EShClientVulkan, VulkanClientVersion); + shader.setEnvTarget(glslang::EShTargetSpv, TargetVersion); + + EShMessages messages = (EShMessages)(EShMsgSpvRules | EShMsgVulkanRules); + const int DefaultVersion = 100; + std::string pre_processed_code; + + //preprocess + if (!shader.preprocess(&default_builtin_resource, DefaultVersion, ENoProfile, false, false, messages, &pre_processed_code, includer)) { + + if (r_error) { + (*r_error) = "Failed pre-processing on shader stage: " + String(shader_stage_names[p_stages[i].shader_stage]) + "\n"; + (*r_error) += shader.getInfoLog(); + (*r_error) += "\n"; + (*r_error) += shader.getInfoDebugLog(); + } + + return INVALID_ID; + } + //set back.. + cs_strings = pre_processed_code.c_str(); + shader.setStrings(&cs_strings, 1); + + //parse + if (!shader.parse(&default_builtin_resource, DefaultVersion, false, messages)) { + if (r_error) { + (*r_error) = "Failed parsing on shader stage: " + String(shader_stage_names[p_stages[i].shader_stage]) + "\n"; + (*r_error) += shader.getInfoLog(); + (*r_error) += "\n"; + (*r_error) += shader.getInfoDebugLog(); + } + return INVALID_ID; + } + + //link + glslang::TProgram program; + program.addShader(&shader); + + if (!program.link(messages)) { + if (r_error) { + (*r_error) = "Failed linking on shader stage: " + String(shader_stage_names[p_stages[i].shader_stage]) + "\n"; + (*r_error) += program.getInfoLog(); + (*r_error) += "\n"; + (*r_error) += program.getInfoDebugLog(); + } + return INVALID_ID; + } + + //obtain bindings for descriptor layout + program.mapIO(); + program.buildReflection(); + program.dumpReflection(); + + for (int j = 0; j < program.getNumUniformVariables(); j++) { + if (!_uniform_add_binding(bindings, uniform_info, program.getUniform(j), p_stages[i].shader_stage, r_error)) { + return INVALID_ID; + } + } + + for (int j = 0; j < program.getNumUniformBlocks(); j++) { + if (!_uniform_add_binding(bindings, uniform_info, program.getUniformBlock(j), p_stages[i].shader_stage, r_error)) { + return INVALID_ID; + } + } + + for (int j = 0; j < program.getNumBufferVariables(); j++) { + if (!_uniform_add_binding(bindings, uniform_info, program.getBufferVariable(j), p_stages[i].shader_stage, r_error)) { + return INVALID_ID; + } + } + + for (int j = 0; j < program.getNumBufferBlocks(); j++) { + if (!_uniform_add_binding(bindings, uniform_info, program.getBufferBlock(j), p_stages[i].shader_stage, r_error)) { + return INVALID_ID; + } + } + + if (p_stages[i].shader_stage == SHADER_STAGE_VERTEX) { + for (int j = 0; j < program.getNumPipeInputs(); j++) { + if (program.getPipeInput(i).getType()->getQualifier().hasLocation()) { + int location = program.getPipeInput(i).getType()->getQualifier().layoutLocation; + print_line("found vertex location: " + itos(location)); + if (vertex_input_locations.find(location) == -1) { + vertex_input_locations.push_back(location); + } + } + } + } + + if (p_stages[i].shader_stage == SHADER_STAGE_FRAGMENT) { + + fragment_outputs = program.getNumPipeOutputs(); + } + + std::vector<uint32_t> SpirV; + spv::SpvBuildLogger logger; + glslang::SpvOptions spvOptions; + glslang::GlslangToSpv(*program.getIntermediate(stages[p_stages[i].shader_stage]), SpirV, &logger, &spvOptions); + + spirv_code.push_back(SpirV); + + stages_processed |= (1 << p_stages[i].shader_stage); + } + + //all good, let's create modules + + Shader shader; + + shader.vertex_input_locations = vertex_input_locations; + shader.fragment_outputs = fragment_outputs; + + 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 = spirv_code[i].size() * sizeof(uint32_t); + shader_module_create_info.pCode = &spirv_code[i][0]; + + VkShaderModule module; + VkResult res = vkCreateShaderModule(device, &shader_module_create_info, NULL, &module); + if (res) { + success = false; + ERR_PRINT("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 < 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 = bindings[i].size(); + layout_create_info.pBindings = bindings[i].ptr(); + + VkDescriptorSetLayout layout; + VkResult res = vkCreateDescriptorSetLayout(device, &layout_create_info, NULL, &layout); + if (res) { + ERR_PRINT("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 h = set.uniform_info.size() ? hash_djb2_one_32(0) : 0; + for (int j = 0; j < set.uniform_info.size(); j++) { + const Shader::UniformInfo &ui = set.uniform_info[j]; + h = hash_djb2_one_32(ui.type, h); + h = hash_djb2_one_32(ui.binding, h); + h = hash_djb2_one_32(ui.length, h); + h = hash_djb2_one_32(ui.stages, h); + } + + shader.sets.push_back(set); + shader.set_hashes.push_back(h); + } + } + + 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; + } + + //unsupported for now + pipeline_layout_create_info.pSetLayouts = layouts.ptr(); + 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) { + ERR_PRINT("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); + } + + return INVALID_ID; + } + + return shader_owner.make_id(shader); +} + +/******************/ +/**** UNIFORMS ****/ +/******************/ + +RenderingDevice::ID 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, INVALID_ID); + + 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, INVALID_ID); + 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); + } + return uniform_buffer_owner.make_id(buffer); +} + +RenderingDevice::ID 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, INVALID_ID); + + Buffer buffer; + Error err = _buffer_allocate(&buffer, p_size_bytes, VK_BUFFER_USAGE_TRANSFER_DST_BIT | VK_BUFFER_USAGE_STORAGE_BUFFER_BIT, VMA_MEMORY_USAGE_GPU_ONLY); + ERR_FAIL_COND_V(err != OK, INVALID_ID); + + 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); + } + return storage_buffer_owner.make_id(buffer); +} + +RenderingDevice::ID 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, INVALID_ID, "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, INVALID_ID); + + TextureBuffer texture_buffer; + Error err = _buffer_allocate(&texture_buffer.buffer, size_bytes, VK_BUFFER_USAGE_TRANSFER_DST_BIT | VK_BUFFER_USAGE_UNIFORM_TEXEL_BUFFER_BIT, VMA_MEMORY_USAGE_GPU_ONLY); + ERR_FAIL_COND_V(err != OK, INVALID_ID); + + 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); + } + + 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(INVALID_ID, "Unable to create buffer view"); + } + + //allocate the view + return texture_buffer_owner.make_id(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); + } +} + +RenderingDevice::ID RenderingDeviceVulkan::uniform_set_create(const Vector<Uniform> &p_uniforms, ID p_shader, uint32_t p_shader_set) { + + _THREAD_SAFE_METHOD_ + + ERR_FAIL_COND_V(p_uniforms.size() == 0, INVALID_ID); + + Shader *shader = shader_owner.getornull(p_shader); + ERR_FAIL_COND_V(!shader, INVALID_ID); + + ERR_FAIL_COND_V_MSG(p_shader_set >= (uint32_t)shader->sets.size() || shader->sets[p_shader_set].uniform_info.size() == 0, INVALID_ID, + "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(); + print_line("uniform count: " + itos(uniform_count)); + + uint32_t set_uniform_count = set.uniform_info.size(); + const Shader::UniformInfo *set_uniforms = set.uniform_info.ptr(); + + print_line("set_uniform count: " + itos(set_uniform_count)); + + 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; +#ifdef DEBUG_ENABLED + //used for verification to make sure a uniform set does not use a framebuffer bound texture + Vector<ID> bound_textures; +#endif + + for (uint32_t i = 0; i < set_uniform_count; i++) { + const Shader::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, INVALID_ID, + "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, INVALID_ID, + "Mismatch uniform type for binding (" + itos(set_uniform.binding) + ")."); + + 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(INVALID_ID, "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(INVALID_ID, "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, INVALID_ID, "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(INVALID_ID, "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(INVALID_ID, "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, INVALID_ID, "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, INVALID_ID, "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), INVALID_ID, + "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; + +#ifdef DEBUG_ENABLED + bound_textures.push_back(texture->owner != INVALID_ID ? texture->owner : uniform.ids[j + 1]); +#endif + + if (texture->owner != INVALID_ID) { + texture = texture_owner.getornull(texture->owner); + ERR_FAIL_COND_V(!texture, INVALID_ID); //bug, should never happen + } + + img_info.imageLayout = texture->reading_layout; + + image_info.push_back(img_info); + } + + 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(INVALID_ID, "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(INVALID_ID, "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, INVALID_ID, "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), INVALID_ID, + "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; + +#ifdef DEBUG_ENABLED + bound_textures.push_back(texture->owner != INVALID_ID ? texture->owner : uniform.ids[j]); +#endif + + if (texture->owner != INVALID_ID) { + texture = texture_owner.getornull(texture->owner); + ERR_FAIL_COND_V(!texture, INVALID_ID); //bug, should never happen + } + + img_info.imageLayout = texture->reading_layout; + + image_info.push_back(img_info); + } + + 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: { + //todo + } break; + case UNIFORM_TYPE_TEXTURE_BUFFER: { + if (uniform.ids.size() != set_uniform.length) { + if (set_uniform.length > 1) { + ERR_FAIL_V_MSG(INVALID_ID, "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(INVALID_ID, "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, INVALID_ID, "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(INVALID_ID, "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(INVALID_ID, "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, INVALID_ID, "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, INVALID_ID, "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, INVALID_ID, + "Uniform buffer (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, INVALID_ID, "Uniform buffer (binding: " + itos(uniform.binding) + ") is invalid."); + + ERR_FAIL_COND_V_MSG(buffer->size != (uint32_t)set_uniform.length, INVALID_ID, + "Uniform buffer (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, INVALID_ID, + "Storage buffer (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, INVALID_ID, "Storage buffer (binding: " + itos(uniform.binding) + ") is invalid."); + + ERR_FAIL_COND_V_MSG(buffer->size != (uint32_t)set_uniform.length, INVALID_ID, + "Storage buffer (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, INVALID_ID, + "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, INVALID_ID); + + 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(INVALID_ID, "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.pipeline_layout = shader->pipeline_layout; + uniform_set.hash = shader->set_hashes[p_shader_set]; + + ID id = uniform_set_owner.make_id(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 ID *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; +} + +Error RenderingDeviceVulkan::buffer_update(ID p_buffer, uint32_t p_offset, uint32_t p_size, void *p_data, bool p_sync_with_draw) { + _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 (uniform_buffer_owner.owns(p_buffer)) { + buffer = uniform_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(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."); + + return _buffer_update(buffer, p_offset, (uint8_t *)p_data, p_size, p_sync_with_draw); +} + +/*************************/ +/**** RENDER PIPELINE ****/ +/*************************/ + +RenderingDevice::ID RenderingDeviceVulkan::render_pipeline_create(ID p_shader, ID p_framebuffer_format, ID p_vertex_description, 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, INVALID_ID); + + 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), INVALID_ID); + 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, INVALID_ID, + "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_description != INVALID_ID) { + //uses vertices, else it does not + ERR_FAIL_COND_V(!vertex_descriptions.has(p_vertex_description), INVALID_ID); + VertexDescriptionCache &vd = vertex_descriptions[p_vertex_description]; + + pipeline_vertex_input_state_create_info = vd.create_info; + + //validate with inputs + for (int i = 0; i < shader->vertex_input_locations.size(); i++) { + uint32_t location = shader->vertex_input_locations[i]; + const VertexDescriptionKey &k = vd.E->key(); + bool found = false; + for (int j = 0; j < k.vertex_descriptions.size(); j++) { + if (k.vertex_descriptions[j].location == location) { + found = true; + } + } + + ERR_FAIL_COND_V_MSG(!found, INVALID_ID, + "Shader vertex input location (" + itos(location) + ") 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_locations.size(), INVALID_ID, + "Shader contains vertex inputs (" + itos(shader->vertex_input_locations.size()) + ") but no vertex input description was provided for pipeline creation."); + } + //input assembly + + ERR_FAIL_INDEX_V(p_render_primitive, RENDER_PRIMITIVE_MAX, INVALID_ID); + + 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, INVALID_ID); + 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, INVALID_ID); + 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(), INVALID_ID); + 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, INVALID_ID); + 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, INVALID_ID); + 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, INVALID_ID); + 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, INVALID_ID); + 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, INVALID_ID); + 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, INVALID_ID); + 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, INVALID_ID); + 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, INVALID_ID); + 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, INVALID_ID); + 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, INVALID_ID); + 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(), INVALID_ID); + + 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, INVALID_ID); + 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, INVALID_ID); + 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, INVALID_ID); + 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, INVALID_ID); + 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, INVALID_ID); + 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, INVALID_ID); + 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.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, INVALID_ID); + + pipeline.dynamic_state = p_dynamic_state_flags; + pipeline.framebuffer_format = p_framebuffer_format; + pipeline.vertex_format = p_vertex_description; + pipeline.uses_restart_indices = input_assembly_create_info.primitiveRestartEnable; + pipeline.set_hashes = shader->set_hashes; + + static const uint32_t primitive_divisor[RENDER_PRIMITIVE_MAX] = { + 1, 2, 1, 1, 1, 3, 1, 1, 1, 1, 1 + }; + pipeline.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.primitive_minimum = primitive_minimum[p_render_primitive]; + + //create ID to associate with this pipeline + ID id = pipeline_owner.make_id(pipeline); + //now add aall the dependencies + _add_dependency(id, p_shader); + return id; +} + +/****************/ +/**** SCREEN ****/ +/****************/ + +int RenderingDeviceVulkan::screen_get_width(int p_screen) const { + _THREAD_SAFE_METHOD_ + + return context->get_screen_width(p_screen); +} +int RenderingDeviceVulkan::screen_get_height(int p_screen) const { + _THREAD_SAFE_METHOD_ + + return context->get_screen_height(p_screen); +} +RenderingDevice::ID 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::ID 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."); + VkCommandBuffer command_buffer = frames[frame].draw_command_buffer; + draw_list = memnew(DrawList); + draw_list->command_buffer = command_buffer; + draw_list->validation.framebuffer_format = screen_get_framebuffer_format(); + 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->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(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_x; + + vkCmdSetScissor(command_buffer, 0, 1, &scissor); + + return ID_TYPE_DRAW_LIST; +} + +Error RenderingDeviceVulkan::_draw_list_setup_framebuffer(Framebuffer *p_framebuffer, InitialAction p_initial_action, FinalAction p_final_action, VkFramebuffer *r_framebuffer, VkRenderPass *r_render_pass) { + + Framebuffer::VersionKey vk; + vk.initial_action = p_initial_action; + vk.final_action = p_final_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_action, p_final_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); + } + 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_action, FinalAction p_final_action, const Vector<Color> &p_clear_colors, 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; + if (p_initial_action == INITIAL_ACTION_CLEAR) { + 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 (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 = 1.0; + clear_value.depthStencil.stencil = 0; + } 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.push_back(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_textures = p_final_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; +} + +RenderingDevice::ID RenderingDeviceVulkan::draw_list_begin(ID p_framebuffer, InitialAction p_initial_action, FinalAction p_final_action, const Vector<Color> &p_clear_colors, const Rect2 &p_region) { + + _THREAD_SAFE_METHOD_ + + Framebuffer *framebuffer = framebuffer_owner.getornull(p_framebuffer); + ERR_FAIL_COND_V(!framebuffer, INVALID_ID); + + Point2i viewport_offset; + Point2i viewport_size = framebuffer->size; + + if (p_region != Rect2()) { //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_action == INITIAL_ACTION_CLEAR) { //check clear values + + int color_attachments = framebuffer_formats[framebuffer->format_id].color_attachments; + ERR_FAIL_COND_V_MSG(p_clear_colors.size() != color_attachments, INVALID_ID, + "Clear color values supplied (" + itos(p_clear_colors.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_action, p_final_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_action, p_final_action, p_clear_colors, 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; + draw_list->validation.framebuffer_format = framebuffer->format_id; + draw_list_count = 0; + draw_list_split = false; + + 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); + + return ID_TYPE_DRAW_LIST; +} + +Error RenderingDeviceVulkan::draw_list_begin_split(ID p_framebuffer, uint32_t p_splits, ID *r_split_ids, InitialAction p_initial_action, FinalAction p_final_action, const Vector<Color> &p_clear_colors, 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; + + if (p_region != Rect2()) { //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_action == INITIAL_ACTION_CLEAR) { //check clear values + + int color_attachments = framebuffer_formats[framebuffer->format_id].color_attachments; + ERR_FAIL_COND_V_MSG(p_clear_colors.size() != color_attachments, ERR_INVALID_PARAMETER, + "Clear color values supplied (" + itos(p_clear_colors.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_action, p_final_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_action, p_final_action, p_clear_colors, 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; + draw_list[i].validation.framebuffer_format = framebuffer->format_id; + + 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] = (ID(1) << ID(ID_TYPE_SPLIT_DRAW_LIST)) + i; + } + + return OK; +} + +RenderingDeviceVulkan::DrawList *RenderingDeviceVulkan::_get_draw_list_ptr(ID 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 >> ID(ID_BASE_SHIFT) == ID_TYPE_SPLIT_DRAW_LIST) { + if (!draw_list_split) { + return NULL; + } + + uint64_t index = p_id & ((ID(1) << ID(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(ID p_list, ID p_render_pipeline) { + + DrawList *dl = _get_draw_list_ptr(p_list); + ERR_FAIL_COND(!dl); + ERR_FAIL_COND_MSG(!dl->validation.active, "Submitted Draw Lists can no longer be modified."); + + const RenderPipeline *pipeline = pipeline_owner.getornull(p_render_pipeline); + ERR_FAIL_COND(!pipeline); + + ERR_FAIL_COND(pipeline->framebuffer_format != dl->validation.framebuffer_format); + + vkCmdBindPipeline(dl->command_buffer, VK_PIPELINE_BIND_POINT_GRAPHICS, pipeline->pipeline); + + //update render pass pipeline info + dl->validation.pipeline_active = true; + dl->validation.pipeline_dynamic_state = pipeline->dynamic_state; + dl->validation.pipeline_vertex_format = pipeline->vertex_format; + dl->validation.pipeline_uses_restart_indices = pipeline->uses_restart_indices; + dl->validation.pipeline_primitive_divisor = pipeline->primitive_divisor; + + dl->validation.pipeline_primitive_minimum = pipeline->primitive_minimum; + dl->validation.pipeline_set_hashes = pipeline->set_hashes; +} + +void RenderingDeviceVulkan::draw_list_bind_uniform_set(ID p_list, ID p_uniform_set, uint32_t p_index) { + ERR_FAIL_COND_MSG(p_index >= limits.maxBoundDescriptorSets, + "Attempting to bind a descriptor set (" + itos(p_index) + ") greater than what the hardware supports (" + itos(limits.maxBoundDescriptorSets) + ")."); + DrawList *dl = _get_draw_list_ptr(p_list); + ERR_FAIL_COND(!dl); + ERR_FAIL_COND_MSG(!dl->validation.active, "Submitted Draw Lists can no longer be modified."); + + const UniformSet *uniform_set = uniform_set_owner.getornull(p_uniform_set); + ERR_FAIL_COND(!uniform_set); + + if ((uint32_t)dl->validation.set_hashes.size() <= p_index) { + uint32_t csize = dl->validation.set_hashes.size(); + uint32_t new_size = p_uniform_set + 1; + dl->validation.set_hashes.resize(new_size); + for (uint32_t i = csize; i < new_size; i++) { + dl->validation.set_hashes.write[i] = 0; + } + } +#ifdef DEBUG_ENABLED + //validate that textures used are not bound + //this can be a bit slow in large descriptor sets, + //so it's disabled on release + uint32_t tb_count = uniform_set->textures.size(); + const ID *tb_ptr = uniform_set->textures.ptr(); + uint32_t bound_count = draw_list_bound_textures.size(); + const ID *bound_ptr = draw_list_bound_textures.ptr(); + for (uint32_t i = 0; i < tb_count; i++) { + for (uint32_t j = 0; j < bound_count; j++) { + ERR_FAIL_COND_MSG(tb_ptr[i] == bound_ptr[j], + "Attempted to use the same texture in framebuffer attachment and a uniform set, this is not allowed."); + } + } +#endif + dl->validation.set_hashes.write[p_index] = uniform_set->hash; + + vkCmdBindDescriptorSets(dl->command_buffer, VK_PIPELINE_BIND_POINT_GRAPHICS, uniform_set->pipeline_layout, p_index, 1, &uniform_set->descriptor_set, 0, NULL); +} + +void RenderingDeviceVulkan::draw_list_bind_vertex_array(ID p_list, ID p_vertex_array) { + DrawList *dl = _get_draw_list_ptr(p_list); + ERR_FAIL_COND(!dl); + ERR_FAIL_COND_MSG(!dl->validation.active, "Submitted Draw Lists can no longer be modified."); + + const VertexArray *vertex_array = vertex_array_owner.getornull(p_vertex_array); + ERR_FAIL_COND(!vertex_array); + dl->validation.vertex_format = vertex_array->description; + dl->validation.vertex_array_size = vertex_array->vertex_count; + dl->validation.vertex_max_instances_allowed = vertex_array->max_instances_allowed; + + vkCmdBindVertexBuffers(dl->command_buffer, 0, vertex_array->buffers.size(), vertex_array->buffers.ptr(), vertex_array->offsets.ptr()); +} +void RenderingDeviceVulkan::draw_list_bind_index_array(ID p_list, ID p_index_array) { + + DrawList *dl = _get_draw_list_ptr(p_list); + ERR_FAIL_COND(!dl); + ERR_FAIL_COND_MSG(!dl->validation.active, "Submitted Draw Lists can no longer be modified."); + + const IndexArray *index_array = index_array_owner.getornull(p_index_array); + ERR_FAIL_COND(!index_array); + + dl->validation.index_array_size = index_array->indices; + dl->validation.index_array_max_index = index_array->max_index; + 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_draw(ID p_list, bool p_use_indices, uint32_t p_instances) { + + DrawList *dl = _get_draw_list_ptr(p_list); + ERR_FAIL_COND(!dl); + ERR_FAIL_COND_MSG(!dl->validation.active, "Submitted Draw Lists can no longer be modified."); + + 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) + ")."); + } + //compare hashes + if (dl->validation.pipeline_set_hashes.size()) { + ERR_FAIL_COND_MSG(dl->validation.pipeline_set_hashes.size() > dl->validation.set_hashes.size(), + "Render pipeline requires uniform sets which were not set at the time of drawing."); + + uint32_t hash_count = dl->validation.pipeline_set_hashes.size(); + const uint32_t *phashes = dl->validation.pipeline_set_hashes.ptr(); + const uint32_t *shashes = dl->validation.set_hashes.ptr(); + + for (uint32_t i = 0; i < hash_count; i++) { + if (phashes[i] == 0) { + continue; //not used by pipeline, no need to check + } + if (phashes[i] != shashes[i]) { + if (shashes[i] == 0) { + ERR_FAIL_MSG("Uniforms were never supplied for set (" + itos(i) + ") at the time of drawing, which are required by the pipeline"); + } else { + ERR_FAIL_MSG("Uniforms supplied for set (" + itos(i) + ") are not the same format as required by the pipeline shader."); + } + } + } + } + + if (p_use_indices) { + ERR_FAIL_COND_MSG(!dl->validation.index_array_size, + "Draw command requested indices, but no index buffer was set."); + if (dl->validation.pipeline_vertex_format != INVALID_ID) { + //uses vertices, do some vertex validations + ERR_FAIL_COND_MSG(dl->validation.vertex_array_size < dl->validation.index_array_max_index, + "Index array references (max index: " + itos(dl->validation.index_array_max_index) + ") indices beyond the vertex array size (" + itos(dl->validation.vertex_array_size) + ")."); + } + + ERR_FAIL_COND_MSG(dl->validation.pipeline_uses_restart_indices != dl->validation.index_buffer_uses_restart_indices, + "The usage of restart indices in index buffer does not match the render primitive in the pipeline."); + + uint32_t to_draw = dl->validation.index_array_size; + + 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) + ")."); + + vkCmdDrawIndexed(dl->command_buffer, to_draw, p_instances, dl->validation.index_array_offset, 0, 0); + } else { + ERR_FAIL_COND_MSG(dl->validation.pipeline_vertex_format == INVALID_ID, + "Draw command lacks indices, but pipeline format does not use vertices."); + + uint32_t to_draw = dl->validation.vertex_array_size; + + 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) + ")."); + + vkCmdDraw(dl->command_buffer, to_draw, p_instances, 0, 0); + } +} + +void RenderingDeviceVulkan::draw_list_enable_scissor(ID p_list, const Rect2 &p_rect) { +} +void RenderingDeviceVulkan::draw_list_disable_scissor(ID p_list) { +} + +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; + } + + if (draw_list_unbind_textures) { + 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 + texture->bound = false; + } + } + draw_list_bound_textures.clear(); +} +#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(ID 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; + 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 (pipeline_owner.owns(p_id)) { + RenderPipeline *pipeline = pipeline_owner.getornull(p_id); + frames[frame].pipelines_to_dispose_of.push_back(*pipeline); + pipeline_owner.free(p_id); + } else { + ERR_PRINT("Attempted to free invalid ID: " + itos(p_id)); + } +} +void RenderingDeviceVulkan::free(ID p_id) { + + _THREAD_SAFE_METHOD_ + + _free_dependencies(p_id); //recursively erase dependencies first, to avoid potential API problems + _free_internal(p_id); +} + +void RenderingDeviceVulkan::finalize_frame() { + + _THREAD_SAFE_METHOD_ + + 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)."); + } + + { //complete the setup buffer (that needs to be processed before anything else) + vkEndCommandBuffer(frames[frame].setup_command_buffer); + vkEndCommandBuffer(frames[frame].draw_command_buffer); + } +} + +void RenderingDeviceVulkan::_free_pending_resources() { + //free in dependency usage order, so nothing weird happens + + //pipelines + while (frames[frame].pipelines_to_dispose_of.front()) { + RenderPipeline *pipeline = &frames[frame].pipelines_to_dispose_of.front()->get(); + + vkDestroyPipeline(device, pipeline->pipeline, NULL); + + frames[frame].pipelines_to_dispose_of.pop_front(); + } + + //uniform sets + while (frames[frame].uniform_sets_to_dispose_of.front()) { + UniformSet *uniform_set = &frames[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[frame].uniform_sets_to_dispose_of.pop_front(); + } + + //buffer views + while (frames[frame].buffer_views_to_dispose_of.front()) { + VkBufferView buffer_view = frames[frame].buffer_views_to_dispose_of.front()->get(); + + vkDestroyBufferView(device, buffer_view, NULL); + + frames[frame].buffer_views_to_dispose_of.pop_front(); + } + + //shaders + while (frames[frame].shaders_to_dispose_of.front()) { + Shader *shader = &frames[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[frame].shaders_to_dispose_of.pop_front(); + } + + //samplers + while (frames[frame].samplers_to_dispose_of.front()) { + VkSampler sampler = frames[frame].samplers_to_dispose_of.front()->get(); + + vkDestroySampler(device, sampler, NULL); + + frames[frame].samplers_to_dispose_of.pop_front(); + } + + //framebuffers + while (frames[frame].framebuffers_to_dispose_of.front()) { + Framebuffer *framebuffer = &frames[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[frame].framebuffers_to_dispose_of.pop_front(); + } + + //textures + while (frames[frame].textures_to_dispose_of.front()) { + Texture *texture = &frames[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 == INVALID_ID) { + //actually owns the image and the allocation too + vmaDestroyImage(allocator, texture->image, texture->allocation); + vmaFreeMemory(allocator, texture->allocation); + } + frames[frame].textures_to_dispose_of.pop_front(); + } + + //buffers + while (frames[frame].buffers_to_dispose_of.front()) { + _buffer_free(&frames[frame].buffers_to_dispose_of.front()->get()); + + frames[frame].buffers_to_dispose_of.pop_front(); + } +} + +void RenderingDeviceVulkan::advance_frame() { + + _THREAD_SAFE_METHOD_ + + //advance the frame + frame = (frame + 1) % frame_count; + + //erase pending resources + _free_pending_resources(); + + //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; + } +} + +void RenderingDeviceVulkan::initialize(VulkanContext *p_context) { + + context = p_context; + device = p_context->get_device(); + frame_count = p_context->get_frame_count(); + limits = p_context->get_device_limits(); + + { //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++) { + + { //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); + } + } + + { + //begin the first command buffer for the first frame, so + //setting up things can be done in the meantime until finalize_frame(), 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); + print_line("update size: " + itos(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; +} +void RenderingDeviceVulkan::finalize() { + + memdelete_arr(frames); +} + +RenderingDeviceVulkan::RenderingDeviceVulkan() { +} |