From d8bd35e8d6bad02aa174c81a36b976159dfee3c3 Mon Sep 17 00:00:00 2001 From: Stenzek Date: Sat, 29 Jun 2024 12:37:23 +1000 Subject: [PATCH] GPUDevice: Add D24S8/D32F/D32FS8 types --- src/util/d3d_common.cpp | 49 ++++++++++--------- src/util/gpu_texture.cpp | 11 +++-- src/util/gpu_texture.h | 3 ++ src/util/metal_device.mm | 3 ++ src/util/opengl_texture.cpp | 94 ++++++++++++++++++++----------------- src/util/vulkan_device.cpp | 3 ++ 6 files changed, 93 insertions(+), 70 deletions(-) diff --git a/src/util/d3d_common.cpp b/src/util/d3d_common.cpp index 38a8b9cae..b0e2802c8 100644 --- a/src/util/d3d_common.cpp +++ b/src/util/d3d_common.cpp @@ -470,29 +470,32 @@ std::optional> D3DCommon::CompileShader(u32 shader_model, b static constexpr std::array(GPUTexture::Format::MaxCount)> s_format_mapping = {{ // clang-format off - // d3d_format srv_format rtv_format dsv_format - {DXGI_FORMAT_UNKNOWN, DXGI_FORMAT_UNKNOWN, DXGI_FORMAT_UNKNOWN, DXGI_FORMAT_UNKNOWN }, // Unknown - {DXGI_FORMAT_R8G8B8A8_UNORM, DXGI_FORMAT_R8G8B8A8_UNORM, DXGI_FORMAT_R8G8B8A8_UNORM, DXGI_FORMAT_UNKNOWN }, // RGBA8 - {DXGI_FORMAT_B8G8R8A8_UNORM, DXGI_FORMAT_B8G8R8A8_UNORM, DXGI_FORMAT_B8G8R8A8_UNORM, DXGI_FORMAT_UNKNOWN }, // BGRA8 - {DXGI_FORMAT_B5G6R5_UNORM, DXGI_FORMAT_B5G6R5_UNORM, DXGI_FORMAT_B5G6R5_UNORM, DXGI_FORMAT_UNKNOWN }, // RGB565 - {DXGI_FORMAT_B5G5R5A1_UNORM, DXGI_FORMAT_B5G5R5A1_UNORM, DXGI_FORMAT_B5G5R5A1_UNORM, DXGI_FORMAT_UNKNOWN }, // RGBA5551 - {DXGI_FORMAT_R8_UNORM, DXGI_FORMAT_R8_UNORM, DXGI_FORMAT_R8_UNORM, DXGI_FORMAT_UNKNOWN }, // R8 - {DXGI_FORMAT_R16_TYPELESS, DXGI_FORMAT_R16_UNORM, DXGI_FORMAT_UNKNOWN, DXGI_FORMAT_D16_UNORM }, // D16 - {DXGI_FORMAT_R16_UNORM, DXGI_FORMAT_R16_UNORM, DXGI_FORMAT_R16_UNORM, DXGI_FORMAT_UNKNOWN }, // R16 - {DXGI_FORMAT_R16_SINT, DXGI_FORMAT_R16_SINT, DXGI_FORMAT_R16_SINT, DXGI_FORMAT_UNKNOWN }, // R16I - {DXGI_FORMAT_R16_UINT, DXGI_FORMAT_R16_UINT, DXGI_FORMAT_R16_UINT, DXGI_FORMAT_UNKNOWN }, // R16U - {DXGI_FORMAT_R16_FLOAT, DXGI_FORMAT_R16_FLOAT, DXGI_FORMAT_R16_FLOAT, DXGI_FORMAT_UNKNOWN }, // R16F - {DXGI_FORMAT_R32_SINT, DXGI_FORMAT_R32_SINT, DXGI_FORMAT_R32_SINT, DXGI_FORMAT_UNKNOWN }, // R32I - {DXGI_FORMAT_R32_UINT, DXGI_FORMAT_R32_UINT, DXGI_FORMAT_R32_UINT, DXGI_FORMAT_UNKNOWN }, // R32U - {DXGI_FORMAT_R32_FLOAT, DXGI_FORMAT_R32_FLOAT, DXGI_FORMAT_R32_FLOAT, DXGI_FORMAT_UNKNOWN }, // R32F - {DXGI_FORMAT_R8G8_UNORM, DXGI_FORMAT_R8G8_UNORM, DXGI_FORMAT_R8G8_UNORM, DXGI_FORMAT_UNKNOWN }, // RG8 - {DXGI_FORMAT_R16G16_UNORM, DXGI_FORMAT_R16G16_UNORM, DXGI_FORMAT_R16G16_UNORM, DXGI_FORMAT_UNKNOWN }, // RG16 - {DXGI_FORMAT_R16G16_FLOAT, DXGI_FORMAT_R16G16_FLOAT, DXGI_FORMAT_R16G16_FLOAT, DXGI_FORMAT_UNKNOWN }, // RG16F - {DXGI_FORMAT_R32G32_FLOAT, DXGI_FORMAT_R32G32_FLOAT, DXGI_FORMAT_R32G32_FLOAT, DXGI_FORMAT_UNKNOWN }, // RG32F - {DXGI_FORMAT_R16G16B16A16_UNORM, DXGI_FORMAT_R16G16B16A16_UNORM, DXGI_FORMAT_R16G16B16A16_UNORM, DXGI_FORMAT_UNKNOWN }, // RGBA16 - {DXGI_FORMAT_R16G16B16A16_FLOAT, DXGI_FORMAT_R16G16B16A16_FLOAT, DXGI_FORMAT_R16G16B16A16_FLOAT, DXGI_FORMAT_UNKNOWN }, // RGBA16F - {DXGI_FORMAT_R32G32B32A32_FLOAT, DXGI_FORMAT_R32G32B32A32_FLOAT, DXGI_FORMAT_R32G32B32A32_FLOAT, DXGI_FORMAT_UNKNOWN }, // RGBA32F - {DXGI_FORMAT_R10G10B10A2_UNORM, DXGI_FORMAT_R10G10B10A2_UNORM, DXGI_FORMAT_R10G10B10A2_UNORM, DXGI_FORMAT_UNKNOWN }, // RGB10A2 + // d3d_format srv_format rtv_format dsv_format + {DXGI_FORMAT_UNKNOWN, DXGI_FORMAT_UNKNOWN, DXGI_FORMAT_UNKNOWN, DXGI_FORMAT_UNKNOWN }, // Unknown + {DXGI_FORMAT_R8G8B8A8_UNORM, DXGI_FORMAT_R8G8B8A8_UNORM, DXGI_FORMAT_R8G8B8A8_UNORM, DXGI_FORMAT_UNKNOWN }, // RGBA8 + {DXGI_FORMAT_B8G8R8A8_UNORM, DXGI_FORMAT_B8G8R8A8_UNORM, DXGI_FORMAT_B8G8R8A8_UNORM, DXGI_FORMAT_UNKNOWN }, // BGRA8 + {DXGI_FORMAT_B5G6R5_UNORM, DXGI_FORMAT_B5G6R5_UNORM, DXGI_FORMAT_B5G6R5_UNORM, DXGI_FORMAT_UNKNOWN }, // RGB565 + {DXGI_FORMAT_B5G5R5A1_UNORM, DXGI_FORMAT_B5G5R5A1_UNORM, DXGI_FORMAT_B5G5R5A1_UNORM, DXGI_FORMAT_UNKNOWN }, // RGBA5551 + {DXGI_FORMAT_R8_UNORM, DXGI_FORMAT_R8_UNORM, DXGI_FORMAT_R8_UNORM, DXGI_FORMAT_UNKNOWN }, // R8 + {DXGI_FORMAT_R16_TYPELESS, DXGI_FORMAT_R16_UNORM, DXGI_FORMAT_UNKNOWN, DXGI_FORMAT_D16_UNORM }, // D16 + {DXGI_FORMAT_R24G8_TYPELESS, DXGI_FORMAT_R24_UNORM_X8_TYPELESS, DXGI_FORMAT_R24_UNORM_X8_TYPELESS, DXGI_FORMAT_D24_UNORM_S8_UINT }, // D24S8 + {DXGI_FORMAT_R32_TYPELESS, DXGI_FORMAT_R32_FLOAT, DXGI_FORMAT_R32_FLOAT, DXGI_FORMAT_D32_FLOAT }, // D32F + {DXGI_FORMAT_R32G8X24_TYPELESS, DXGI_FORMAT_R32_FLOAT_X8X24_TYPELESS,DXGI_FORMAT_R32_FLOAT_X8X24_TYPELESS, DXGI_FORMAT_D32_FLOAT_S8X24_UINT }, // D32FS8 + {DXGI_FORMAT_R16_UNORM, DXGI_FORMAT_R16_UNORM, DXGI_FORMAT_R16_UNORM, DXGI_FORMAT_UNKNOWN }, // R16 + {DXGI_FORMAT_R16_SINT, DXGI_FORMAT_R16_SINT, DXGI_FORMAT_R16_SINT, DXGI_FORMAT_UNKNOWN }, // R16I + {DXGI_FORMAT_R16_UINT, DXGI_FORMAT_R16_UINT, DXGI_FORMAT_R16_UINT, DXGI_FORMAT_UNKNOWN }, // R16U + {DXGI_FORMAT_R16_FLOAT, DXGI_FORMAT_R16_FLOAT, DXGI_FORMAT_R16_FLOAT, DXGI_FORMAT_UNKNOWN }, // R16F + {DXGI_FORMAT_R32_SINT, DXGI_FORMAT_R32_SINT, DXGI_FORMAT_R32_SINT, DXGI_FORMAT_UNKNOWN }, // R32I + {DXGI_FORMAT_R32_UINT, DXGI_FORMAT_R32_UINT, DXGI_FORMAT_R32_UINT, DXGI_FORMAT_UNKNOWN }, // R32U + {DXGI_FORMAT_R32_FLOAT, DXGI_FORMAT_R32_FLOAT, DXGI_FORMAT_R32_FLOAT, DXGI_FORMAT_UNKNOWN }, // R32F + {DXGI_FORMAT_R8G8_UNORM, DXGI_FORMAT_R8G8_UNORM, DXGI_FORMAT_R8G8_UNORM, DXGI_FORMAT_UNKNOWN }, // RG8 + {DXGI_FORMAT_R16G16_UNORM, DXGI_FORMAT_R16G16_UNORM, DXGI_FORMAT_R16G16_UNORM, DXGI_FORMAT_UNKNOWN }, // RG16 + {DXGI_FORMAT_R16G16_FLOAT, DXGI_FORMAT_R16G16_FLOAT, DXGI_FORMAT_R16G16_FLOAT, DXGI_FORMAT_UNKNOWN }, // RG16F + {DXGI_FORMAT_R32G32_FLOAT, DXGI_FORMAT_R32G32_FLOAT, DXGI_FORMAT_R32G32_FLOAT, DXGI_FORMAT_UNKNOWN }, // RG32F + {DXGI_FORMAT_R16G16B16A16_UNORM, DXGI_FORMAT_R16G16B16A16_UNORM, DXGI_FORMAT_R16G16B16A16_UNORM, DXGI_FORMAT_UNKNOWN }, // RGBA16 + {DXGI_FORMAT_R16G16B16A16_FLOAT, DXGI_FORMAT_R16G16B16A16_FLOAT, DXGI_FORMAT_R16G16B16A16_FLOAT, DXGI_FORMAT_UNKNOWN }, // RGBA16F + {DXGI_FORMAT_R32G32B32A32_FLOAT, DXGI_FORMAT_R32G32B32A32_FLOAT, DXGI_FORMAT_R32G32B32A32_FLOAT, DXGI_FORMAT_UNKNOWN }, // RGBA32F + {DXGI_FORMAT_R10G10B10A2_UNORM, DXGI_FORMAT_R10G10B10A2_UNORM, DXGI_FORMAT_R10G10B10A2_UNORM, DXGI_FORMAT_UNKNOWN }, // RGB10A2 // clang-format on }}; diff --git a/src/util/gpu_texture.cpp b/src/util/gpu_texture.cpp index da8e8588f..52897c8e5 100644 --- a/src/util/gpu_texture.cpp +++ b/src/util/gpu_texture.cpp @@ -33,6 +33,9 @@ const char* GPUTexture::GetFormatName(Format format) "RGB5551", // RGBA5551 "R8", // R8 "D16", // D16 + "D24S8", // D24S8 + "D32F", // D32F + "D32FS8S", // D32FS8 "R16", // R16 "R16I", // R16I "R16U", // R16U @@ -149,6 +152,9 @@ u32 GPUTexture::GetPixelSize(GPUTexture::Format format) 2, // RGBA5551 1, // R8 2, // D16 + 4, // D24S8 + 4, // D32F + 8, // D32FS8 2, // R16 2, // R16I 2, // R16U @@ -171,13 +177,12 @@ u32 GPUTexture::GetPixelSize(GPUTexture::Format format) bool GPUTexture::IsDepthFormat(Format format) { - return (format == Format::D16); + return (format >= Format::D16 && format <= Format::D32FS8); } bool GPUTexture::IsDepthStencilFormat(Format format) { - // None needed yet. - return false; + return (format == Format::D24S8 || format == Format::D32FS8); } bool GPUTexture::IsCompressedFormat(Format format) diff --git a/src/util/gpu_texture.h b/src/util/gpu_texture.h index 496be5686..0e018fa94 100644 --- a/src/util/gpu_texture.h +++ b/src/util/gpu_texture.h @@ -41,6 +41,9 @@ public: RGBA5551, R8, D16, + D24S8, + D32F, + D32FS8, R16, R16I, R16U, diff --git a/src/util/metal_device.mm b/src/util/metal_device.mm index f3ddc3a01..e3db52163 100644 --- a/src/util/metal_device.mm +++ b/src/util/metal_device.mm @@ -39,6 +39,9 @@ static constexpr std::array(GPUTexture::Format: MTLPixelFormatA1BGR5Unorm, // RGBA5551 MTLPixelFormatR8Unorm, // R8 MTLPixelFormatDepth16Unorm, // D16 + MTLPixelFormatDepth24Unorm_Stencil8, // D24S8 + MTLPixelFormatDepth32Float, // D32F + MTLPixelFormatDepth32Float_Stencil8, // D32FS8 MTLPixelFormatR16Unorm, // R16 MTLPixelFormatR16Sint, // R16I MTLPixelFormatR16Uint, // R16U diff --git a/src/util/opengl_texture.cpp b/src/util/opengl_texture.cpp index a0f4401e1..3ef3ca974 100644 --- a/src/util/opengl_texture.cpp +++ b/src/util/opengl_texture.cpp @@ -29,55 +29,61 @@ const std::tuple& OpenGLTexture::GetPixelFormatMapping(G { static constexpr std::array, static_cast(GPUTexture::Format::MaxCount)> mapping = {{ - {}, // Unknown - {GL_RGBA8, GL_RGBA, GL_UNSIGNED_BYTE}, // RGBA8 - {GL_RGBA8, GL_BGRA, GL_UNSIGNED_BYTE}, // BGRA8 - {GL_RGB565, GL_RGB, GL_UNSIGNED_SHORT_5_6_5}, // RGB565 - {GL_RGB5_A1, GL_BGRA, GL_UNSIGNED_SHORT_1_5_5_5_REV}, // RGBA5551 - {GL_R8, GL_RED, GL_UNSIGNED_BYTE}, // R8 - {GL_DEPTH_COMPONENT16, GL_DEPTH_COMPONENT, GL_SHORT}, // D16 - {GL_R16, GL_RED, GL_UNSIGNED_SHORT}, // R16 - {GL_R16I, GL_RED_INTEGER, GL_SHORT}, // R16I - {GL_R16UI, GL_RED_INTEGER, GL_UNSIGNED_SHORT}, // R16U - {GL_R16F, GL_RED, GL_HALF_FLOAT}, // R16F - {GL_R32I, GL_RED_INTEGER, GL_INT}, // R32I - {GL_R32UI, GL_RED_INTEGER, GL_UNSIGNED_INT}, // R32U - {GL_R32F, GL_RED, GL_FLOAT}, // R32F - {GL_RG8, GL_RG_INTEGER, GL_UNSIGNED_BYTE}, // RG8 - {GL_RG16F, GL_RG, GL_UNSIGNED_SHORT}, // RG16 - {GL_RG16F, GL_RG, GL_HALF_FLOAT}, // RG16F - {GL_RG32F, GL_RG, GL_FLOAT}, // RG32F - {GL_RGBA16, GL_RGBA, GL_UNSIGNED_BYTE}, // RGBA16 - {GL_RGBA16F, GL_RGBA, GL_HALF_FLOAT}, // RGBA16F - {GL_RGBA32F, GL_RGBA, GL_FLOAT}, // RGBA32F - {GL_RGB10_A2, GL_BGRA, GL_UNSIGNED_INT_2_10_10_10_REV}, // RGB10A2 + {}, // Unknown + {GL_RGBA8, GL_RGBA, GL_UNSIGNED_BYTE}, // RGBA8 + {GL_RGBA8, GL_BGRA, GL_UNSIGNED_BYTE}, // BGRA8 + {GL_RGB565, GL_RGB, GL_UNSIGNED_SHORT_5_6_5}, // RGB565 + {GL_RGB5_A1, GL_BGRA, GL_UNSIGNED_SHORT_1_5_5_5_REV}, // RGBA5551 + {GL_R8, GL_RED, GL_UNSIGNED_BYTE}, // R8 + {GL_DEPTH_COMPONENT16, GL_DEPTH_COMPONENT, GL_SHORT}, // D16 + {GL_DEPTH24_STENCIL8, GL_DEPTH_STENCIL, GL_UNSIGNED_INT}, // D24S8 + {GL_DEPTH_COMPONENT32F, GL_DEPTH_COMPONENT, GL_FLOAT}, // D32F + {GL_DEPTH32F_STENCIL8, GL_DEPTH_STENCIL, GL_FLOAT}, // D32FS8 + {GL_R16, GL_RED, GL_UNSIGNED_SHORT}, // R16 + {GL_R16I, GL_RED_INTEGER, GL_SHORT}, // R16I + {GL_R16UI, GL_RED_INTEGER, GL_UNSIGNED_SHORT}, // R16U + {GL_R16F, GL_RED, GL_HALF_FLOAT}, // R16F + {GL_R32I, GL_RED_INTEGER, GL_INT}, // R32I + {GL_R32UI, GL_RED_INTEGER, GL_UNSIGNED_INT}, // R32U + {GL_R32F, GL_RED, GL_FLOAT}, // R32F + {GL_RG8, GL_RG_INTEGER, GL_UNSIGNED_BYTE}, // RG8 + {GL_RG16F, GL_RG, GL_UNSIGNED_SHORT}, // RG16 + {GL_RG16F, GL_RG, GL_HALF_FLOAT}, // RG16F + {GL_RG32F, GL_RG, GL_FLOAT}, // RG32F + {GL_RGBA16, GL_RGBA, GL_UNSIGNED_BYTE}, // RGBA16 + {GL_RGBA16F, GL_RGBA, GL_HALF_FLOAT}, // RGBA16F + {GL_RGBA32F, GL_RGBA, GL_FLOAT}, // RGBA32F + {GL_RGB10_A2, GL_BGRA, GL_UNSIGNED_INT_2_10_10_10_REV}, // RGB10A2 }}; // GLES doesn't have the non-normalized 16-bit formats.. use float and hope for the best, lol. static constexpr std::array, static_cast(GPUTexture::Format::MaxCount)> mapping_gles = {{ - {}, // Unknown - {GL_RGBA8, GL_RGBA, GL_UNSIGNED_BYTE}, // RGBA8 - {GL_RGBA8, GL_BGRA, GL_UNSIGNED_BYTE}, // BGRA8 - {GL_RGB565, GL_RGB, GL_UNSIGNED_SHORT_5_6_5}, // RGB565 - {GL_RGB5_A1, GL_BGRA, GL_UNSIGNED_SHORT_1_5_5_5_REV}, // RGBA5551 - {GL_R8, GL_RED, GL_UNSIGNED_BYTE}, // R8 - {GL_DEPTH_COMPONENT16, GL_DEPTH_COMPONENT, GL_SHORT}, // D16 - {GL_R16F, GL_RED, GL_HALF_FLOAT}, // R16 - {GL_R16I, GL_RED_INTEGER, GL_SHORT}, // R16I - {GL_R16UI, GL_RED_INTEGER, GL_UNSIGNED_SHORT}, // R16U - {GL_R16F, GL_RED, GL_HALF_FLOAT}, // R16F - {GL_R32I, GL_RED_INTEGER, GL_INT}, // R32I - {GL_R32UI, GL_RED_INTEGER, GL_UNSIGNED_INT}, // R32U - {GL_R32F, GL_RED, GL_FLOAT}, // R32F - {GL_RG8, GL_RG, GL_UNSIGNED_BYTE}, // RG8 - {GL_RG16F, GL_RG, GL_HALF_FLOAT}, // RG16 - {GL_RG16F, GL_RG, GL_HALF_FLOAT}, // RG16F - {GL_RG32F, GL_RG, GL_FLOAT}, // RG32F - {GL_RGBA16F, GL_RGBA, GL_HALF_FLOAT}, // RGBA16 - {GL_RGBA16F, GL_RGBA, GL_HALF_FLOAT}, // RGBA16F - {GL_RGBA32F, GL_RGBA, GL_FLOAT}, // RGBA32F - {GL_RGB10_A2, GL_BGRA, GL_UNSIGNED_INT_2_10_10_10_REV}, // RGB10A2 + {}, // Unknown + {GL_RGBA8, GL_RGBA, GL_UNSIGNED_BYTE}, // RGBA8 + {GL_RGBA8, GL_BGRA, GL_UNSIGNED_BYTE}, // BGRA8 + {GL_RGB565, GL_RGB, GL_UNSIGNED_SHORT_5_6_5}, // RGB565 + {GL_RGB5_A1, GL_BGRA, GL_UNSIGNED_SHORT_1_5_5_5_REV}, // RGBA5551 + {GL_R8, GL_RED, GL_UNSIGNED_BYTE}, // R8 + {GL_DEPTH_COMPONENT16, GL_DEPTH_COMPONENT, GL_SHORT}, // D16 + {GL_DEPTH24_STENCIL8, GL_DEPTH_STENCIL, GL_UNSIGNED_INT}, // D24S8 + {GL_DEPTH_COMPONENT32F, GL_DEPTH_COMPONENT, GL_FLOAT}, // D32F + {GL_DEPTH32F_STENCIL8, GL_DEPTH_STENCIL, GL_FLOAT}, // D32FS8 + {GL_R16F, GL_RED, GL_HALF_FLOAT}, // R16 + {GL_R16I, GL_RED_INTEGER, GL_SHORT}, // R16I + {GL_R16UI, GL_RED_INTEGER, GL_UNSIGNED_SHORT}, // R16U + {GL_R16F, GL_RED, GL_HALF_FLOAT}, // R16F + {GL_R32I, GL_RED_INTEGER, GL_INT}, // R32I + {GL_R32UI, GL_RED_INTEGER, GL_UNSIGNED_INT}, // R32U + {GL_R32F, GL_RED, GL_FLOAT}, // R32F + {GL_RG8, GL_RG, GL_UNSIGNED_BYTE}, // RG8 + {GL_RG16F, GL_RG, GL_HALF_FLOAT}, // RG16 + {GL_RG16F, GL_RG, GL_HALF_FLOAT}, // RG16F + {GL_RG32F, GL_RG, GL_FLOAT}, // RG32F + {GL_RGBA16F, GL_RGBA, GL_HALF_FLOAT}, // RGBA16 + {GL_RGBA16F, GL_RGBA, GL_HALF_FLOAT}, // RGBA16F + {GL_RGBA32F, GL_RGBA, GL_FLOAT}, // RGBA32F + {GL_RGB10_A2, GL_BGRA, GL_UNSIGNED_INT_2_10_10_10_REV}, // RGB10A2 }}; return gles ? mapping_gles[static_cast(format)] : mapping[static_cast(format)]; diff --git a/src/util/vulkan_device.cpp b/src/util/vulkan_device.cpp index ade9f27b3..0bdb7da64 100644 --- a/src/util/vulkan_device.cpp +++ b/src/util/vulkan_device.cpp @@ -78,6 +78,9 @@ const std::array(GPUTexture::Format::MaxCount)> Vulka VK_FORMAT_R5G5B5A1_UNORM_PACK16, // RGBA5551 VK_FORMAT_R8_UNORM, // R8 VK_FORMAT_D16_UNORM, // D16 + VK_FORMAT_D24_UNORM_S8_UINT, // D24S8 + VK_FORMAT_D32_SFLOAT, // D32F + VK_FORMAT_D32_SFLOAT_S8_UINT, // D32FS8 VK_FORMAT_R16_UNORM, // R16 VK_FORMAT_R16_SINT, // R16I VK_FORMAT_R16_UINT, // R16U