summaryrefslogtreecommitdiffstats
path: root/src/vulkan/formats.c
diff options
context:
space:
mode:
Diffstat (limited to 'src/vulkan/formats.c')
-rw-r--r--src/vulkan/formats.c616
1 files changed, 616 insertions, 0 deletions
diff --git a/src/vulkan/formats.c b/src/vulkan/formats.c
new file mode 100644
index 0000000..f0eb0fb
--- /dev/null
+++ b/src/vulkan/formats.c
@@ -0,0 +1,616 @@
+/*
+ * This file is part of libplacebo.
+ *
+ * libplacebo is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public
+ * License as published by the Free Software Foundation; either
+ * version 2.1 of the License, or (at your option) any later version.
+ *
+ * libplacebo is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public
+ * License along with libplacebo. If not, see <http://www.gnu.org/licenses/>.
+ */
+
+#include "formats.h"
+
+#define FMT(_name, num, size, ftype, bits, idx) \
+ (struct pl_fmt_t) { \
+ .name = _name, \
+ .type = PL_FMT_##ftype, \
+ .num_components = num, \
+ .component_depth = bits, \
+ .internal_size = size, \
+ .opaque = false, \
+ .texel_size = size, \
+ .texel_align = size, \
+ .host_bits = bits, \
+ .sample_order = idx, \
+ }
+
+#define IDX(...) {__VA_ARGS__}
+#define BITS(...) {__VA_ARGS__}
+
+#define REGFMT(name, num, bits, type) \
+ FMT(name, num, (num) * (bits) / 8, type, \
+ BITS(bits, bits, bits, bits), \
+ IDX(0, 1, 2, 3))
+
+#define EMUFMT(_name, in, en, ib, eb, ftype) \
+ (struct pl_fmt_t) { \
+ .name = _name, \
+ .type = PL_FMT_##ftype, \
+ .num_components = en, \
+ .component_depth = BITS(ib, ib, ib, ib),\
+ .internal_size = (in) * (ib) / 8, \
+ .opaque = false, \
+ .emulated = true, \
+ .texel_size = (en) * (eb) / 8, \
+ .texel_align = (eb) / 8, \
+ .host_bits = BITS(eb, eb, eb, eb),\
+ .sample_order = IDX(0, 1, 2, 3), \
+ }
+
+#define PACKED16FMT(_name, num, b) \
+ (struct pl_fmt_t) { \
+ .name = _name, \
+ .type = PL_FMT_UNORM, \
+ .num_components = num, \
+ .component_depth = BITS(b, b, b, b), \
+ .internal_size = (num) * 2, \
+ .texel_size = (num) * 2, \
+ .texel_align = (num) * 2, \
+ .host_bits = BITS(16, 16, 16, 16),\
+ .sample_order = IDX(0, 1, 2, 3), \
+ }
+
+#define PLANARFMT(_name, planes, size, bits) \
+ (struct pl_fmt_t) { \
+ .name = _name, \
+ .type = PL_FMT_UNORM, \
+ .num_planes = planes, \
+ .num_components = 3, \
+ .component_depth = {bits, bits, bits}, \
+ .internal_size = size, \
+ .opaque = true, \
+ }
+
+static const struct vk_format rgb8e = {
+ .tfmt = VK_FORMAT_R8G8B8A8_UNORM,
+ .bfmt = VK_FORMAT_R8G8B8_UNORM,
+ .icomps = 4,
+ .fmt = EMUFMT("rgb8", 4, 3, 8, 8, UNORM),
+};
+
+static const struct vk_format rgb16e = {
+ .tfmt = VK_FORMAT_R16G16B16A16_UNORM,
+ .bfmt = VK_FORMAT_R16G16B16_UNORM,
+ .icomps = 4,
+ .fmt = EMUFMT("rgb16", 4, 3, 16, 16, UNORM),
+};
+
+static const struct vk_format vk_formats[] = {
+ // Regular, byte-aligned integer formats
+ {VK_FORMAT_R8_UNORM, REGFMT("r8", 1, 8, UNORM)},
+ {VK_FORMAT_R8G8_UNORM, REGFMT("rg8", 2, 8, UNORM)},
+ {VK_FORMAT_R8G8B8_UNORM, REGFMT("rgb8", 3, 8, UNORM), .emufmt = &rgb8e},
+ {VK_FORMAT_R8G8B8A8_UNORM, REGFMT("rgba8", 4, 8, UNORM)},
+ {VK_FORMAT_R16_UNORM, REGFMT("r16", 1, 16, UNORM)},
+ {VK_FORMAT_R16G16_UNORM, REGFMT("rg16", 2, 16, UNORM)},
+ {VK_FORMAT_R16G16B16_UNORM, REGFMT("rgb16", 3, 16, UNORM), .emufmt = &rgb16e},
+ {VK_FORMAT_R16G16B16A16_UNORM, REGFMT("rgba16", 4, 16, UNORM)},
+
+ {VK_FORMAT_R8_SNORM, REGFMT("r8s", 1, 8, SNORM)},
+ {VK_FORMAT_R8G8_SNORM, REGFMT("rg8s", 2, 8, SNORM)},
+ {VK_FORMAT_R8G8B8_SNORM, REGFMT("rgb8s", 3, 8, SNORM)},
+ {VK_FORMAT_R8G8B8A8_SNORM, REGFMT("rgba8s", 4, 8, SNORM)},
+ {VK_FORMAT_R16_SNORM, REGFMT("r16s", 1, 16, SNORM)},
+ {VK_FORMAT_R16G16_SNORM, REGFMT("rg16s", 2, 16, SNORM)},
+ {VK_FORMAT_R16G16B16_SNORM, REGFMT("rgb16s", 3, 16, SNORM)},
+ {VK_FORMAT_R16G16B16A16_SNORM, REGFMT("rgba16s", 4, 16, SNORM)},
+
+ // Float formats (native formats: hf = half float, df = double float)
+ {VK_FORMAT_R16_SFLOAT, REGFMT("r16hf", 1, 16, FLOAT)},
+ {VK_FORMAT_R16G16_SFLOAT, REGFMT("rg16hf", 2, 16, FLOAT)},
+ {VK_FORMAT_R16G16B16_SFLOAT, REGFMT("rgb16hf", 3, 16, FLOAT)},
+ {VK_FORMAT_R16G16B16A16_SFLOAT, REGFMT("rgba16hf", 4, 16, FLOAT)},
+ {VK_FORMAT_R32_SFLOAT, REGFMT("r32f", 1, 32, FLOAT)},
+ {VK_FORMAT_R32G32_SFLOAT, REGFMT("rg32f", 2, 32, FLOAT)},
+ {VK_FORMAT_R32G32B32_SFLOAT, REGFMT("rgb32f", 3, 32, FLOAT)},
+ {VK_FORMAT_R32G32B32A32_SFLOAT, REGFMT("rgba32f", 4, 32, FLOAT)},
+
+ // Float formats (emulated upload/download)
+ {VK_FORMAT_R16_SFLOAT, EMUFMT("r16f", 1, 1, 16, 32, FLOAT)},
+ {VK_FORMAT_R16G16_SFLOAT, EMUFMT("rg16f", 2, 2, 16, 32, FLOAT)},
+ {VK_FORMAT_R16G16B16_SFLOAT, EMUFMT("rgb16f", 3, 3, 16, 32, FLOAT)},
+ {VK_FORMAT_R16G16B16A16_SFLOAT, EMUFMT("rgba16f", 4, 4, 16, 32, FLOAT)},
+
+ // Integer-sampled formats
+ {VK_FORMAT_R8_UINT, REGFMT("r8u", 1, 8, UINT)},
+ {VK_FORMAT_R8G8_UINT, REGFMT("rg8u", 2, 8, UINT)},
+ {VK_FORMAT_R8G8B8_UINT, REGFMT("rgb8u", 3, 8, UINT)},
+ {VK_FORMAT_R8G8B8A8_UINT, REGFMT("rgba8u", 4, 8, UINT)},
+ {VK_FORMAT_R16_UINT, REGFMT("r16u", 1, 16, UINT)},
+ {VK_FORMAT_R16G16_UINT, REGFMT("rg16u", 2, 16, UINT)},
+ {VK_FORMAT_R16G16B16_UINT, REGFMT("rgb16u", 3, 16, UINT)},
+ {VK_FORMAT_R16G16B16A16_UINT, REGFMT("rgba16u", 4, 16, UINT)},
+ {VK_FORMAT_R32_UINT, REGFMT("r32u", 1, 32, UINT)},
+ {VK_FORMAT_R32G32_UINT, REGFMT("rg32u", 2, 32, UINT)},
+ {VK_FORMAT_R32G32B32_UINT, REGFMT("rgb32u", 3, 32, UINT)},
+ {VK_FORMAT_R32G32B32A32_UINT, REGFMT("rgba32u", 4, 32, UINT)},
+
+ {VK_FORMAT_R8_SINT, REGFMT("r8i", 1, 8, SINT)},
+ {VK_FORMAT_R8G8_SINT, REGFMT("rg8i", 2, 8, SINT)},
+ {VK_FORMAT_R8G8B8_SINT, REGFMT("rgb8i", 3, 8, SINT)},
+ {VK_FORMAT_R8G8B8A8_SINT, REGFMT("rgba8i", 4, 8, SINT)},
+ {VK_FORMAT_R16_SINT, REGFMT("r16i", 1, 16, SINT)},
+ {VK_FORMAT_R16G16_SINT, REGFMT("rg16i", 2, 16, SINT)},
+ {VK_FORMAT_R16G16B16_SINT, REGFMT("rgb16i", 3, 16, SINT)},
+ {VK_FORMAT_R16G16B16A16_SINT, REGFMT("rgba16i", 4, 16, SINT)},
+ {VK_FORMAT_R32_SINT, REGFMT("r32i", 1, 32, SINT)},
+ {VK_FORMAT_R32G32_SINT, REGFMT("rg32i", 2, 32, SINT)},
+ {VK_FORMAT_R32G32B32_SINT, REGFMT("rgb32i", 3, 32, SINT)},
+ {VK_FORMAT_R32G32B32A32_SINT, REGFMT("rgba32i", 4, 32, SINT)},
+
+ // "Swapped" component order formats
+ {VK_FORMAT_B8G8R8_UNORM, FMT("bgr8", 3, 3, UNORM, BITS(8, 8, 8), IDX(2, 1, 0))},
+ {VK_FORMAT_B8G8R8A8_UNORM, FMT("bgra8", 4, 4, UNORM, BITS(8, 8, 8, 8), IDX(2, 1, 0, 3))},
+
+ {VK_FORMAT_B8G8R8_UINT, FMT("bgr8u", 3, 3, UINT, BITS(8, 8, 8), IDX(2, 1, 0))},
+ {VK_FORMAT_B8G8R8A8_UINT, FMT("bgra8u", 4, 4, UINT, BITS(8, 8, 8, 8), IDX(2, 1, 0, 3))},
+
+ {VK_FORMAT_B8G8R8_SINT, FMT("bgr8i", 3, 3, SINT, BITS(8, 8, 8), IDX(2, 1, 0))},
+ {VK_FORMAT_B8G8R8A8_SINT, FMT("bgra8i", 4, 4, SINT, BITS(8, 8, 8, 8), IDX(2, 1, 0, 3))},
+
+ // "Packed" integer formats
+ //
+ // Note: These have the component order reversed from what the vulkan name
+ // implies, because we order our IDX from LSB to MSB (consistent with the
+ // usual ordering from lowest byte to highest byte, on little endian
+ // platforms), but Vulkan names them from MSB to LSB.
+ {VK_FORMAT_R4G4_UNORM_PACK8, FMT("gr4", 2, 1, UNORM, BITS(4, 4), IDX(1, 0))},
+ {VK_FORMAT_B4G4R4A4_UNORM_PACK16, FMT("argb4", 4, 2, UNORM, BITS(4, 4, 4, 4), IDX(3, 0, 1, 2))},
+ {VK_FORMAT_R4G4B4A4_UNORM_PACK16, FMT("abgr4", 4, 2, UNORM, BITS(4, 4, 4, 4), IDX(3, 2, 1, 0))},
+
+ {VK_FORMAT_R5G6B5_UNORM_PACK16, FMT("bgr565", 3, 2, UNORM, BITS(5, 6, 5), IDX(2, 1, 0))},
+ {VK_FORMAT_B5G6R5_UNORM_PACK16, FMT("rgb565", 3, 2, UNORM, BITS(5, 6, 5), IDX(0, 1, 2))},
+
+ {VK_FORMAT_R5G5B5A1_UNORM_PACK16, FMT("a1bgr5", 4, 2, UNORM, BITS(1, 5, 5, 5), IDX(3, 2, 1, 0))},
+ {VK_FORMAT_B5G5R5A1_UNORM_PACK16, FMT("a1rgb5", 4, 2, UNORM, BITS(1, 5, 5, 5), IDX(3, 0, 1, 2))},
+ {VK_FORMAT_A1R5G5B5_UNORM_PACK16, FMT("bgr5a1", 4, 2, UNORM, BITS(5, 5, 5, 1), IDX(2, 1, 0, 3))},
+
+ {VK_FORMAT_A2B10G10R10_UNORM_PACK32, FMT("rgb10a2", 4, 4, UNORM, BITS(10, 10, 10, 2), IDX(0, 1, 2, 3))},
+ {VK_FORMAT_A2R10G10B10_UNORM_PACK32, FMT("bgr10a2", 4, 4, UNORM, BITS(10, 10, 10, 2), IDX(2, 1, 0, 3))},
+ {VK_FORMAT_A2B10G10R10_SNORM_PACK32, FMT("rgb10a2s", 4, 4, SNORM, BITS(10, 10, 10, 2), IDX(0, 1, 2, 3))},
+ {VK_FORMAT_A2R10G10B10_SNORM_PACK32, FMT("bgr10a2s", 4, 4, SNORM, BITS(10, 10, 10, 2), IDX(2, 1, 0, 3))},
+ {VK_FORMAT_A2B10G10R10_UINT_PACK32, FMT("rgb10a2u", 4, 4, UINT, BITS(10, 10, 10, 2), IDX(0, 1, 2, 3))},
+ {VK_FORMAT_A2R10G10B10_UINT_PACK32, FMT("bgr10a2u", 4, 4, UINT, BITS(10, 10, 10, 2), IDX(2, 1, 0, 3))},
+ {VK_FORMAT_A2B10G10R10_SINT_PACK32, FMT("rgb10a2i", 4, 4, SINT, BITS(10, 10, 10, 2), IDX(0, 1, 2, 3))},
+ {VK_FORMAT_A2R10G10B10_SINT_PACK32, FMT("bgr10a2i", 4, 4, SINT, BITS(10, 10, 10, 2), IDX(2, 1, 0, 3))},
+
+
+ // Packed 16 bit formats
+ {VK_FORMAT_R10X6_UNORM_PACK16, PACKED16FMT("rx10", 1, 10)},
+ {VK_FORMAT_R10X6G10X6_UNORM_2PACK16, PACKED16FMT("rxgx10", 2, 10)},
+ {VK_FORMAT_R12X4_UNORM_PACK16, PACKED16FMT("rx12", 1, 12)},
+ {VK_FORMAT_R12X4G12X4_UNORM_2PACK16, PACKED16FMT("rxgx12", 2, 12)},
+
+ // FIXME: enabling these requires VK_EXT_rgba10x6_formats or equivalent
+ // {VK_FORMAT_R10X6G10X6B10X6A10X6_UNORM_4PACK16, PACKED16FMT("rxgxbxax10", 4, 10)},
+ // {VK_FORMAT_R12X4G12X4B12X4A12X4_UNORM_4PACK16, PACKED16FMT("rxgxbxax12", 4, 12)},
+
+ // Planar formats
+ {VK_FORMAT_G8_B8_R8_3PLANE_420_UNORM, PLANARFMT("g8_b8_r8_420", 3, 12, 8),
+ .pfmt = {
+ {VK_FORMAT_R8_UNORM},
+ {VK_FORMAT_R8_UNORM, .sx = 1, .sy = 1},
+ {VK_FORMAT_R8_UNORM, .sx = 1, .sy = 1},
+ },
+ },
+ {VK_FORMAT_G8_B8_R8_3PLANE_422_UNORM, PLANARFMT("g8_b8_r8_422", 3, 16, 8),
+ .pfmt = {
+ {VK_FORMAT_R8_UNORM},
+ {VK_FORMAT_R8_UNORM, .sx = 1},
+ {VK_FORMAT_R8_UNORM, .sx = 1},
+ },
+ },
+ {VK_FORMAT_G8_B8_R8_3PLANE_444_UNORM, PLANARFMT("g8_b8_r8_444", 3, 24, 8),
+ .pfmt = {
+ {VK_FORMAT_R8_UNORM},
+ {VK_FORMAT_R8_UNORM},
+ {VK_FORMAT_R8_UNORM},
+ },
+ },
+
+ {VK_FORMAT_G16_B16_R16_3PLANE_420_UNORM, PLANARFMT("g16_b16_r16_420", 3, 24, 16),
+ .pfmt = {
+ {VK_FORMAT_R16_UNORM},
+ {VK_FORMAT_R16_UNORM, .sx = 1, .sy = 1},
+ {VK_FORMAT_R16_UNORM, .sx = 1, .sy = 1},
+ },
+ },
+ {VK_FORMAT_G16_B16_R16_3PLANE_422_UNORM, PLANARFMT("g16_b16_r16_422", 3, 32, 16),
+ .pfmt = {
+ {VK_FORMAT_R16_UNORM},
+ {VK_FORMAT_R16_UNORM, .sx = 1},
+ {VK_FORMAT_R16_UNORM, .sx = 1},
+ },
+ },
+ {VK_FORMAT_G16_B16_R16_3PLANE_444_UNORM, PLANARFMT("g16_b16_r16_444", 3, 48, 16),
+ .pfmt = {
+ {VK_FORMAT_R16_UNORM},
+ {VK_FORMAT_R16_UNORM},
+ {VK_FORMAT_R16_UNORM},
+ },
+ },
+
+ {VK_FORMAT_G10X6_B10X6_R10X6_3PLANE_420_UNORM_3PACK16, PLANARFMT("gx10_bx10_rx10_420", 3, 24, 10),
+ .pfmt = {
+ {VK_FORMAT_R10X6_UNORM_PACK16},
+ {VK_FORMAT_R10X6_UNORM_PACK16, .sx = 1, .sy = 1},
+ {VK_FORMAT_R10X6_UNORM_PACK16, .sx = 1, .sy = 1},
+ },
+ },
+ {VK_FORMAT_G10X6_B10X6_R10X6_3PLANE_422_UNORM_3PACK16, PLANARFMT("gx10_bx10_rx10_422", 3, 32, 10),
+ .pfmt = {
+ {VK_FORMAT_R10X6_UNORM_PACK16},
+ {VK_FORMAT_R10X6_UNORM_PACK16, .sx = 1},
+ {VK_FORMAT_R10X6_UNORM_PACK16, .sx = 1},
+ },
+ },
+ {VK_FORMAT_G10X6_B10X6_R10X6_3PLANE_444_UNORM_3PACK16, PLANARFMT("gx10_bx10_rx10_444", 3, 48, 10),
+ .pfmt = {
+ {VK_FORMAT_R10X6_UNORM_PACK16},
+ {VK_FORMAT_R10X6_UNORM_PACK16},
+ {VK_FORMAT_R10X6_UNORM_PACK16},
+ },
+ },
+
+ {VK_FORMAT_G12X4_B12X4_R12X4_3PLANE_420_UNORM_3PACK16, PLANARFMT("gx12_bx12_rx12_420", 3, 24, 12),
+ .pfmt = {
+ {VK_FORMAT_R12X4_UNORM_PACK16},
+ {VK_FORMAT_R12X4_UNORM_PACK16, .sx = 1, .sy = 1},
+ {VK_FORMAT_R12X4_UNORM_PACK16, .sx = 1, .sy = 1},
+ },
+ },
+ {VK_FORMAT_G12X4_B12X4_R12X4_3PLANE_422_UNORM_3PACK16, PLANARFMT("gx12_bx12_rx12_422", 3, 32, 12),
+ .pfmt = {
+ {VK_FORMAT_R12X4_UNORM_PACK16},
+ {VK_FORMAT_R12X4_UNORM_PACK16, .sx = 1},
+ {VK_FORMAT_R12X4_UNORM_PACK16, .sx = 1},
+ },
+ },
+ {VK_FORMAT_G12X4_B12X4_R12X4_3PLANE_444_UNORM_3PACK16, PLANARFMT("gx12_bx12_rx12_444", 3, 48, 12),
+ .pfmt = {
+ {VK_FORMAT_R12X4_UNORM_PACK16},
+ {VK_FORMAT_R12X4_UNORM_PACK16},
+ {VK_FORMAT_R12X4_UNORM_PACK16},
+ },
+ },
+
+ {VK_FORMAT_G8_B8R8_2PLANE_420_UNORM, PLANARFMT("g8_br8_420", 2, 12, 8),
+ .pfmt = {
+ {VK_FORMAT_R8_UNORM},
+ {VK_FORMAT_R8G8_UNORM, .sx = 1, .sy = 1},
+ },
+ },
+ {VK_FORMAT_G8_B8R8_2PLANE_422_UNORM, PLANARFMT("g8_br8_422", 2, 16, 8),
+ .pfmt = {
+ {VK_FORMAT_R8_UNORM},
+ {VK_FORMAT_R8G8_UNORM, .sx = 1},
+ },
+ },
+ {VK_FORMAT_G8_B8R8_2PLANE_444_UNORM, PLANARFMT("g8_br8_444", 2, 24, 8),
+ .min_ver = VK_API_VERSION_1_3,
+ .pfmt = {
+ {VK_FORMAT_R8_UNORM},
+ {VK_FORMAT_R8G8_UNORM},
+ },
+ },
+
+ {VK_FORMAT_G16_B16R16_2PLANE_420_UNORM, PLANARFMT("g16_br16_420", 2, 24, 16),
+ .pfmt = {
+ {VK_FORMAT_R16_UNORM},
+ {VK_FORMAT_R16G16_UNORM, .sx = 1, .sy = 1},
+ },
+ },
+ {VK_FORMAT_G16_B16R16_2PLANE_422_UNORM, PLANARFMT("g16_br16_422", 2, 32, 16),
+ .pfmt = {
+ {VK_FORMAT_R16_UNORM},
+ {VK_FORMAT_R16G16_UNORM, .sx = 1},
+ },
+ },
+ {VK_FORMAT_G16_B16R16_2PLANE_444_UNORM, PLANARFMT("g16_br16_444", 2, 48, 16),
+ .min_ver = VK_API_VERSION_1_3,
+ .pfmt = {
+ {VK_FORMAT_R16_UNORM},
+ {VK_FORMAT_R16G16_UNORM},
+ },
+ },
+
+ {VK_FORMAT_G10X6_B10X6R10X6_2PLANE_420_UNORM_3PACK16, PLANARFMT("gx10_bxrx10_420", 2, 24, 10),
+ .pfmt = {
+ {VK_FORMAT_R10X6_UNORM_PACK16},
+ {VK_FORMAT_R10X6G10X6_UNORM_2PACK16, .sx = 1, .sy = 1},
+ },
+ },
+ {VK_FORMAT_G10X6_B10X6R10X6_2PLANE_422_UNORM_3PACK16, PLANARFMT("gx10_bxrx10_422", 2, 32, 10),
+ .pfmt = {
+ {VK_FORMAT_R10X6_UNORM_PACK16},
+ {VK_FORMAT_R10X6G10X6_UNORM_2PACK16, .sx = 1},
+ },
+ },
+ {VK_FORMAT_G10X6_B10X6R10X6_2PLANE_444_UNORM_3PACK16, PLANARFMT("gx10_bxrx10_444", 2, 48, 10),
+ .min_ver = VK_API_VERSION_1_3,
+ .pfmt = {
+ {VK_FORMAT_R10X6_UNORM_PACK16},
+ {VK_FORMAT_R10X6G10X6_UNORM_2PACK16},
+ },
+ },
+
+ {VK_FORMAT_G12X4_B12X4R12X4_2PLANE_420_UNORM_3PACK16, PLANARFMT("gx12_bxrx12_420", 2, 24, 12),
+ .pfmt = {
+ {VK_FORMAT_R12X4_UNORM_PACK16},
+ {VK_FORMAT_R12X4G12X4_UNORM_2PACK16, .sx = 1, .sy = 1},
+ },
+ },
+ {VK_FORMAT_G12X4_B12X4R12X4_2PLANE_422_UNORM_3PACK16, PLANARFMT("gx12_bxrx12_422", 2, 32, 12),
+ .pfmt = {
+ {VK_FORMAT_R12X4_UNORM_PACK16},
+ {VK_FORMAT_R12X4G12X4_UNORM_2PACK16, .sx = 1},
+ },
+ },
+ {VK_FORMAT_G12X4_B12X4R12X4_2PLANE_444_UNORM_3PACK16, PLANARFMT("gx12_bxrx12_444", 2, 48, 12),
+ .min_ver = VK_API_VERSION_1_3,
+ .pfmt = {
+ {VK_FORMAT_R12X4_UNORM_PACK16},
+ {VK_FORMAT_R12X4G12X4_UNORM_2PACK16},
+ },
+ },
+
+ {0}
+};
+
+#undef BITS
+#undef IDX
+#undef REGFMT
+#undef FMT
+
+void vk_setup_formats(struct pl_gpu_t *gpu)
+{
+ struct pl_vk *p = PL_PRIV(gpu);
+ struct vk_ctx *vk = p->vk;
+ PL_ARRAY(pl_fmt) formats = {0};
+
+ // Texture format emulation requires at least support for texel buffers
+ bool has_emu = gpu->glsl.compute && gpu->limits.max_buffer_texels;
+
+ for (const struct vk_format *pvk_fmt = vk_formats; pvk_fmt->tfmt; pvk_fmt++) {
+ const struct vk_format *vk_fmt = pvk_fmt;
+
+ // Skip formats that require a too new version of Vulkan
+ if (vk_fmt->min_ver > vk->api_ver)
+ continue;
+
+ // Skip formats with innately emulated representation if unsupported
+ if (vk_fmt->fmt.emulated && !has_emu)
+ continue;
+
+ // Suppress some errors/warnings spit out by the format probing code
+ pl_log_level_cap(vk->log, PL_LOG_INFO);
+
+ bool has_drm_mods = vk->GetImageDrmFormatModifierPropertiesEXT;
+ VkDrmFormatModifierPropertiesEXT modifiers[16] = {0};
+ VkDrmFormatModifierPropertiesListEXT drm_props = {
+ .sType = VK_STRUCTURE_TYPE_DRM_FORMAT_MODIFIER_PROPERTIES_LIST_EXT,
+ .drmFormatModifierCount = PL_ARRAY_SIZE(modifiers),
+ .pDrmFormatModifierProperties = modifiers,
+ };
+
+ VkFormatProperties2KHR prop2 = {
+ .sType = VK_STRUCTURE_TYPE_FORMAT_PROPERTIES_2,
+ .pNext = has_drm_mods ? &drm_props : NULL,
+ };
+
+ vk->GetPhysicalDeviceFormatProperties2KHR(vk->physd, vk_fmt->tfmt, &prop2);
+
+ // If wholly unsupported, try falling back to the emulation formats
+ // for texture operations
+ VkFormatProperties *prop = &prop2.formatProperties;
+ while (has_emu && !prop->optimalTilingFeatures && vk_fmt->emufmt) {
+ vk_fmt = vk_fmt->emufmt;
+ vk->GetPhysicalDeviceFormatProperties2KHR(vk->physd, vk_fmt->tfmt, &prop2);
+ }
+
+ VkFormatFeatureFlags texflags = prop->optimalTilingFeatures;
+ VkFormatFeatureFlags bufflags = prop->bufferFeatures;
+ if (vk_fmt->fmt.emulated) {
+ // Emulated formats might have a different buffer representation
+ // than their texture representation. If they don't, assume their
+ // buffer representation is nonsensical (e.g. r16f)
+ if (vk_fmt->bfmt) {
+ vk->GetPhysicalDeviceFormatProperties(vk->physd, vk_fmt->bfmt, prop);
+ bufflags = prop->bufferFeatures;
+ } else {
+ bufflags = 0;
+ }
+ } else if (vk_fmt->fmt.num_planes) {
+ // Planar textures cannot be used directly
+ texflags = bufflags = 0;
+ }
+
+ pl_log_level_cap(vk->log, PL_LOG_NONE);
+
+ struct pl_fmt_t *fmt = pl_alloc_obj(gpu, fmt, struct pl_fmt_vk);
+ struct pl_fmt_vk *fmtp = PL_PRIV(fmt);
+ *fmt = vk_fmt->fmt;
+ *fmtp = (struct pl_fmt_vk) {
+ .vk_fmt = vk_fmt
+ };
+
+ // Always set the signature to the actual texture format, so we can use
+ // it to guarantee renderpass compatibility.
+ fmt->signature = (uint64_t) vk_fmt->tfmt;
+
+ // For sanity, clear the superfluous fields
+ for (int i = fmt->num_components; i < 4; i++) {
+ fmt->component_depth[i] = 0;
+ fmt->sample_order[i] = 0;
+ fmt->host_bits[i] = 0;
+ }
+
+ // We can set this universally
+ fmt->fourcc = pl_fmt_fourcc(fmt);
+
+ if (has_drm_mods) {
+
+ if (drm_props.drmFormatModifierCount == PL_ARRAY_SIZE(modifiers)) {
+ PL_WARN(gpu, "DRM modifier list for format %s possibly truncated",
+ fmt->name);
+ }
+
+ // Query the list of supported DRM modifiers from the driver
+ PL_ARRAY(uint64_t) modlist = {0};
+ for (int i = 0; i < drm_props.drmFormatModifierCount; i++) {
+ if (modifiers[i].drmFormatModifierPlaneCount > 1) {
+ PL_TRACE(gpu, "Ignoring format modifier %s of "
+ "format %s because its plane count %d > 1",
+ PRINT_DRM_MOD(modifiers[i].drmFormatModifier),
+ fmt->name, modifiers[i].drmFormatModifierPlaneCount);
+ continue;
+ }
+
+ // Only warn about texture format features relevant to us
+ const VkFormatFeatureFlags flag_mask =
+ VK_FORMAT_FEATURE_COLOR_ATTACHMENT_BLEND_BIT |
+ VK_FORMAT_FEATURE_SAMPLED_IMAGE_FILTER_LINEAR_BIT |
+ VK_FORMAT_FEATURE_SAMPLED_IMAGE_BIT |
+ VK_FORMAT_FEATURE_STORAGE_IMAGE_BIT |
+ VK_FORMAT_FEATURE_COLOR_ATTACHMENT_BIT |
+ VK_FORMAT_FEATURE_BLIT_SRC_BIT |
+ VK_FORMAT_FEATURE_BLIT_DST_BIT;
+
+
+ VkFormatFeatureFlags flags = modifiers[i].drmFormatModifierTilingFeatures;
+ if ((flags & flag_mask) != (texflags & flag_mask)) {
+ PL_DEBUG(gpu, "DRM format modifier %s of format %s "
+ "supports fewer caps (0x%"PRIx32") than optimal tiling "
+ "(0x%"PRIx32"), may result in limited capability!",
+ PRINT_DRM_MOD(modifiers[i].drmFormatModifier),
+ fmt->name, flags, texflags);
+ }
+
+ PL_ARRAY_APPEND(fmt, modlist, modifiers[i].drmFormatModifier);
+ }
+
+ fmt->num_modifiers = modlist.num;
+ fmt->modifiers = modlist.elem;
+
+ } else if (gpu->export_caps.tex & PL_HANDLE_DMA_BUF) {
+
+ // Hard-code a list of static mods that we're likely to support
+ static const uint64_t static_mods[2] = {
+ DRM_FORMAT_MOD_INVALID,
+ DRM_FORMAT_MOD_LINEAR,
+ };
+
+ fmt->num_modifiers = PL_ARRAY_SIZE(static_mods);
+ fmt->modifiers = static_mods;
+
+ }
+
+ struct { VkFormatFeatureFlags flags; enum pl_fmt_caps caps; } bufbits[] = {
+ {VK_FORMAT_FEATURE_VERTEX_BUFFER_BIT, PL_FMT_CAP_VERTEX},
+ {VK_FORMAT_FEATURE_UNIFORM_TEXEL_BUFFER_BIT, PL_FMT_CAP_TEXEL_UNIFORM},
+ {VK_FORMAT_FEATURE_STORAGE_TEXEL_BUFFER_BIT, PL_FMT_CAP_TEXEL_STORAGE},
+ };
+
+ for (int i = 0; i < PL_ARRAY_SIZE(bufbits); i++) {
+ if ((bufflags & bufbits[i].flags) == bufbits[i].flags)
+ fmt->caps |= bufbits[i].caps;
+ }
+
+ if (fmt->caps) {
+ fmt->glsl_type = pl_var_glsl_type_name(pl_var_from_fmt(fmt, ""));
+ pl_assert(fmt->glsl_type);
+ }
+
+ struct { VkFormatFeatureFlags flags; enum pl_fmt_caps caps; } bits[] = {
+ {VK_FORMAT_FEATURE_COLOR_ATTACHMENT_BLEND_BIT, PL_FMT_CAP_BLENDABLE},
+ {VK_FORMAT_FEATURE_SAMPLED_IMAGE_FILTER_LINEAR_BIT, PL_FMT_CAP_LINEAR},
+ {VK_FORMAT_FEATURE_SAMPLED_IMAGE_BIT, PL_FMT_CAP_SAMPLEABLE},
+ {VK_FORMAT_FEATURE_STORAGE_IMAGE_BIT, PL_FMT_CAP_STORABLE},
+ {VK_FORMAT_FEATURE_COLOR_ATTACHMENT_BIT, PL_FMT_CAP_RENDERABLE},
+
+ // We don't distinguish between the two blit modes for pl_fmt_caps
+ {VK_FORMAT_FEATURE_BLIT_SRC_BIT | VK_FORMAT_FEATURE_BLIT_DST_BIT,
+ PL_FMT_CAP_BLITTABLE},
+ };
+
+ for (int i = 0; i < PL_ARRAY_SIZE(bits); i++) {
+ if ((texflags & bits[i].flags) == bits[i].flags)
+ fmt->caps |= bits[i].caps;
+ }
+
+ // For blit emulation via compute shaders
+ if (!(fmt->caps & PL_FMT_CAP_BLITTABLE) && (fmt->caps & PL_FMT_CAP_STORABLE)) {
+ fmt->caps |= PL_FMT_CAP_BLITTABLE;
+ fmtp->blit_emulated = true;
+ }
+
+ // This is technically supported for all textures, but the semantics
+ // of pl_gpu require it only be listed for non-opaque ones
+ if (!fmt->opaque)
+ fmt->caps |= PL_FMT_CAP_HOST_READABLE;
+
+ // Vulkan requires a minimum GLSL version that supports textureGather()
+ if (fmt->caps & PL_FMT_CAP_SAMPLEABLE)
+ fmt->gatherable = true;
+
+ // Disable implied capabilities where the dependencies are unavailable
+ enum pl_fmt_caps storable = PL_FMT_CAP_STORABLE | PL_FMT_CAP_TEXEL_STORAGE;
+ if (!(fmt->caps & PL_FMT_CAP_SAMPLEABLE))
+ fmt->caps &= ~PL_FMT_CAP_LINEAR;
+ if (!gpu->glsl.compute)
+ fmt->caps &= ~storable;
+
+ bool has_nofmt = vk->features.features.shaderStorageImageReadWithoutFormat &&
+ vk->features.features.shaderStorageImageWriteWithoutFormat;
+
+ if (fmt->caps & storable) {
+ int real_comps = PL_DEF(vk_fmt->icomps, fmt->num_components);
+ fmt->glsl_format = pl_fmt_glsl_format(fmt, real_comps);
+ if (!fmt->glsl_format && !has_nofmt) {
+ PL_DEBUG(gpu, "Storable format '%s' has no matching GLSL "
+ "format qualifier but read/write without format "
+ "is not supported.. disabling", fmt->name);
+ fmt->caps &= ~storable;
+ }
+ }
+
+ if (fmt->caps & storable)
+ fmt->caps |= PL_FMT_CAP_READWRITE;
+
+ // Pick sub-plane formats for planar formats
+ for (int n = 0; n < fmt->num_planes; n++) {
+ for (int i = 0; i < formats.num; i++) {
+ if (formats.elem[i]->signature == vk_fmt->pfmt[n].fmt) {
+ fmt->planes[n].format = formats.elem[i];
+ fmt->planes[n].shift_x = vk_fmt->pfmt[n].sx;
+ fmt->planes[n].shift_y = vk_fmt->pfmt[n].sy;
+ break;
+ }
+ }
+
+ pl_assert(fmt->planes[n].format);
+ }
+
+ PL_ARRAY_APPEND(gpu, formats, fmt);
+ }
+
+ gpu->formats = formats.elem;
+ gpu->num_formats = formats.num;
+}