GPU: Add YUVA NV12A multi-plane format support
If SkiaGraphite is enabled, HEVC with Alpha fails to playback on macOS,
to support HEVC with Alpha decoding on macOS, we need to introduce a new
plane and a new multi-plane format NV12A (R8BG8A8Triplanar420Unorm) for
dawn, note that currently this format is only supported on macOS.
Bug: chromium:1490807
Bug: chromium:1175525
Change-Id: Iae9215c30c6ec3cae11e8d80efc52754c1fcd7f8
Reviewed-on: https://dawn-review.googlesource.com/c/dawn/+/159300
Kokoro: Kokoro <noreply+kokoro@google.com>
Commit-Queue: 朱思达 <zhusida@bytedance.com>
Reviewed-by: Austin Eng <enga@chromium.org>
Reviewed-by: Corentin Wallez <cwallez@chromium.org>
diff --git a/dawn.json b/dawn.json
index 5801342..bff7679 100644
--- a/dawn.json
+++ b/dawn.json
@@ -1918,6 +1918,7 @@
{"value": 1021, "name": "multi planar format p010", "tags": ["dawn"]},
{"value": 1022, "name": "host mapped pointer", "tags": ["dawn"]},
{"value": 1023, "name": "multi planar render targets", "tags": ["dawn"]},
+ {"value": 1024, "name": "multi planar format nv12a", "tags": ["dawn"]},
{"value": 1100, "name": "shared texture memory vk dedicated allocation", "tags": ["dawn", "native"]},
{"value": 1101, "name": "shared texture memory a hardware buffer", "tags": ["dawn", "native"]},
@@ -3385,7 +3386,8 @@
{"value": 1, "name": "stencil only"},
{"value": 2, "name": "depth only"},
{"value": 3, "name": "plane 0 only", "tags": ["dawn"]},
- {"value": 4, "name": "plane 1 only", "tags": ["dawn"]}
+ {"value": 4, "name": "plane 1 only", "tags": ["dawn"]},
+ {"value": 5, "name": "plane 2 only", "tags": ["dawn"]}
]
},
"texture data layout": {
@@ -3545,7 +3547,8 @@
{"value": 101, "name": "RGBA16 snorm", "tags": ["dawn"]},
{"value": 102, "name": "R8 BG8 Biplanar 420 unorm", "tags": ["dawn"]},
- {"value": 103, "name": "R10X6 BG10X6 Biplanar 420 unorm", "tags": ["dawn"]}
+ {"value": 103, "name": "R10X6 BG10X6 Biplanar 420 unorm", "tags": ["dawn"]},
+ {"value": 104, "name": "R8 BG8 A8 Triplanar 420 unorm", "tags": ["dawn"]}
]
},
"texture usage": {
diff --git a/docs/dawn/features/multi_planar_format_nv12a.md b/docs/dawn/features/multi_planar_format_nv12a.md
new file mode 100644
index 0000000..35c4e7e
--- /dev/null
+++ b/docs/dawn/features/multi_planar_format_nv12a.md
@@ -0,0 +1,6 @@
+# Multi Planar Format NV12A (Experimental!)
+
+The `multi-planar-format-nv12a` feature allows rendering NV12 format with an extra alpha plane. A typical usage scenario of this feature is HEVC with Alpha video decoding feature on Chrome for Mac. macOS will directly decode the buffer into such format without extra YUVA to RGBA conversion, which turns out has better power efficiency.
+
+Notes:
+ - The format is only available on macOS >= 10.15.
diff --git a/src/dawn/common/IOSurfaceUtils.cpp b/src/dawn/common/IOSurfaceUtils.cpp
index f8cb1f0..a76d5f1 100644
--- a/src/dawn/common/IOSurfaceUtils.cpp
+++ b/src/dawn/common/IOSurfaceUtils.cpp
@@ -48,6 +48,8 @@
return kCVPixelFormatType_420YpCbCr8BiPlanarVideoRange;
case wgpu::TextureFormat::R10X6BG10X6Biplanar420Unorm:
return kCVPixelFormatType_420YpCbCr10BiPlanarVideoRange;
+ case wgpu::TextureFormat::R8BG8A8Triplanar420Unorm:
+ return kCVPixelFormatType_420YpCbCr8VideoRange_8A_TriPlanar;
default:
DAWN_UNREACHABLE();
return 0;
@@ -59,6 +61,8 @@
case wgpu::TextureFormat::R8BG8Biplanar420Unorm:
case wgpu::TextureFormat::R10X6BG10X6Biplanar420Unorm:
return 2;
+ case wgpu::TextureFormat::R8BG8A8Triplanar420Unorm:
+ return 3;
default:
DAWN_UNREACHABLE();
return 1;
@@ -69,7 +73,8 @@
switch (format) {
case wgpu::TextureFormat::R8BG8Biplanar420Unorm:
case wgpu::TextureFormat::R10X6BG10X6Biplanar420Unorm:
- return plane == 0 ? 1 : 2;
+ case wgpu::TextureFormat::R8BG8A8Triplanar420Unorm:
+ return plane == 1 ? 2 : 1;
default:
DAWN_UNREACHABLE();
return 0;
@@ -79,9 +84,10 @@
size_t BytesPerElement(wgpu::TextureFormat format, size_t plane) {
switch (format) {
case wgpu::TextureFormat::R8BG8Biplanar420Unorm:
- return plane == 0 ? 1 : 2;
+ case wgpu::TextureFormat::R8BG8A8Triplanar420Unorm:
+ return plane == 1 ? 2 : 1;
case wgpu::TextureFormat::R10X6BG10X6Biplanar420Unorm:
- return plane == 0 ? 2 : 4;
+ return plane == 1 ? 4 : 2;
default:
DAWN_UNREACHABLE();
return 0;
diff --git a/src/dawn/native/CommandEncoder.cpp b/src/dawn/native/CommandEncoder.cpp
index 41cf372..128bbea 100644
--- a/src/dawn/native/CommandEncoder.cpp
+++ b/src/dawn/native/CommandEncoder.cpp
@@ -346,9 +346,9 @@
DAWN_TRY(ValidateCanUseAs(attachment->GetTexture(), wgpu::TextureUsage::RenderAttachment,
usageValidationMode));
- // Plane0 and Plane1 aspects for multiplanar texture views should be allowed as color
+ // Plane0, Plane1, and Plane2 aspects for multiplanar texture views should be allowed as color
// attachments.
- Aspect kRenderableAspects = Aspect::Color | Aspect::Plane0 | Aspect::Plane1;
+ Aspect kRenderableAspects = Aspect::Color | Aspect::Plane0 | Aspect::Plane1 | Aspect::Plane2;
DAWN_INVALID_IF(
!(attachment->GetAspects() & kRenderableAspects) || !attachment->GetFormat().isRenderable,
"The color attachment %s format (%s) is not color renderable.", attachment,
diff --git a/src/dawn/native/CommandValidation.cpp b/src/dawn/native/CommandValidation.cpp
index 1646b1e..6c32c09 100644
--- a/src/dawn/native/CommandValidation.cpp
+++ b/src/dawn/native/CommandValidation.cpp
@@ -457,6 +457,8 @@
return Aspect::Plane0;
case wgpu::TextureAspect::Plane1Only:
return Aspect::Plane1;
+ case wgpu::TextureAspect::Plane2Only:
+ return Aspect::Plane2;
}
DAWN_UNREACHABLE();
}
diff --git a/src/dawn/native/Features.cpp b/src/dawn/native/Features.cpp
index 664cecc..b7a9cf3 100644
--- a/src/dawn/native/Features.cpp
+++ b/src/dawn/native/Features.cpp
@@ -126,6 +126,11 @@
{Feature::MultiPlanarFormatP010,
{"Import and use the P010 multi-planar texture format with per plane views",
"https://bugs.chromium.org/p/dawn/issues/detail?id=551", FeatureInfo::FeatureState::Stable}},
+ {Feature::MultiPlanarFormatNv12a,
+ {"Import and use the NV12A multi-planar texture format with per plane views",
+ "https://dawn.googlesource.com/dawn/+/refs/heads/main/docs/dawn/features/"
+ "multi_planar_format_nv12a.md",
+ FeatureInfo::FeatureState::Experimental}},
{Feature::MultiPlanarRenderTargets,
{"Import and use multi-planar texture formats as render attachments",
"https://bugs.chromium.org/p/dawn/issues/detail?id=1337", FeatureInfo::FeatureState::Stable}},
diff --git a/src/dawn/native/Format.cpp b/src/dawn/native/Format.cpp
index 6da03b5..8b37341 100644
--- a/src/dawn/native/Format.cpp
+++ b/src/dawn/native/Format.cpp
@@ -128,7 +128,7 @@
}
bool Format::IsMultiPlanar() const {
- return aspects & (Aspect::Plane0 | Aspect::Plane1);
+ return aspects & (Aspect::Plane0 | Aspect::Plane1 | Aspect::Plane2);
}
bool Format::CopyCompatibleWith(const Format& otherFormat) const {
@@ -385,12 +385,13 @@
AddFormat(internalFormat);
};
- auto AddMultiAspectFormat = [&AddFormat, &table](wgpu::TextureFormat format, Aspect aspects,
- wgpu::TextureFormat firstFormat,
- wgpu::TextureFormat secondFormat,
- Cap capabilites,
- UnsupportedReason unsupportedReason,
- ComponentCount componentCount) {
+ auto AddMultiAspectFormat = [&AddFormat, &table](
+ wgpu::TextureFormat format, Aspect aspects, Cap capabilites,
+ UnsupportedReason unsupportedReason,
+ ComponentCount componentCount, wgpu::TextureFormat firstFormat,
+ wgpu::TextureFormat secondFormat,
+ wgpu::TextureFormat thirdFormat =
+ wgpu::TextureFormat::Undefined) {
Format internalFormat;
internalFormat.format = format;
internalFormat.baseFormat = format;
@@ -415,6 +416,10 @@
internalFormat.aspectInfo[0] = table[firstFormatIndex].aspectInfo[0];
internalFormat.aspectInfo[1] = table[secondFormatIndex].aspectInfo[0];
+ if (thirdFormat != wgpu::TextureFormat::Undefined) {
+ const FormatIndex thirdFormatIndex = ComputeFormatIndex(thirdFormat);
+ internalFormat.aspectInfo[2] = table[thirdFormatIndex].aspectInfo[0];
+ }
AddFormat(internalFormat);
};
@@ -492,11 +497,11 @@
// using 0 here to mean "unsized" and adding a backend-specific query for the block size.
AddDepthFormat(wgpu::TextureFormat::Depth24Plus, 4, Format::supported);
AddMultiAspectFormat(wgpu::TextureFormat::Depth24PlusStencil8,
- Aspect::Depth | Aspect::Stencil, wgpu::TextureFormat::Depth24Plus, wgpu::TextureFormat::Stencil8, Cap::Renderable | Cap::Multisample, Format::supported, ComponentCount(2));
+ Aspect::Depth | Aspect::Stencil, Cap::Renderable | Cap::Multisample, Format::supported, ComponentCount(2), wgpu::TextureFormat::Depth24Plus, wgpu::TextureFormat::Stencil8);
AddDepthFormat(wgpu::TextureFormat::Depth32Float, 4, Format::supported);
UnsupportedReason d32s8UnsupportedReason = device->HasFeature(Feature::Depth32FloatStencil8) ? Format::supported : RequiresFeature{wgpu::FeatureName::Depth32FloatStencil8};
AddMultiAspectFormat(wgpu::TextureFormat::Depth32FloatStencil8,
- Aspect::Depth | Aspect::Stencil, wgpu::TextureFormat::Depth32Float, wgpu::TextureFormat::Stencil8, Cap::Renderable | Cap::Multisample, d32s8UnsupportedReason, ComponentCount(2));
+ Aspect::Depth | Aspect::Stencil, Cap::Renderable | Cap::Multisample, d32s8UnsupportedReason, ComponentCount(2), wgpu::TextureFormat::Depth32Float, wgpu::TextureFormat::Stencil8);
// BC compressed formats
UnsupportedReason bcFormatUnsupportedReason = device->HasFeature(Feature::TextureCompressionBC) ? Format::supported : RequiresFeature{wgpu::FeatureName::TextureCompressionBC};
@@ -563,11 +568,13 @@
const UnsupportedReason multiPlanarFormatUnsupportedReason = device->HasFeature(Feature::DawnMultiPlanarFormats) ? Format::supported : RequiresFeature{wgpu::FeatureName::DawnMultiPlanarFormats};
auto multiPlanarCapabilities = device->HasFeature(Feature::MultiPlanarRenderTargets) ? Cap::Renderable : Cap::None;
AddMultiAspectFormat(wgpu::TextureFormat::R8BG8Biplanar420Unorm, Aspect::Plane0 | Aspect::Plane1,
- wgpu::TextureFormat::R8Unorm, wgpu::TextureFormat::RG8Unorm, multiPlanarCapabilities, multiPlanarFormatUnsupportedReason, ComponentCount(3));
+ multiPlanarCapabilities, multiPlanarFormatUnsupportedReason, ComponentCount(3), wgpu::TextureFormat::R8Unorm, wgpu::TextureFormat::RG8Unorm);
const UnsupportedReason multiPlanarFormatP010UnsupportedReason = device->HasFeature(Feature::MultiPlanarFormatP010) ? Format::supported : RequiresFeature{wgpu::FeatureName::MultiPlanarFormatP010};
AddMultiAspectFormat(wgpu::TextureFormat::R10X6BG10X6Biplanar420Unorm, Aspect::Plane0 | Aspect::Plane1,
- wgpu::TextureFormat::R16Unorm, wgpu::TextureFormat::RG16Unorm, multiPlanarCapabilities, multiPlanarFormatP010UnsupportedReason, ComponentCount(3));
-
+ multiPlanarCapabilities, multiPlanarFormatP010UnsupportedReason, ComponentCount(3), wgpu::TextureFormat::R16Unorm, wgpu::TextureFormat::RG16Unorm);
+ const UnsupportedReason multiPlanarFormatNv12aUnsupportedReason = device->HasFeature(Feature::MultiPlanarFormatNv12a) ? Format::supported : RequiresFeature{wgpu::FeatureName::MultiPlanarFormatNv12a};
+ AddMultiAspectFormat(wgpu::TextureFormat::R8BG8A8Triplanar420Unorm, Aspect::Plane0 | Aspect::Plane1 | Aspect::Plane2,
+ multiPlanarCapabilities, multiPlanarFormatNv12aUnsupportedReason, ComponentCount(4), wgpu::TextureFormat::R8Unorm, wgpu::TextureFormat::RG8Unorm, wgpu::TextureFormat::R8Unorm);
// clang-format on
// This checks that each format is set at least once, the second part of checking that all
diff --git a/src/dawn/native/Format.h b/src/dawn/native/Format.h
index 4481d9c..5192910 100644
--- a/src/dawn/native/Format.h
+++ b/src/dawn/native/Format.h
@@ -106,7 +106,7 @@
// The number of formats Dawn knows about. Asserts in BuildFormatTable ensure that this is the
// exact number of known format.
-static constexpr uint32_t kKnownFormatCount = 103;
+static constexpr uint32_t kKnownFormatCount = 104;
using FormatIndex = TypedInteger<struct FormatIndexT, uint32_t>;
diff --git a/src/dawn/native/Subresource.cpp b/src/dawn/native/Subresource.cpp
index 8ce4ca8..0937a85 100644
--- a/src/dawn/native/Subresource.cpp
+++ b/src/dawn/native/Subresource.cpp
@@ -53,6 +53,8 @@
return Aspect::Plane0;
case wgpu::TextureAspect::Plane1Only:
return Aspect::Plane1;
+ case wgpu::TextureAspect::Plane2Only:
+ return Aspect::Plane2;
default:
break;
}
@@ -69,6 +71,9 @@
case 1:
textureAspect = wgpu::TextureAspect::Plane1Only;
break;
+ case 2:
+ textureAspect = wgpu::TextureAspect::Plane2Only;
+ break;
default:
DAWN_UNREACHABLE();
}
@@ -88,6 +93,8 @@
return format.aspects & Aspect::Plane0;
case wgpu::TextureAspect::Plane1Only:
return format.aspects & Aspect::Plane1;
+ case wgpu::TextureAspect::Plane2Only:
+ return format.aspects & Aspect::Plane2;
}
DAWN_UNREACHABLE();
}
@@ -103,6 +110,8 @@
case Aspect::Plane1:
case Aspect::Stencil:
return 1;
+ case Aspect::Plane2:
+ return 2;
default:
DAWN_UNREACHABLE();
}
diff --git a/src/dawn/native/Subresource.h b/src/dawn/native/Subresource.h
index 41f0a77..2dea349 100644
--- a/src/dawn/native/Subresource.h
+++ b/src/dawn/native/Subresource.h
@@ -45,15 +45,16 @@
// Aspects used to select individual planes in a multi-planar format.
Plane0 = 0x8,
Plane1 = 0x10,
+ Plane2 = 0x20,
// An aspect for that represents the combination of both the depth and stencil aspects. It
// can be ignored outside of the Vulkan backend.
- CombinedDepthStencil = 0x20,
+ CombinedDepthStencil = 0x40,
};
template <>
struct EnumBitmaskSize<Aspect> {
- static constexpr unsigned value = 6;
+ static constexpr unsigned value = 7;
};
// Convert the TextureAspect to an Aspect mask for the format. ASSERTs if the aspect
diff --git a/src/dawn/native/SubresourceStorage.h b/src/dawn/native/SubresourceStorage.h
index 6031659..34ad25c 100644
--- a/src/dawn/native/SubresourceStorage.h
+++ b/src/dawn/native/SubresourceStorage.h
@@ -230,7 +230,7 @@
// Invariant: if an aspect is marked compressed, then all it's layers are marked as
// compressed.
- static constexpr size_t kMaxAspects = 2;
+ static constexpr size_t kMaxAspects = 3;
std::array<bool, kMaxAspects> mAspectCompressed;
std::array<T, kMaxAspects> mInlineAspectData;
diff --git a/src/dawn/native/Texture.cpp b/src/dawn/native/Texture.cpp
index 36408a9..3f8f221 100644
--- a/src/dawn/native/Texture.cpp
+++ b/src/dawn/native/Texture.cpp
@@ -836,6 +836,7 @@
case Aspect::CombinedDepthStencil:
return mBaseSize;
case Aspect::Plane0:
+ case Aspect::Plane2:
DAWN_ASSERT(GetFormat().IsMultiPlanar());
return mBaseSize;
case Aspect::Plane1: {
@@ -844,6 +845,7 @@
switch (GetFormat().format) {
case wgpu::TextureFormat::R8BG8Biplanar420Unorm:
case wgpu::TextureFormat::R10X6BG10X6Biplanar420Unorm:
+ case wgpu::TextureFormat::R8BG8A8Triplanar420Unorm:
if (planeSize.width > 1) {
planeSize.width >>= 1;
}
diff --git a/src/dawn/native/d3d/UtilsD3D.cpp b/src/dawn/native/d3d/UtilsD3D.cpp
index 32b5e4d..d745560 100644
--- a/src/dawn/native/d3d/UtilsD3D.cpp
+++ b/src/dawn/native/d3d/UtilsD3D.cpp
@@ -250,6 +250,7 @@
case wgpu::TextureFormat::R8BG8Biplanar420Unorm:
case wgpu::TextureFormat::R10X6BG10X6Biplanar420Unorm:
+ case wgpu::TextureFormat::R8BG8A8Triplanar420Unorm:
case wgpu::TextureFormat::Undefined:
DAWN_UNREACHABLE();
}
@@ -395,6 +396,7 @@
case wgpu::TextureFormat::ASTC12x10UnormSrgb:
case wgpu::TextureFormat::ASTC12x12Unorm:
case wgpu::TextureFormat::ASTC12x12UnormSrgb:
+ case wgpu::TextureFormat::R8BG8A8Triplanar420Unorm:
case wgpu::TextureFormat::Undefined:
DAWN_UNREACHABLE();
diff --git a/src/dawn/native/metal/BackendMTL.mm b/src/dawn/native/metal/BackendMTL.mm
index f75cf11..5d73162 100644
--- a/src/dawn/native/metal/BackendMTL.mm
+++ b/src/dawn/native/metal/BackendMTL.mm
@@ -555,6 +555,10 @@
EnableFeature(Feature::MultiPlanarFormatExtendedUsages);
}
+ if (@available(macOS 10.15, iOS 13.0, *)) {
+ EnableFeature(Feature::MultiPlanarFormatNv12a);
+ }
+
if (@available(macOS 11.0, iOS 10.0, *)) {
// Memoryless storage mode for Metal textures is available only
// from the Apple2 family of GPUs on.
diff --git a/src/dawn/native/metal/SharedTextureMemoryMTL.mm b/src/dawn/native/metal/SharedTextureMemoryMTL.mm
index ef60322..6860dd0 100644
--- a/src/dawn/native/metal/SharedTextureMemoryMTL.mm
+++ b/src/dawn/native/metal/SharedTextureMemoryMTL.mm
@@ -62,7 +62,10 @@
return wgpu::TextureFormat::R16Unorm;
case kCVPixelFormatType_420YpCbCr8BiPlanarVideoRange:
return wgpu::TextureFormat::R8BG8Biplanar420Unorm;
- // TODO(dawn:551): Add R10X6BG10X6Biplanar420Unorm support.
+ case kCVPixelFormatType_420YpCbCr10BiPlanarVideoRange:
+ return wgpu::TextureFormat::R10X6BG10X6Biplanar420Unorm;
+ case kCVPixelFormatType_420YpCbCr8VideoRange_8A_TriPlanar:
+ return wgpu::TextureFormat::R8BG8A8Triplanar420Unorm;
default:
return DAWN_VALIDATION_ERROR("Unsupported IOSurface format (%x).", format);
}
diff --git a/src/dawn/native/metal/TextureMTL.mm b/src/dawn/native/metal/TextureMTL.mm
index 2365b60..7576489 100644
--- a/src/dawn/native/metal/TextureMTL.mm
+++ b/src/dawn/native/metal/TextureMTL.mm
@@ -246,6 +246,8 @@
return wgpu::TextureFormat::R8BG8Biplanar420Unorm;
case kCVPixelFormatType_420YpCbCr10BiPlanarVideoRange:
return wgpu::TextureFormat::R10X6BG10X6Biplanar420Unorm;
+ case kCVPixelFormatType_420YpCbCr8VideoRange_8A_TriPlanar:
+ return wgpu::TextureFormat::R8BG8A8Triplanar420Unorm;
default:
return DAWN_VALIDATION_ERROR("Unsupported IOSurface format (%x).", format);
}
@@ -649,6 +651,7 @@
case wgpu::TextureFormat::R8BG8Biplanar420Unorm:
case wgpu::TextureFormat::R10X6BG10X6Biplanar420Unorm:
+ case wgpu::TextureFormat::R8BG8A8Triplanar420Unorm:
case wgpu::TextureFormat::Undefined:
DAWN_UNREACHABLE();
}
@@ -1003,6 +1006,9 @@
case Aspect::Plane1:
DAWN_ASSERT(mMtlPlaneTextures->size() > 1);
return mMtlPlaneTextures[1].Get();
+ case Aspect::Plane2:
+ DAWN_ASSERT(mMtlPlaneTextures->size() > 2);
+ return mMtlPlaneTextures[2].Get();
default:
DAWN_ASSERT(mMtlPlaneTextures->size() == 1);
return mMtlPlaneTextures[0].Get();
diff --git a/src/dawn/native/opengl/CommandBufferGL.cpp b/src/dawn/native/opengl/CommandBufferGL.cpp
index 5600917..f6bd10e 100644
--- a/src/dawn/native/opengl/CommandBufferGL.cpp
+++ b/src/dawn/native/opengl/CommandBufferGL.cpp
@@ -342,6 +342,7 @@
case Aspect::CombinedDepthStencil:
case Aspect::Plane0:
case Aspect::Plane1:
+ case Aspect::Plane2:
DAWN_UNREACHABLE();
case Aspect::Depth:
gl.TexParameteri(target, GL_DEPTH_STENCIL_TEXTURE_MODE,
@@ -725,6 +726,7 @@
case Aspect::None:
case Aspect::Plane0:
case Aspect::Plane1:
+ case Aspect::Plane2:
DAWN_UNREACHABLE();
}
diff --git a/src/dawn/native/opengl/UtilsGL.cpp b/src/dawn/native/opengl/UtilsGL.cpp
index 20e5fab..a961322 100644
--- a/src/dawn/native/opengl/UtilsGL.cpp
+++ b/src/dawn/native/opengl/UtilsGL.cpp
@@ -131,6 +131,7 @@
case Aspect::None:
case Aspect::Plane0:
case Aspect::Plane1:
+ case Aspect::Plane2:
DAWN_UNREACHABLE();
}
if (srcTarget == GL_TEXTURE_2D) {
diff --git a/src/dawn/native/vulkan/TextureVk.cpp b/src/dawn/native/vulkan/TextureVk.cpp
index 6d65e13..02eb0ae 100644
--- a/src/dawn/native/vulkan/TextureVk.cpp
+++ b/src/dawn/native/vulkan/TextureVk.cpp
@@ -310,10 +310,10 @@
}
// Some multiplanar images cannot have planes transitioned separately and instead Vulkan
- // requires that the "Color" aspect be used for barriers, so Plane0|Plane1 is promoted to just
- // Color. The Vulkan spec requires: "If image has a single-plane color format or is not
+ // requires that the "Color" aspect be used for barriers, so Plane0|Plane1|Plane2 is promoted to
+ // just Color. The Vulkan spec requires: "If image has a single-plane color format or is not
// disjoint, then the aspectMask member of subresourceRange must be VK_IMAGE_ASPECT_COLOR_BIT.".
- if (format.aspects == (Aspect::Plane0 | Aspect::Plane1)) {
+ if (format.IsMultiPlanar()) {
return Aspect::Color;
}
@@ -555,7 +555,8 @@
return VK_FORMAT_G8_B8R8_2PLANE_420_UNORM;
case wgpu::TextureFormat::R10X6BG10X6Biplanar420Unorm:
return VK_FORMAT_G10X6_B10X6R10X6_2PLANE_420_UNORM_3PACK16;
-
+ // R8BG8A8Triplanar420Unorm format is only supported on macOS.
+ case wgpu::TextureFormat::R8BG8A8Triplanar420Unorm:
case wgpu::TextureFormat::Undefined:
break;
}
@@ -1486,7 +1487,7 @@
}
// need to clear the texture with a copy from buffer
DAWN_ASSERT(range.aspects == Aspect::Color || range.aspects == Aspect::Plane0 ||
- range.aspects == Aspect::Plane1);
+ range.aspects == Aspect::Plane1 || range.aspects == Aspect::Plane2);
const TexelBlockInfo& blockInfo = GetFormat().GetAspectInfo(range.aspects).block;
Extent3D largestMipSize =
diff --git a/src/dawn/native/vulkan/UtilsVulkan.cpp b/src/dawn/native/vulkan/UtilsVulkan.cpp
index cdcefe3..16940512 100644
--- a/src/dawn/native/vulkan/UtilsVulkan.cpp
+++ b/src/dawn/native/vulkan/UtilsVulkan.cpp
@@ -111,6 +111,9 @@
case Aspect::Plane1:
flags |= VK_IMAGE_ASPECT_PLANE_1_BIT;
break;
+ case Aspect::Plane2:
+ flags |= VK_IMAGE_ASPECT_PLANE_2_BIT;
+ break;
case Aspect::None:
DAWN_UNREACHABLE();
diff --git a/src/dawn/node/binding/Converter.cpp b/src/dawn/node/binding/Converter.cpp
index ad4062b..bcd8b37 100644
--- a/src/dawn/node/binding/Converter.cpp
+++ b/src/dawn/node/binding/Converter.cpp
@@ -658,6 +658,7 @@
case wgpu::TextureFormat::R16Snorm:
case wgpu::TextureFormat::R16Unorm:
case wgpu::TextureFormat::R8BG8Biplanar420Unorm:
+ case wgpu::TextureFormat::R8BG8A8Triplanar420Unorm:
case wgpu::TextureFormat::RG16Snorm:
case wgpu::TextureFormat::RG16Unorm:
case wgpu::TextureFormat::RGBA16Snorm:
@@ -1502,6 +1503,7 @@
case wgpu::FeatureName::MSAARenderToSingleSampled:
case wgpu::FeatureName::MultiPlanarFormatExtendedUsages:
case wgpu::FeatureName::MultiPlanarFormatP010:
+ case wgpu::FeatureName::MultiPlanarFormatNv12a:
case wgpu::FeatureName::Norm16TextureFormats:
case wgpu::FeatureName::PixelLocalStorageCoherent:
case wgpu::FeatureName::PixelLocalStorageNonCoherent:
diff --git a/src/dawn/tests/end2end/SharedTextureMemoryTests.cpp b/src/dawn/tests/end2end/SharedTextureMemoryTests.cpp
index 648d1eb..576e789 100644
--- a/src/dawn/tests/end2end/SharedTextureMemoryTests.cpp
+++ b/src/dawn/tests/end2end/SharedTextureMemoryTests.cpp
@@ -640,7 +640,9 @@
if (properties.usage & wgpu::TextureUsage::RenderAttachment) {
ASSERT_DEVICE_ERROR_MSG(UseInRenderPass(device, texture),
HasSubstr("without current access"));
- } else if (properties.format != wgpu::TextureFormat::R8BG8Biplanar420Unorm) {
+ } else if (properties.format != wgpu::TextureFormat::R8BG8Biplanar420Unorm &&
+ properties.format != wgpu::TextureFormat::R10X6BG10X6Biplanar420Unorm &&
+ properties.format != wgpu::TextureFormat::R8BG8A8Triplanar420Unorm) {
if (properties.usage & wgpu::TextureUsage::CopySrc) {
ASSERT_DEVICE_ERROR_MSG(UseInCopy(device, texture),
HasSubstr("without current access"));
@@ -676,7 +678,9 @@
// Use the texture on the GPU; it should not crash.
if (properties.usage & wgpu::TextureUsage::RenderAttachment) {
UseInRenderPass(device, texture);
- } else if (properties.format != wgpu::TextureFormat::R8BG8Biplanar420Unorm) {
+ } else if (properties.format != wgpu::TextureFormat::R8BG8Biplanar420Unorm &&
+ properties.format != wgpu::TextureFormat::R10X6BG10X6Biplanar420Unorm &&
+ properties.format != wgpu::TextureFormat::R8BG8A8Triplanar420Unorm) {
DAWN_ASSERT(properties.usage & wgpu::TextureUsage::CopySrc);
UseInCopy(device, texture);
}
diff --git a/src/dawn/tests/end2end/SharedTextureMemoryTests_apple.mm b/src/dawn/tests/end2end/SharedTextureMemoryTests_apple.mm
index 949b675..51caf7c 100644
--- a/src/dawn/tests/end2end/SharedTextureMemoryTests_apple.mm
+++ b/src/dawn/tests/end2end/SharedTextureMemoryTests_apple.mm
@@ -56,7 +56,8 @@
std::vector<wgpu::FeatureName> RequiredFeatures() const override {
return {wgpu::FeatureName::SharedTextureMemoryIOSurface,
wgpu::FeatureName::SharedFenceMTLSharedEvent,
- wgpu::FeatureName::DawnMultiPlanarFormats};
+ wgpu::FeatureName::DawnMultiPlanarFormats,
+ wgpu::FeatureName::MultiPlanarFormatNv12a};
}
// Create one basic shared texture memory. It should support most operations.
@@ -92,7 +93,9 @@
std::make_pair(kCVPixelFormatType_32BGRA, 4),
std::make_pair(kCVPixelFormatType_TwoComponent8, 2),
std::make_pair(kCVPixelFormatType_OneComponent8, 1),
+ // Below bytes per element isn't correct.
std::make_pair(kCVPixelFormatType_420YpCbCr8BiPlanarVideoRange, 4),
+ std::make_pair(kCVPixelFormatType_420YpCbCr8VideoRange_8A_TriPlanar, 4),
// TODO(dawn:551): Add R10X6BG10X6Biplanar420Unorm support.
}) {
for (uint32_t size : {4, 64}) {
diff --git a/src/dawn/tests/end2end/VideoViewsTests.cpp b/src/dawn/tests/end2end/VideoViewsTests.cpp
index 7a6b4cd..f8055f5 100644
--- a/src/dawn/tests/end2end/VideoViewsTests.cpp
+++ b/src/dawn/tests/end2end/VideoViewsTests.cpp
@@ -45,10 +45,10 @@
VideoViewsTestBackend::~VideoViewsTestBackend() = default;
-constexpr std::array<utils::RGBA8, 2> VideoViewsTestsBase::kYellowYUVColor;
-constexpr std::array<utils::RGBA8, 2> VideoViewsTestsBase::kWhiteYUVColor;
-constexpr std::array<utils::RGBA8, 2> VideoViewsTestsBase::kBlueYUVColor;
-constexpr std::array<utils::RGBA8, 2> VideoViewsTestsBase::kRedYUVColor;
+constexpr std::array<utils::RGBA8, 3> VideoViewsTestsBase::kYellowYUVAColor;
+constexpr std::array<utils::RGBA8, 3> VideoViewsTestsBase::kWhiteYUVAColor;
+constexpr std::array<utils::RGBA8, 3> VideoViewsTestsBase::kBlueYUVAColor;
+constexpr std::array<utils::RGBA8, 3> VideoViewsTestsBase::kRedYUVAColor;
void VideoViewsTestsBase::SetUp() {
DawnTestWithParams<Params>::SetUp();
@@ -67,6 +67,11 @@
if (mIsMultiPlanarFormatP010Supported) {
requiredFeatures.push_back(wgpu::FeatureName::MultiPlanarFormatP010);
}
+ mIsMultiPlanarFormatNv12aSupported =
+ SupportsFeatures({wgpu::FeatureName::MultiPlanarFormatNv12a});
+ if (mIsMultiPlanarFormatNv12aSupported) {
+ requiredFeatures.push_back(wgpu::FeatureName::MultiPlanarFormatNv12a);
+ }
mIsNorm16TextureFormatsSupported = SupportsFeatures({wgpu::FeatureName::Norm16TextureFormats});
if (mIsNorm16TextureFormatsSupported) {
requiredFeatures.push_back(wgpu::FeatureName::Norm16TextureFormats);
@@ -83,6 +88,10 @@
return mIsMultiPlanarFormatP010Supported;
}
+bool VideoViewsTestsBase::IsMultiPlanarFormatNv12aSupported() const {
+ return mIsMultiPlanarFormatNv12aSupported;
+}
+
bool VideoViewsTestsBase::IsNorm16TextureFormatsSupported() const {
return mIsNorm16TextureFormatsSupported;
}
@@ -100,6 +109,14 @@
return IsNorm16TextureFormatsSupported() && IsMultiPlanarFormatP010Supported();
}
+ if (GetFormat() == wgpu::TextureFormat::R8BG8A8Triplanar420Unorm) {
+ // R8BG8A8Triplanar420Unorm is only supported on Metal.
+ if (!IsMetal()) {
+ return false;
+ }
+ return IsMultiPlanarFormatNv12aSupported();
+ }
+
return true;
}
@@ -110,85 +127,129 @@
// image is converted from a solid yellow 4x4 block.
// static
template <typename T>
-std::vector<T> VideoViewsTestsBase::GetTestTextureData(wgpu::TextureFormat format,
- bool isCheckerboard) {
+std::vector<T> VideoViewsTestsBase::GetTestTextureData(bool isMultiPlane,
+ bool isCheckerboard,
+ bool hasAlpha) {
const uint8_t kLeftShiftBits = (sizeof(T) - 1) * 8;
- constexpr T Yy = kYellowYUVColor[kYUVLumaPlaneIndex].r << kLeftShiftBits;
- constexpr T Yu = kYellowYUVColor[kYUVChromaPlaneIndex].r << kLeftShiftBits;
- constexpr T Yv = kYellowYUVColor[kYUVChromaPlaneIndex].g << kLeftShiftBits;
+ constexpr T Yy = kYellowYUVAColor[kYUVALumaPlaneIndex].r << kLeftShiftBits;
+ constexpr T Yu = kYellowYUVAColor[kYUVAChromaPlaneIndex].r << kLeftShiftBits;
+ constexpr T Yv = kYellowYUVAColor[kYUVAChromaPlaneIndex].g << kLeftShiftBits;
+ constexpr T Ya = kYellowYUVAColor[kYUVAAlphaPlaneIndex].r << kLeftShiftBits;
- constexpr T Wy = kWhiteYUVColor[kYUVLumaPlaneIndex].r << kLeftShiftBits;
- constexpr T Wu = kWhiteYUVColor[kYUVChromaPlaneIndex].r << kLeftShiftBits;
- constexpr T Wv = kWhiteYUVColor[kYUVChromaPlaneIndex].g << kLeftShiftBits;
+ constexpr T Wy = kWhiteYUVAColor[kYUVALumaPlaneIndex].r << kLeftShiftBits;
+ constexpr T Wu = kWhiteYUVAColor[kYUVAChromaPlaneIndex].r << kLeftShiftBits;
+ constexpr T Wv = kWhiteYUVAColor[kYUVAChromaPlaneIndex].g << kLeftShiftBits;
+ constexpr T Wa = kWhiteYUVAColor[kYUVAAlphaPlaneIndex].r << kLeftShiftBits;
- constexpr T Ry = kRedYUVColor[kYUVLumaPlaneIndex].r << kLeftShiftBits;
- constexpr T Ru = kRedYUVColor[kYUVChromaPlaneIndex].r << kLeftShiftBits;
- constexpr T Rv = kRedYUVColor[kYUVChromaPlaneIndex].g << kLeftShiftBits;
+ constexpr T Ry = kRedYUVAColor[kYUVALumaPlaneIndex].r << kLeftShiftBits;
+ constexpr T Ru = kRedYUVAColor[kYUVAChromaPlaneIndex].r << kLeftShiftBits;
+ constexpr T Rv = kRedYUVAColor[kYUVAChromaPlaneIndex].g << kLeftShiftBits;
+ constexpr T Ra = kRedYUVAColor[kYUVAAlphaPlaneIndex].r << kLeftShiftBits;
- constexpr T By = kBlueYUVColor[kYUVLumaPlaneIndex].r << kLeftShiftBits;
- constexpr T Bu = kBlueYUVColor[kYUVChromaPlaneIndex].r << kLeftShiftBits;
- constexpr T Bv = kBlueYUVColor[kYUVChromaPlaneIndex].g << kLeftShiftBits;
+ constexpr T By = kBlueYUVAColor[kYUVALumaPlaneIndex].r << kLeftShiftBits;
+ constexpr T Bu = kBlueYUVAColor[kYUVAChromaPlaneIndex].r << kLeftShiftBits;
+ constexpr T Bv = kBlueYUVAColor[kYUVAChromaPlaneIndex].g << kLeftShiftBits;
+ constexpr T Ba = kBlueYUVAColor[kYUVAAlphaPlaneIndex].r << kLeftShiftBits;
- switch (format) {
+ if (isMultiPlane) {
// The first 16 bytes is the luma plane (Y), followed by the chroma plane (UV) which
// is half the number of bytes (subsampled by 2) but same bytes per line as luma
- // plane.
- case wgpu::TextureFormat::R8BG8Biplanar420Unorm:
- case wgpu::TextureFormat::R10X6BG10X6Biplanar420Unorm:
- if (isCheckerboard) {
- return {
- Wy, Wy, Ry, Ry, // plane 0, start + 0
- Wy, Wy, Ry, Ry, //
- Yy, Yy, By, By, //
- Yy, Yy, By, By, //
- Wu, Wv, Ru, Rv, // plane 1, start + 16
- Yu, Yv, Bu, Bv, //
- };
- } else {
- return {
- Yy, Yy, Yy, Yy, // plane 0, start + 0
- Yy, Yy, Yy, Yy, //
- Yy, Yy, Yy, Yy, //
- Yy, Yy, Yy, Yy, //
- Yu, Yv, Yu, Yv, // plane 1, start + 16
- Yu, Yv, Yu, Yv, //
- };
- }
- case wgpu::TextureFormat::RGBA8Unorm:
- // Combines both NV12 planes by directly mapping back to RGB: R=Y, G=U, B=V.
- if (isCheckerboard) {
- return {
- Yy, Yu, Yv, Yy, Yu, Yv, By, Bu, Bv, By, Bu, Bv, //
- Yy, Yu, Yv, Yy, Yu, Yv, By, Bu, Bv, By, Bu, Bv, //
- Wy, Wu, Wv, Wy, Wu, Wv, Ry, Ru, Rv, Ry, Ru, Rv, //
- Wy, Wu, Wv, Wy, Wu, Wv, Ry, Ru, Rv, Ry, Ru, Rv, //
- };
- } else {
- return {
- Yy, Yu, Yv, Yy, Yu, Yv, Yy, Yu, Yv, Yy, Yu, Yv, //
- Yy, Yu, Yv, Yy, Yu, Yv, Yy, Yu, Yv, Yy, Yu, Yv, //
- Yy, Yu, Yv, Yy, Yu, Yv, Yy, Yu, Yv, Yy, Yu, Yv, //
- Yy, Yu, Yv, Yy, Yu, Yv, Yy, Yu, Yv, Yy, Yu, Yv, //
- };
- }
- default:
- DAWN_UNREACHABLE();
- return {};
+ // plane. If alpha exist, then alpha plane (A) should has the same number of bytes and same
+ // bytes per line as luma plane.
+ if (hasAlpha && isCheckerboard) {
+ return {
+ Wy, Wy, Ry, Ry, // plane 0, start + 0
+ Wy, Wy, Ry, Ry, //
+ Yy, Yy, By, By, //
+ Yy, Yy, By, By, //
+ Wu, Wv, Ru, Rv, // plane 1, start + 16
+ Yu, Yv, Bu, Bv, //
+ Wa, Wa, Ra, Ra, // plane 2, start + 24
+ Wa, Wa, Ra, Ra, //
+ Ya, Ya, Ba, Ba, //
+ Ya, Ya, Ba, Ba, //
+ };
+ } else if (hasAlpha && !isCheckerboard) {
+ return {
+ Yy, Yy, Yy, Yy, // plane 0, start + 0
+ Yy, Yy, Yy, Yy, //
+ Yy, Yy, Yy, Yy, //
+ Yy, Yy, Yy, Yy, //
+ Yu, Yv, Yu, Yv, // plane 1, start + 16
+ Yu, Yv, Yu, Yv, //
+ Ya, Ya, Ya, Ya, // plane 2, start + 24
+ Ya, Ya, Ya, Ya, //
+ Ya, Ya, Ya, Ya, //
+ Ya, Ya, Ya, Ya, //
+ };
+ } else if (isCheckerboard) {
+ return {
+ Wy, Wy, Ry, Ry, // plane 0, start + 0
+ Wy, Wy, Ry, Ry, //
+ Yy, Yy, By, By, //
+ Yy, Yy, By, By, //
+ Wu, Wv, Ru, Rv, // plane 1, start + 16
+ Yu, Yv, Bu, Bv, //
+ };
+ } else {
+ return {
+ Yy, Yy, Yy, Yy, // plane 0, start + 0
+ Yy, Yy, Yy, Yy, //
+ Yy, Yy, Yy, Yy, //
+ Yy, Yy, Yy, Yy, //
+ Yu, Yv, Yu, Yv, // plane 1, start + 16
+ Yu, Yv, Yu, Yv, //
+ };
+ }
+ }
+
+ // Combines both planes by directly mapping back to RGBA: R=Y, G=U, B=V, A=A.
+ if (hasAlpha && isCheckerboard) {
+ return {
+ Yy, Yu, Yv, Ya, Yy, Yu, Yv, Ya, By, Bu, Bv, Ba, By, Bu, Bv, Ba, //
+ Yy, Yu, Yv, Ya, Yy, Yu, Yv, Ya, By, Bu, Bv, Ba, By, Bu, Bv, Ba, //
+ Wy, Wu, Wv, Wa, Wy, Wu, Wv, Wa, Ry, Ru, Rv, Ra, Ry, Ru, Rv, Ra, //
+ Wy, Wu, Wv, Wa, Wy, Wu, Wv, Wa, Ry, Ru, Rv, Ra, Ry, Ru, Rv, Ra, //
+ };
+ } else if (hasAlpha && !isCheckerboard) {
+ return {
+ Yy, Yu, Yv, Ya, Yy, Yu, Yv, Ya, Yy, Yu, Yv, Ya, Yy, Yu, Yv, Ya, //
+ Yy, Yu, Yv, Ya, Yy, Yu, Yv, Ya, Yy, Yu, Yv, Ya, Yy, Yu, Yv, Ya, //
+ Yy, Yu, Yv, Ya, Yy, Yu, Yv, Ya, Yy, Yu, Yv, Ya, Yy, Yu, Yv, Ya, //
+ Yy, Yu, Yv, Ya, Yy, Yu, Yv, Ya, Yy, Yu, Yv, Ya, Yy, Yu, Yv, Ya, //
+ };
+ } else if (isCheckerboard) {
+ return {
+ Yy, Yu, Yv, Yy, Yu, Yv, By, Bu, Bv, By, Bu, Bv, //
+ Yy, Yu, Yv, Yy, Yu, Yv, By, Bu, Bv, By, Bu, Bv, //
+ Wy, Wu, Wv, Wy, Wu, Wv, Ry, Ru, Rv, Ry, Ru, Rv, //
+ Wy, Wu, Wv, Wy, Wu, Wv, Ry, Ru, Rv, Ry, Ru, Rv, //
+ };
+ } else {
+ return {
+ Yy, Yu, Yv, Yy, Yu, Yv, Yy, Yu, Yv, Yy, Yu, Yv, //
+ Yy, Yu, Yv, Yy, Yu, Yv, Yy, Yu, Yv, Yy, Yu, Yv, //
+ Yy, Yu, Yv, Yy, Yu, Yv, Yy, Yu, Yv, Yy, Yu, Yv, //
+ Yy, Yu, Yv, Yy, Yu, Yv, Yy, Yu, Yv, Yy, Yu, Yv, //
+ };
}
}
-template std::vector<uint8_t> VideoViewsTestsBase::GetTestTextureData<uint8_t>(
- wgpu::TextureFormat format,
- bool isCheckerboard);
+template std::vector<uint8_t> VideoViewsTestsBase::GetTestTextureData<uint8_t>(bool isMultiPlane,
+ bool isCheckerboard,
+ bool hasAlpha);
template std::vector<uint16_t> VideoViewsTestsBase::GetTestTextureData<uint16_t>(
- wgpu::TextureFormat format,
- bool isCheckerboard);
+ bool isMultiPlane,
+ bool isCheckerboard,
+ bool hasAlpha);
uint32_t VideoViewsTestsBase::NumPlanes(wgpu::TextureFormat format) {
switch (format) {
case wgpu::TextureFormat::R8BG8Biplanar420Unorm:
case wgpu::TextureFormat::R10X6BG10X6Biplanar420Unorm:
return 2;
+ case wgpu::TextureFormat::R8BG8A8Triplanar420Unorm:
+ return 3;
default:
DAWN_UNREACHABLE();
return 0;
@@ -199,57 +260,57 @@
std::vector<T> VideoViewsTestsBase::GetTestTextureDataWithPlaneIndex(size_t planeIndex,
size_t bytesPerRow,
size_t height,
- bool isCheckerboard) {
- std::vector<T> texelData = VideoViewsTestsBase::GetTestTextureData<T>(
- wgpu::TextureFormat::R8BG8Biplanar420Unorm, isCheckerboard);
- const uint32_t texelDataWidth = kYUVImageDataWidthInTexels;
- const uint32_t texelDataHeight =
- planeIndex == 0 ? kYUVImageDataHeightInTexels : kYUVImageDataHeightInTexels / 2;
+ bool isCheckerboard,
+ bool hasAlpha) {
+ std::vector<T> texelData =
+ VideoViewsTestsBase::GetTestTextureData<T>(/*isMultiPlane*/ true, isCheckerboard, hasAlpha);
+ const uint32_t texelDataWidth = kYUVAImageDataWidthInTexels;
+ const uint32_t texelDataHeight = planeIndex == kYUVAChromaPlaneIndex
+ ? kYUVAImageDataHeightInTexels / 2
+ : kYUVAImageDataHeightInTexels;
size_t rowPitch = bytesPerRow / sizeof(T);
std::vector<T> texels(rowPitch * height, 0);
- uint32_t plane_first_texel_offset = 0;
- // The size of the test video frame is 4 x 4
+ uint32_t planeFirstTexelOffset = 0;
+ // The size of the test video frame is 4 x 4, and TexelData is 4 * 6 size or (4 * 10 size if
+ // alpha exist)
switch (planeIndex) {
- case VideoViewsTestsBase::kYUVLumaPlaneIndex:
- for (uint32_t i = 0; i < texelDataHeight; ++i) {
- if (i < texelDataHeight) {
- for (uint32_t j = 0; j < texelDataWidth; ++j) {
- texels[rowPitch * i + j] =
- texelData[texelDataWidth * i + j + plane_first_texel_offset];
- }
- }
- }
- return texels;
- case VideoViewsTestsBase::kYUVChromaPlaneIndex:
- // TexelData is 4 * 6 size, first 4 * 4 is Y plane, UV plane started
- // at index 16.
- plane_first_texel_offset = 16;
- for (uint32_t i = 0; i < texelDataHeight; ++i) {
- if (i < texelDataHeight) {
- for (uint32_t j = 0; j < texelDataWidth; ++j) {
- texels[rowPitch * i + j] =
- texelData[texelDataWidth * i + j + plane_first_texel_offset];
- }
- }
- }
- return texels;
+ case VideoViewsTestsBase::kYUVALumaPlaneIndex:
+ planeFirstTexelOffset = 0;
+ break;
+ case VideoViewsTestsBase::kYUVAChromaPlaneIndex:
+ planeFirstTexelOffset = 16;
+ break;
+ case VideoViewsTestsBase::kYUVAAlphaPlaneIndex:
+ planeFirstTexelOffset = 24;
+ break;
default:
DAWN_UNREACHABLE();
return {};
}
+ for (uint32_t i = 0; i < texelDataHeight; ++i) {
+ if (i < texelDataHeight) {
+ for (uint32_t j = 0; j < texelDataWidth; ++j) {
+ texels[rowPitch * i + j] =
+ texelData[texelDataWidth * i + j + planeFirstTexelOffset];
+ }
+ }
+ }
+ return texels;
}
template std::vector<uint8_t> VideoViewsTestsBase::GetTestTextureDataWithPlaneIndex<uint8_t>(
size_t planeIndex,
size_t bytesPerRow,
size_t height,
- bool isCheckerboard);
+ bool isCheckerboard,
+ bool hasAlpha);
template std::vector<uint16_t> VideoViewsTestsBase::GetTestTextureDataWithPlaneIndex<uint16_t>(
size_t planeIndex,
size_t bytesPerRow,
size_t height,
- bool isCheckerboard);
+ bool isCheckerboard,
+ bool hasAlpha);
wgpu::TextureFormat VideoViewsTestsBase::GetFormat() const {
return GetParam().mFormat;
@@ -258,15 +319,30 @@
wgpu::TextureFormat VideoViewsTestsBase::GetPlaneFormat(int plane) const {
switch (GetFormat()) {
case wgpu::TextureFormat::R8BG8Biplanar420Unorm:
- return plane == 0 ? wgpu::TextureFormat::R8Unorm : wgpu::TextureFormat::RG8Unorm;
+ case wgpu::TextureFormat::R8BG8A8Triplanar420Unorm:
+ return plane == 1 ? wgpu::TextureFormat::RG8Unorm : wgpu::TextureFormat::R8Unorm;
case wgpu::TextureFormat::R10X6BG10X6Biplanar420Unorm:
- return plane == 0 ? wgpu::TextureFormat::R16Unorm : wgpu::TextureFormat::RG16Unorm;
+ return plane == 1 ? wgpu::TextureFormat::RG16Unorm : wgpu::TextureFormat::R16Unorm;
default:
DAWN_UNREACHABLE();
return wgpu::TextureFormat::Undefined;
}
}
+wgpu::TextureAspect VideoViewsTestsBase::GetPlaneAspect(int plane) const {
+ switch (plane) {
+ case VideoViewsTestsBase::kYUVALumaPlaneIndex:
+ return wgpu::TextureAspect::Plane0Only;
+ case VideoViewsTestsBase::kYUVAChromaPlaneIndex:
+ return wgpu::TextureAspect::Plane1Only;
+ case VideoViewsTestsBase::kYUVAAlphaPlaneIndex:
+ return wgpu::TextureAspect::Plane2Only;
+ default:
+ DAWN_UNREACHABLE();
+ return wgpu::TextureAspect::All;
+ }
+}
+
// Vertex shader used to render a sampled texture into a quad.
wgpu::ShaderModule VideoViewsTestsBase::GetTestVertexShaderModule() const {
return utils::CreateShaderModule(device, R"(
@@ -357,7 +433,7 @@
})");
utils::BasicRenderPass renderPass = utils::CreateBasicRenderPass(
- device, kYUVImageDataWidthInTexels, kYUVImageDataHeightInTexels);
+ device, kYUVAImageDataWidthInTexels, kYUVAImageDataHeightInTexels);
renderPipelineDescriptor.cTargets[0].format = renderPass.colorFormat;
renderPipelineDescriptor.primitive.topology = wgpu::PrimitiveTopology::TriangleList;
@@ -379,7 +455,7 @@
queue.Submit(1, &commands);
// Test the luma plane in the top left corner of RGB image.
- EXPECT_TEXTURE_EQ(&kYellowYUVColor[kYUVLumaPlaneIndex], renderPass.color, {0, 0}, {1, 1}, 0,
+ EXPECT_TEXTURE_EQ(&kYellowYUVAColor[kYUVALumaPlaneIndex], renderPass.color, {0, 0}, {1, 1}, 0,
wgpu::TextureAspect::All, 0, kTolerance);
mBackend->DestroyVideoTextureForTest(std::move(platformTexture));
@@ -418,7 +494,7 @@
})");
utils::BasicRenderPass renderPass = utils::CreateBasicRenderPass(
- device, kYUVImageDataWidthInTexels, kYUVImageDataHeightInTexels);
+ device, kYUVAImageDataWidthInTexels, kYUVAImageDataHeightInTexels);
renderPipelineDescriptor.cTargets[0].format = renderPass.colorFormat;
renderPipelineDescriptor.primitive.topology = wgpu::PrimitiveTopology::TriangleList;
@@ -440,7 +516,7 @@
queue.Submit(1, &commands);
// Test the chroma plane in the top left corner of RGB image.
- EXPECT_TEXTURE_EQ(&kYellowYUVColor[kYUVChromaPlaneIndex], renderPass.color, {0, 0}, {1, 1}, 0,
+ EXPECT_TEXTURE_EQ(&kYellowYUVAColor[kYUVAChromaPlaneIndex], renderPass.color, {0, 0}, {1, 1}, 0,
wgpu::TextureAspect::All, 0, kTolerance);
mBackend->DestroyVideoTextureForTest(std::move(platformTexture));
}
@@ -457,6 +533,10 @@
mBackend->DestroyVideoTextureForTest(std::move(platformTexture));
GTEST_SKIP() << "Skipped because not supported.";
}
+ const bool hasAlpha = NumPlanes(GetFormat()) > 2;
+ if (hasAlpha) {
+ GTEST_SKIP() << "Skipped because format is not YUV.";
+ }
wgpu::TextureViewDescriptor lumaViewDesc;
lumaViewDesc.format = GetPlaneFormat(0);
@@ -485,7 +565,7 @@
})");
utils::BasicRenderPass renderPass = utils::CreateBasicRenderPass(
- device, kYUVImageDataWidthInTexels, kYUVImageDataHeightInTexels);
+ device, kYUVAImageDataWidthInTexels, kYUVAImageDataHeightInTexels);
renderPipelineDescriptor.cTargets[0].format = renderPass.colorFormat;
wgpu::RenderPipeline renderPipeline = device.CreateRenderPipeline(&renderPipelineDescriptor);
@@ -507,14 +587,103 @@
queue.Submit(1, &commands);
std::vector<uint8_t> expectedData =
- GetTestTextureData<uint8_t>(wgpu::TextureFormat::RGBA8Unorm, true);
+ GetTestTextureData<uint8_t>(/*isMultiPlane*/ false, /*isCheckerboard*/ true, hasAlpha);
std::vector<utils::RGBA8> expectedRGBA;
for (uint8_t i = 0; i < expectedData.size(); i += 3) {
expectedRGBA.push_back({expectedData[i], expectedData[i + 1], expectedData[i + 2], 0xFF});
}
EXPECT_TEXTURE_EQ(expectedRGBA.data(), renderPass.color, {0, 0},
- {kYUVImageDataWidthInTexels, kYUVImageDataHeightInTexels}, 0,
+ {kYUVAImageDataWidthInTexels, kYUVAImageDataHeightInTexels}, 0,
+ wgpu::TextureAspect::All, 0, kTolerance);
+ mBackend->DestroyVideoTextureForTest(std::move(platformTexture));
+}
+
+// Renders a "checkerboard" texture into a RGBA quad, then checks the the entire
+// contents to ensure the image has not been flipped.
+TEST_P(VideoViewsTests, SampleYUVAtoRGBA) {
+ std::unique_ptr<VideoViewsTestBackend::PlatformTexture> platformTexture =
+ mBackend->CreateVideoTextureForTest(GetFormat(), wgpu::TextureUsage::TextureBinding,
+ /*isCheckerboard*/ true,
+ /*initialized*/ true);
+ ASSERT_NE(platformTexture.get(), nullptr);
+ if (!platformTexture->CanWrapAsWGPUTexture()) {
+ mBackend->DestroyVideoTextureForTest(std::move(platformTexture));
+ GTEST_SKIP() << "Skipped because not supported.";
+ }
+
+ const bool hasAlpha = NumPlanes(GetFormat()) > 2;
+ if (!hasAlpha) {
+ GTEST_SKIP() << "Skipped because format is not YUVA.";
+ }
+
+ wgpu::TextureViewDescriptor lumaViewDesc;
+ lumaViewDesc.format = GetPlaneFormat(0);
+ lumaViewDesc.aspect = wgpu::TextureAspect::Plane0Only;
+ wgpu::TextureView lumaTextureView = platformTexture->wgpuTexture.CreateView(&lumaViewDesc);
+
+ wgpu::TextureViewDescriptor chromaViewDesc;
+ chromaViewDesc.format = GetPlaneFormat(1);
+ chromaViewDesc.aspect = wgpu::TextureAspect::Plane1Only;
+ wgpu::TextureView chromaTextureView = platformTexture->wgpuTexture.CreateView(&chromaViewDesc);
+
+ wgpu::TextureViewDescriptor alphaViewDesc;
+ alphaViewDesc.format = GetPlaneFormat(2);
+ alphaViewDesc.aspect = wgpu::TextureAspect::Plane2Only;
+ wgpu::TextureView alphaTextureView = platformTexture->wgpuTexture.CreateView(&alphaViewDesc);
+
+ utils::ComboRenderPipelineDescriptor renderPipelineDescriptor;
+ renderPipelineDescriptor.vertex.module = GetTestVertexShaderModule();
+
+ renderPipelineDescriptor.cFragment.module = utils::CreateShaderModule(device, R"(
+ @group(0) @binding(0) var sampler0 : sampler;
+ @group(0) @binding(1) var lumaTexture : texture_2d<f32>;
+ @group(0) @binding(2) var chromaTexture : texture_2d<f32>;
+ @group(0) @binding(3) var alphaTexture : texture_2d<f32>;
+
+ @fragment
+ fn main(@location(0) texCoord : vec2f) -> @location(0) vec4f {
+ let y : f32 = textureSample(lumaTexture, sampler0, texCoord).r;
+ let u : f32 = textureSample(chromaTexture, sampler0, texCoord).r;
+ let v : f32 = textureSample(chromaTexture, sampler0, texCoord).g;
+ let a : f32 = textureSample(alphaTexture, sampler0, texCoord).r;
+ return vec4f(y, u, v, a);
+ })");
+
+ utils::BasicRenderPass renderPass = utils::CreateBasicRenderPass(
+ device, kYUVAImageDataWidthInTexels, kYUVAImageDataHeightInTexels);
+ renderPipelineDescriptor.cTargets[0].format = renderPass.colorFormat;
+
+ wgpu::RenderPipeline renderPipeline = device.CreateRenderPipeline(&renderPipelineDescriptor);
+
+ wgpu::Sampler sampler = device.CreateSampler();
+
+ wgpu::CommandEncoder encoder = device.CreateCommandEncoder();
+ {
+ wgpu::RenderPassEncoder pass = encoder.BeginRenderPass(&renderPass.renderPassInfo);
+ pass.SetPipeline(renderPipeline);
+ pass.SetBindGroup(0, utils::MakeBindGroup(device, renderPipeline.GetBindGroupLayout(0),
+ {{0, sampler},
+ {1, lumaTextureView},
+ {2, chromaTextureView},
+ {3, alphaTextureView}}));
+ pass.Draw(6);
+ pass.End();
+ }
+
+ wgpu::CommandBuffer commands = encoder.Finish();
+ queue.Submit(1, &commands);
+
+ std::vector<uint8_t> expectedData =
+ GetTestTextureData<uint8_t>(/*isMultiPlane*/ false, /*isCheckerboard*/ true, hasAlpha);
+ std::vector<utils::RGBA8> expectedRGBA;
+ for (uint8_t i = 0; i < expectedData.size(); i += 4) {
+ expectedRGBA.push_back(
+ {expectedData[i], expectedData[i + 1], expectedData[i + 2], expectedData[i + 3]});
+ }
+
+ EXPECT_TEXTURE_EQ(expectedRGBA.data(), renderPass.color, {0, 0},
+ {kYUVAImageDataWidthInTexels, kYUVAImageDataHeightInTexels}, 0,
wgpu::TextureAspect::All, 0, kTolerance);
mBackend->DestroyVideoTextureForTest(std::move(platformTexture));
}
@@ -531,6 +700,10 @@
mBackend->DestroyVideoTextureForTest(std::move(platformTexture));
GTEST_SKIP() << "Skipped because not supported.";
}
+ const bool hasAlpha = NumPlanes(GetFormat()) > 2;
+ if (hasAlpha) {
+ GTEST_SKIP() << "Skipped because format is not YUV.";
+ }
wgpu::TextureViewDescriptor lumaViewDesc;
lumaViewDesc.format = GetPlaneFormat(0);
@@ -560,7 +733,7 @@
})");
utils::BasicRenderPass renderPass = utils::CreateBasicRenderPass(
- device, kYUVImageDataWidthInTexels, kYUVImageDataHeightInTexels);
+ device, kYUVAImageDataWidthInTexels, kYUVAImageDataHeightInTexels);
renderPipelineDescriptor.cTargets[0].format = renderPass.colorFormat;
wgpu::RenderPipeline renderPipeline = device.CreateRenderPipeline(&renderPipelineDescriptor);
@@ -584,14 +757,108 @@
queue.Submit(1, &commands);
std::vector<uint8_t> expectedData =
- GetTestTextureData<uint8_t>(wgpu::TextureFormat::RGBA8Unorm, true);
+ GetTestTextureData<uint8_t>(/*isMultiPlane*/ false, /*isCheckerboard*/ true, hasAlpha);
std::vector<utils::RGBA8> expectedRGBA;
for (uint8_t i = 0; i < expectedData.size(); i += 3) {
- expectedRGBA.push_back({expectedData[i], expectedData[i + 1], expectedData[i + 2], 0xFF});
+ expectedRGBA.push_back({expectedData[i], expectedData[i + 1], expectedData[i + 2], 0xff});
}
EXPECT_TEXTURE_EQ(expectedRGBA.data(), renderPass.color, {0, 0},
- {kYUVImageDataWidthInTexels, kYUVImageDataHeightInTexels}, 0,
+ {kYUVAImageDataWidthInTexels, kYUVAImageDataHeightInTexels}, 0,
+ wgpu::TextureAspect::All, 0, kTolerance);
+ mBackend->DestroyVideoTextureForTest(std::move(platformTexture));
+}
+
+// Renders a "checkerboard" texture into a RGBA quad with three samplers, then checks the the
+// entire contents to ensure the image has not been flipped.
+TEST_P(VideoViewsTests, SampleYUVAtoRGBAMultipleSamplers) {
+ std::unique_ptr<VideoViewsTestBackend::PlatformTexture> platformTexture =
+ mBackend->CreateVideoTextureForTest(GetFormat(), wgpu::TextureUsage::TextureBinding,
+ /*isCheckerboard*/ true,
+ /*initialized*/ true);
+ ASSERT_NE(platformTexture.get(), nullptr);
+ if (!platformTexture->CanWrapAsWGPUTexture()) {
+ mBackend->DestroyVideoTextureForTest(std::move(platformTexture));
+ GTEST_SKIP() << "Skipped because not supported.";
+ }
+ const bool hasAlpha = NumPlanes(GetFormat()) > 2;
+ if (!hasAlpha) {
+ GTEST_SKIP() << "Skipped because format is not YUVA.";
+ }
+
+ wgpu::TextureViewDescriptor lumaViewDesc;
+ lumaViewDesc.format = GetPlaneFormat(0);
+ lumaViewDesc.aspect = wgpu::TextureAspect::Plane0Only;
+ wgpu::TextureView lumaTextureView = platformTexture->wgpuTexture.CreateView(&lumaViewDesc);
+
+ wgpu::TextureViewDescriptor chromaViewDesc;
+ chromaViewDesc.format = GetPlaneFormat(1);
+ chromaViewDesc.aspect = wgpu::TextureAspect::Plane1Only;
+ wgpu::TextureView chromaTextureView = platformTexture->wgpuTexture.CreateView(&chromaViewDesc);
+
+ wgpu::TextureViewDescriptor alphaViewDesc;
+ alphaViewDesc.format = GetPlaneFormat(2);
+ alphaViewDesc.aspect = wgpu::TextureAspect::Plane2Only;
+ wgpu::TextureView alphaTextureView = platformTexture->wgpuTexture.CreateView(&alphaViewDesc);
+
+ utils::ComboRenderPipelineDescriptor renderPipelineDescriptor;
+ renderPipelineDescriptor.vertex.module = GetTestVertexShaderModule();
+
+ renderPipelineDescriptor.cFragment.module = utils::CreateShaderModule(device, R"(
+ @group(0) @binding(0) var sampler0 : sampler;
+ @group(0) @binding(1) var sampler1 : sampler;
+ @group(0) @binding(2) var sampler2 : sampler;
+ @group(0) @binding(3) var lumaTexture : texture_2d<f32>;
+ @group(0) @binding(4) var chromaTexture : texture_2d<f32>;
+ @group(0) @binding(5) var alphaTexture : texture_2d<f32>;
+
+ @fragment
+ fn main(@location(0) texCoord : vec2f) -> @location(0) vec4f {
+ let y : f32 = textureSample(lumaTexture, sampler0, texCoord).r;
+ let u : f32 = textureSample(chromaTexture, sampler1, texCoord).r;
+ let v : f32 = textureSample(chromaTexture, sampler1, texCoord).g;
+ let a : f32 = textureSample(alphaTexture, sampler2, texCoord).r;
+ return vec4f(y, u, v, a);
+ })");
+
+ utils::BasicRenderPass renderPass = utils::CreateBasicRenderPass(
+ device, kYUVAImageDataWidthInTexels, kYUVAImageDataHeightInTexels);
+ renderPipelineDescriptor.cTargets[0].format = renderPass.colorFormat;
+
+ wgpu::RenderPipeline renderPipeline = device.CreateRenderPipeline(&renderPipelineDescriptor);
+
+ wgpu::Sampler sampler0 = device.CreateSampler();
+ wgpu::Sampler sampler1 = device.CreateSampler();
+ wgpu::Sampler sampler2 = device.CreateSampler();
+
+ wgpu::CommandEncoder encoder = device.CreateCommandEncoder();
+ {
+ wgpu::RenderPassEncoder pass = encoder.BeginRenderPass(&renderPass.renderPassInfo);
+ pass.SetPipeline(renderPipeline);
+ pass.SetBindGroup(0, utils::MakeBindGroup(device, renderPipeline.GetBindGroupLayout(0),
+ {{0, sampler0},
+ {1, sampler1},
+ {2, sampler2},
+ {3, lumaTextureView},
+ {4, chromaTextureView},
+ {5, alphaTextureView}}));
+ pass.Draw(6);
+ pass.End();
+ }
+
+ wgpu::CommandBuffer commands = encoder.Finish();
+ queue.Submit(1, &commands);
+
+ std::vector<uint8_t> expectedData =
+ GetTestTextureData<uint8_t>(/*isMultiPlane*/ false, /*isCheckerboard*/ true, hasAlpha);
+ std::vector<utils::RGBA8> expectedRGBA;
+ for (uint8_t i = 0; i < expectedData.size(); i += 4) {
+ expectedRGBA.push_back(
+ {expectedData[i], expectedData[i + 1], expectedData[i + 2], expectedData[i + 3]});
+ }
+
+ EXPECT_TEXTURE_EQ(expectedRGBA.data(), renderPass.color, {0, 0},
+ {kYUVAImageDataWidthInTexels, kYUVAImageDataHeightInTexels}, 0,
wgpu::TextureAspect::All, 0, kTolerance);
mBackend->DestroyVideoTextureForTest(std::move(platformTexture));
}
@@ -615,8 +882,8 @@
ASSERT_DEVICE_ERROR(device.CreateTexture(&descriptor));
}
-// Test texture view creation rules.
-TEST_P(VideoViewsValidationTests, CreateViewValidation) {
+// Test YUV texture view creation rules.
+TEST_P(VideoViewsValidationTests, CreateYUVViewValidation) {
std::unique_ptr<VideoViewsTestBackend::PlatformTexture> platformTexture =
mBackend->CreateVideoTextureForTest(GetFormat(), wgpu::TextureUsage::TextureBinding,
/*isCheckerboard*/ true,
@@ -626,6 +893,10 @@
mBackend->DestroyVideoTextureForTest(std::move(platformTexture));
GTEST_SKIP() << "Skipped because not supported.";
}
+ const bool hasAlpha = NumPlanes(GetFormat()) > 2;
+ if (hasAlpha) {
+ GTEST_SKIP() << "Skipped because format is not YUV.";
+ }
wgpu::TextureViewDescriptor viewDesc = {};
@@ -708,6 +979,129 @@
mBackend->DestroyVideoTextureForTest(std::move(platformTexture));
}
+// Test YUVA texture view creation rules.
+TEST_P(VideoViewsValidationTests, CreateYUVAViewValidation) {
+ std::unique_ptr<VideoViewsTestBackend::PlatformTexture> platformTexture =
+ mBackend->CreateVideoTextureForTest(GetFormat(), wgpu::TextureUsage::TextureBinding,
+ /*isCheckerboard*/ true,
+ /*initialized*/ true);
+ ASSERT_NE(platformTexture.get(), nullptr);
+ if (!platformTexture->CanWrapAsWGPUTexture()) {
+ mBackend->DestroyVideoTextureForTest(std::move(platformTexture));
+ GTEST_SKIP() << "Skipped because not supported.";
+ }
+ const bool hasAlpha = NumPlanes(GetFormat()) > 2;
+ if (!hasAlpha) {
+ GTEST_SKIP() << "Skipped because format is not YUVA.";
+ }
+
+ wgpu::TextureViewDescriptor viewDesc = {};
+
+ // Success case: Per plane view formats unspecified.
+ {
+ viewDesc.aspect = wgpu::TextureAspect::Plane0Only;
+ wgpu::TextureView plane0View = platformTexture->wgpuTexture.CreateView(&viewDesc);
+
+ viewDesc.aspect = wgpu::TextureAspect::Plane1Only;
+ wgpu::TextureView plane1View = platformTexture->wgpuTexture.CreateView(&viewDesc);
+
+ viewDesc.aspect = wgpu::TextureAspect::Plane2Only;
+ wgpu::TextureView plane2View = platformTexture->wgpuTexture.CreateView(&viewDesc);
+
+ ASSERT_NE(plane0View.Get(), nullptr);
+ ASSERT_NE(plane1View.Get(), nullptr);
+ ASSERT_NE(plane2View.Get(), nullptr);
+ }
+
+ // Success case: Per plane view formats specified and aspect.
+ {
+ viewDesc.aspect = wgpu::TextureAspect::Plane0Only;
+ viewDesc.format = GetPlaneFormat(0);
+ wgpu::TextureView plane0View = platformTexture->wgpuTexture.CreateView(&viewDesc);
+
+ viewDesc.aspect = wgpu::TextureAspect::Plane1Only;
+ viewDesc.format = GetPlaneFormat(1);
+ wgpu::TextureView plane1View = platformTexture->wgpuTexture.CreateView(&viewDesc);
+
+ viewDesc.aspect = wgpu::TextureAspect::Plane2Only;
+ viewDesc.format = GetPlaneFormat(2);
+ wgpu::TextureView plane2View = platformTexture->wgpuTexture.CreateView(&viewDesc);
+
+ ASSERT_NE(plane0View.Get(), nullptr);
+ ASSERT_NE(plane1View.Get(), nullptr);
+ ASSERT_NE(plane2View.Get(), nullptr);
+ }
+
+ // Some valid view format, but no plane specified.
+ viewDesc = {};
+ viewDesc.format = GetPlaneFormat(0);
+ ASSERT_DEVICE_ERROR(platformTexture->wgpuTexture.CreateView(&viewDesc));
+
+ // Some valid view format, but no plane specified.
+ viewDesc = {};
+ viewDesc.format = GetPlaneFormat(1);
+ ASSERT_DEVICE_ERROR(platformTexture->wgpuTexture.CreateView(&viewDesc));
+
+ // Some valid view format, but no plane specified.
+ viewDesc = {};
+ viewDesc.format = GetPlaneFormat(2);
+ ASSERT_DEVICE_ERROR(platformTexture->wgpuTexture.CreateView(&viewDesc));
+
+ // Correct plane index but incompatible view format.
+ viewDesc.format = wgpu::TextureFormat::R8Uint;
+ viewDesc.aspect = wgpu::TextureAspect::Plane0Only;
+ ASSERT_DEVICE_ERROR(platformTexture->wgpuTexture.CreateView(&viewDesc));
+
+ // Compatible view format but wrong plane index.
+ viewDesc.format = GetPlaneFormat(0);
+ viewDesc.aspect = wgpu::TextureAspect::Plane1Only;
+ ASSERT_DEVICE_ERROR(platformTexture->wgpuTexture.CreateView(&viewDesc));
+
+ // Compatible view format but wrong plane index.
+ viewDesc.format = GetPlaneFormat(1);
+ viewDesc.aspect = wgpu::TextureAspect::Plane2Only;
+ ASSERT_DEVICE_ERROR(platformTexture->wgpuTexture.CreateView(&viewDesc));
+
+ // Compatible view format but wrong aspect.
+ viewDesc.format = GetPlaneFormat(0);
+ viewDesc.aspect = wgpu::TextureAspect::All;
+ ASSERT_DEVICE_ERROR(platformTexture->wgpuTexture.CreateView(&viewDesc));
+
+ // Create a single plane texture.
+ wgpu::TextureDescriptor desc;
+ desc.format = wgpu::TextureFormat::RGBA8Unorm;
+ desc.dimension = wgpu::TextureDimension::e2D;
+ desc.usage = wgpu::TextureUsage::TextureBinding;
+ desc.size = {1, 1, 1};
+
+ wgpu::Texture texture = device.CreateTexture(&desc);
+
+ // Plane aspect specified with non-planar texture.
+ viewDesc.aspect = wgpu::TextureAspect::Plane0Only;
+ ASSERT_DEVICE_ERROR(texture.CreateView(&viewDesc));
+
+ viewDesc.aspect = wgpu::TextureAspect::Plane1Only;
+ ASSERT_DEVICE_ERROR(texture.CreateView(&viewDesc));
+
+ viewDesc.aspect = wgpu::TextureAspect::Plane2Only;
+ ASSERT_DEVICE_ERROR(texture.CreateView(&viewDesc));
+
+ // Planar views with non-planar texture.
+ viewDesc.aspect = wgpu::TextureAspect::Plane0Only;
+ viewDesc.format = GetPlaneFormat(0);
+ ASSERT_DEVICE_ERROR(texture.CreateView(&viewDesc));
+
+ viewDesc.aspect = wgpu::TextureAspect::Plane1Only;
+ viewDesc.format = GetPlaneFormat(1);
+ ASSERT_DEVICE_ERROR(texture.CreateView(&viewDesc));
+
+ viewDesc.aspect = wgpu::TextureAspect::Plane1Only;
+ viewDesc.format = GetPlaneFormat(2);
+ ASSERT_DEVICE_ERROR(texture.CreateView(&viewDesc));
+
+ mBackend->DestroyVideoTextureForTest(std::move(platformTexture));
+}
+
// Test copying from one multi-planar format into another fails.
TEST_P(VideoViewsValidationTests, T2TCopyAllAspectsFails) {
std::unique_ptr<VideoViewsTestBackend::PlatformTexture> platformTexture1 =
@@ -1007,10 +1401,10 @@
template <typename T>
void RenderToMultiplanarVideoTexture() {
// Create plane texture initialized with data.
- auto CreatePlaneTexWithData = [this](int planeIndex) -> wgpu::Texture {
- auto kSubsampleFactor = planeIndex == kYUVLumaPlaneIndex ? 1 : 2;
- wgpu::Extent3D size = {kYUVImageDataWidthInTexels / kSubsampleFactor,
- kYUVImageDataHeightInTexels / kSubsampleFactor, 1};
+ auto CreatePlaneTexWithData = [this](int planeIndex, bool hasAlpha) -> wgpu::Texture {
+ auto kSubsampleFactor = planeIndex == kYUVAChromaPlaneIndex ? 2 : 1;
+ wgpu::Extent3D size = {kYUVAImageDataWidthInTexels / kSubsampleFactor,
+ kYUVAImageDataHeightInTexels / kSubsampleFactor, 1};
// Create source texture with plane format
wgpu::TextureDescriptor planeTextureDesc;
@@ -1020,10 +1414,10 @@
wgpu::TextureUsage::CopyDst | wgpu::TextureUsage::TextureBinding;
wgpu::Texture planeTexture = device.CreateTexture(&planeTextureDesc);
- // Copy plane (Y/UV) data to the plane source texture.
+ // Copy plane (Y/UV/A) data to the plane source texture.
std::vector<T> planeSrcData = VideoViewsTestsBase::GetTestTextureDataWithPlaneIndex<T>(
planeIndex, kTextureBytesPerRowAlignment,
- kYUVImageDataHeightInTexels / kSubsampleFactor, false);
+ kYUVAImageDataHeightInTexels / kSubsampleFactor, false, hasAlpha);
wgpu::ImageCopyTexture imageCopyTexture = utils::CreateImageCopyTexture(planeTexture);
wgpu::TextureDataLayout textureDataLayout =
utils::CreateTextureDataLayout(0, kTextureBytesPerRowAlignment);
@@ -1034,12 +1428,20 @@
return planeTexture;
};
+ const bool hasAlpha = NumPlanes(GetFormat()) > 2;
+
// Create source texture with plane 0 format i.e. R8/R16Unorm.
- wgpu::Texture plane0Texture = CreatePlaneTexWithData(kYUVLumaPlaneIndex);
+ wgpu::Texture plane0Texture = CreatePlaneTexWithData(kYUVALumaPlaneIndex, hasAlpha);
ASSERT_NE(plane0Texture.Get(), nullptr);
// Create source texture with plane 1 format i.e. RG8/RG16Unorm.
- wgpu::Texture plane1Texture = CreatePlaneTexWithData(kYUVChromaPlaneIndex);
+ wgpu::Texture plane1Texture = CreatePlaneTexWithData(kYUVAChromaPlaneIndex, hasAlpha);
ASSERT_NE(plane1Texture.Get(), nullptr);
+ wgpu::Texture plane2Texture;
+ if (hasAlpha) {
+ // Create source texture with plane 2 format i.e. R8.
+ plane2Texture = CreatePlaneTexWithData(kYUVAAlphaPlaneIndex, hasAlpha);
+ ASSERT_NE(plane2Texture.Get(), nullptr);
+ }
// TODO(dawn:1337): Allow creating uninitialized texture for rendering.
// Create a video texture to be rendered into with multiplanar format.
@@ -1056,8 +1458,8 @@
// Perform plane operations for texting by creating render passes and comparing textures.
auto PerformPlaneOperations = [this](int planeIndex, wgpu::Texture destVideoWGPUTexture,
- wgpu::Texture planeTextureWithData) {
- auto kSubsampleFactor = planeIndex == kYUVLumaPlaneIndex ? 1 : 2;
+ wgpu::Texture planeTextureWithData, bool hasAlpha) {
+ auto kSubsampleFactor = planeIndex == kYUVAChromaPlaneIndex ? 2 : 1;
utils::ComboRenderPipelineDescriptor renderPipelineDescriptor;
renderPipelineDescriptor.vertex.module = GetTestVertexShaderModule();
@@ -1078,9 +1480,7 @@
// Create plane texture view from the multiplanar video texture.
wgpu::TextureViewDescriptor planeViewDesc;
planeViewDesc.format = GetPlaneFormat(planeIndex);
- planeViewDesc.aspect = (planeIndex == kYUVLumaPlaneIndex)
- ? wgpu::TextureAspect::Plane0Only
- : wgpu::TextureAspect::Plane1Only;
+ planeViewDesc.aspect = GetPlaneAspect(planeIndex);
wgpu::TextureView planeTextureView = destVideoWGPUTexture.CreateView(&planeViewDesc);
// Render pass operations for reading the source data from planeTexture view into
@@ -1099,8 +1499,8 @@
// format (i.e. R8/R16Unorm for Y and RG8/RG16Unorm for UV). This is needed as
// multiplanar textures do not support copy operations.
utils::BasicRenderPass basicRenderPass = utils::CreateBasicRenderPass(
- device, kYUVImageDataWidthInTexels / kSubsampleFactor,
- kYUVImageDataHeightInTexels / kSubsampleFactor, GetPlaneFormat(planeIndex));
+ device, kYUVAImageDataWidthInTexels / kSubsampleFactor,
+ kYUVAImageDataHeightInTexels / kSubsampleFactor, GetPlaneFormat(planeIndex));
wgpu::RenderPassEncoder secondPass =
encoder.BeginRenderPass(&basicRenderPass.renderPassInfo);
secondPass.SetPipeline(renderPipeline);
@@ -1115,18 +1515,24 @@
queue.Submit(1, &commands);
std::vector<T> expectedData = VideoViewsTestsBase::GetTestTextureDataWithPlaneIndex<T>(
- planeIndex, kYUVImageDataWidthInTexels * sizeof(T),
- kYUVImageDataHeightInTexels / kSubsampleFactor, false);
+ planeIndex, kYUVAImageDataWidthInTexels * sizeof(T),
+ kYUVAImageDataHeightInTexels / kSubsampleFactor, false, hasAlpha);
EXPECT_TEXTURE_EQ(expectedData.data(), basicRenderPass.color, {0, 0},
- {kYUVImageDataWidthInTexels / kSubsampleFactor,
- kYUVImageDataHeightInTexels / kSubsampleFactor},
+ {kYUVAImageDataWidthInTexels / kSubsampleFactor,
+ kYUVAImageDataHeightInTexels / kSubsampleFactor},
GetPlaneFormat(planeIndex));
};
// Perform operations for the Y plane.
- PerformPlaneOperations(kYUVLumaPlaneIndex, destVideoWGPUTexture, plane0Texture);
+ PerformPlaneOperations(kYUVALumaPlaneIndex, destVideoWGPUTexture, plane0Texture, hasAlpha);
// Perform operations for the UV plane.
- PerformPlaneOperations(kYUVChromaPlaneIndex, destVideoWGPUTexture, plane1Texture);
+ PerformPlaneOperations(kYUVAChromaPlaneIndex, destVideoWGPUTexture, plane1Texture,
+ hasAlpha);
+ if (hasAlpha) {
+ // Perform operations for the UV plane.
+ PerformPlaneOperations(kYUVAAlphaPlaneIndex, destVideoWGPUTexture, plane2Texture,
+ hasAlpha);
+ }
mBackend->DestroyVideoTextureForTest(std::move(destVideoTexture));
}
@@ -1180,7 +1586,7 @@
desc.format = wgpu::TextureFormat::RGBA8Unorm;
desc.dimension = wgpu::TextureDimension::e2D;
desc.usage = wgpu::TextureUsage::RenderAttachment;
- desc.size = {kYUVImageDataWidthInTexels / 2, kYUVImageDataHeightInTexels / 2, 1};
+ desc.size = {kYUVAImageDataWidthInTexels / 2, kYUVAImageDataHeightInTexels / 2, 1};
wgpu::Texture rgbaTexture = device.CreateTexture(&desc);
{
@@ -1206,7 +1612,8 @@
// Tests for rendering to a multiplanar video texture through its views.
TEST_P(VideoViewsRenderTargetTests, RenderToMultiplanarVideoTexture) {
- if (GetFormat() == wgpu::TextureFormat::R8BG8Biplanar420Unorm) {
+ if (GetFormat() == wgpu::TextureFormat::R8BG8Biplanar420Unorm ||
+ GetFormat() == wgpu::TextureFormat::R8BG8A8Triplanar420Unorm) {
RenderToMultiplanarVideoTexture<uint8_t>();
} else if (GetFormat() == wgpu::TextureFormat::R10X6BG10X6Biplanar420Unorm) {
RenderToMultiplanarVideoTexture<uint16_t>();
@@ -1242,6 +1649,7 @@
bool initialized = true) {
switch (format) {
case wgpu::TextureFormat::R8BG8Biplanar420Unorm:
+ case wgpu::TextureFormat::R8BG8A8Triplanar420Unorm:
return CreateMultiPlanarTextureImpl<uint8_t>(format, usage, isCheckerboard,
initialized);
case wgpu::TextureFormat::R10X6BG10X6Biplanar420Unorm:
@@ -1260,8 +1668,8 @@
bool initialized) {
wgpu::TextureDescriptor desc;
desc.format = format;
- desc.size = {VideoViewsTestsBase::kYUVImageDataWidthInTexels,
- VideoViewsTestsBase::kYUVImageDataHeightInTexels, 1};
+ desc.size = {VideoViewsTestsBase::kYUVAImageDataWidthInTexels,
+ VideoViewsTestsBase::kYUVAImageDataHeightInTexels, 1};
desc.usage = usage;
wgpu::DawnTextureInternalUsageDescriptor internalDesc;
@@ -1275,6 +1683,7 @@
if (initialized) {
size_t numPlanes = VideoViewsTestsBase::NumPlanes(format);
+ const bool hasAlpha = numPlanes > 2;
wgpu::DawnEncoderInternalUsageDescriptor encoderInternalDesc;
encoderInternalDesc.useInternalUsages = true;
@@ -1285,23 +1694,26 @@
for (size_t plane = 0; plane < numPlanes; ++plane) {
size_t bytesPerRow =
- VideoViewsTestsBase::kYUVImageDataWidthInTexels * sizeof(ComponentType);
+ VideoViewsTestsBase::kYUVAImageDataWidthInTexels * sizeof(ComponentType);
bytesPerRow = Align(bytesPerRow, 256);
wgpu::ImageCopyTexture copyDst =
utils::CreateImageCopyTexture(texture, 0, {0, 0, 0});
- wgpu::Extent3D copySize{VideoViewsTestsBase::kYUVImageDataWidthInTexels,
- VideoViewsTestsBase::kYUVImageDataHeightInTexels, 1};
+ wgpu::Extent3D copySize{VideoViewsTestsBase::kYUVAImageDataWidthInTexels,
+ VideoViewsTestsBase::kYUVAImageDataHeightInTexels, 1};
switch (plane) {
- case VideoViewsTestsBase::kYUVLumaPlaneIndex:
+ case VideoViewsTestsBase::kYUVALumaPlaneIndex:
copyDst.aspect = wgpu::TextureAspect::Plane0Only;
break;
- case VideoViewsTestsBase::kYUVChromaPlaneIndex:
+ case VideoViewsTestsBase::kYUVAChromaPlaneIndex:
copyDst.aspect = wgpu::TextureAspect::Plane1Only;
copySize.width /= 2;
copySize.height /= 2;
break;
+ case VideoViewsTestsBase::kYUVAAlphaPlaneIndex:
+ copyDst.aspect = wgpu::TextureAspect::Plane2Only;
+ break;
default:
DAWN_UNREACHABLE();
}
@@ -1315,8 +1727,8 @@
auto buffer = device.CreateBuffer(&bufferDesc);
std::vector<ComponentType> data = GetTestTextureDataWithPlaneIndex<ComponentType>(
- plane, bytesPerRow, VideoViewsTestsBase::kYUVImageDataHeightInTexels,
- isCheckerboard);
+ plane, bytesPerRow, VideoViewsTestsBase::kYUVAImageDataHeightInTexels,
+ isCheckerboard, hasAlpha);
memcpy(buffer.GetMappedRange(), data.data(), bufferDesc.size);
buffer.Unmap();
@@ -1420,8 +1832,8 @@
}
}
-// Tests sampling a multi-planar texture.
-TEST_P(VideoViewsExtendedUsagesTests, SamplingMultiPlanarTexture) {
+// Tests sampling a YUV multi-planar texture.
+TEST_P(VideoViewsExtendedUsagesTests, SamplingMultiPlanarYUVTexture) {
// TODO(crbug.com/dawn/1998): Failure on Intel's Vulkan device.
DAWN_SUPPRESS_TEST_IF(IsWindows() && IsVulkan() && IsIntel());
@@ -1430,6 +1842,11 @@
/*initialized*/ true);
EXPECT_NE(texture, nullptr);
+ const bool hasAlpha = NumPlanes(GetFormat()) > 2;
+ if (hasAlpha) {
+ GTEST_SKIP() << "Skipped because format is not YUV.";
+ }
+
wgpu::TextureViewDescriptor lumaViewDesc;
lumaViewDesc.format = GetPlaneFormat(0);
lumaViewDesc.aspect = wgpu::TextureAspect::Plane0Only;
@@ -1457,8 +1874,8 @@
})");
utils::BasicRenderPass renderPass =
- utils::CreateBasicRenderPass(device, kYUVImageDataWidthInTexels,
- kYUVImageDataHeightInTexels, wgpu::TextureFormat::RGBA8Unorm);
+ utils::CreateBasicRenderPass(device, kYUVAImageDataWidthInTexels,
+ kYUVAImageDataHeightInTexels, wgpu::TextureFormat::RGBA8Unorm);
renderPipelineDescriptor.cTargets[0].format = renderPass.colorFormat;
wgpu::RenderPipeline renderPipeline = device.CreateRenderPipeline(&renderPipelineDescriptor);
@@ -1480,14 +1897,97 @@
queue.Submit(1, &commands);
std::vector<uint8_t> expectedData =
- GetTestTextureData<uint8_t>(wgpu::TextureFormat::RGBA8Unorm, /*isCheckerboard*/ true);
+ GetTestTextureData<uint8_t>(/*isMultiPlane*/ false, /*isCheckerboard*/ true, hasAlpha);
std::vector<utils::RGBA8> expectedRGBA;
for (uint8_t i = 0; i < expectedData.size(); i += 3) {
expectedRGBA.push_back({expectedData[i], expectedData[i + 1], expectedData[i + 2], 0xFF});
}
EXPECT_TEXTURE_EQ(expectedRGBA.data(), renderPass.color, {0, 0},
- {kYUVImageDataWidthInTexels, kYUVImageDataHeightInTexels}, 0,
+ {kYUVAImageDataWidthInTexels, kYUVAImageDataHeightInTexels}, 0,
+ wgpu::TextureAspect::All, 0, kTolerance);
+}
+
+// Tests sampling a YUVA multi-planar texture.
+TEST_P(VideoViewsExtendedUsagesTests, SamplingMultiPlanarYUVATexture) {
+ auto texture = CreateMultiPlanarTexture(GetFormat(), wgpu::TextureUsage::TextureBinding,
+ /*isCheckerboard*/ true,
+ /*initialized*/ true);
+ EXPECT_NE(texture, nullptr);
+
+ const bool hasAlpha = NumPlanes(GetFormat()) > 2;
+ if (!hasAlpha) {
+ GTEST_SKIP() << "Skipped because format is not YUVA.";
+ }
+
+ wgpu::TextureViewDescriptor lumaViewDesc;
+ lumaViewDesc.format = GetPlaneFormat(0);
+ lumaViewDesc.aspect = wgpu::TextureAspect::Plane0Only;
+ wgpu::TextureView lumaTextureView = texture.CreateView(&lumaViewDesc);
+
+ wgpu::TextureViewDescriptor chromaViewDesc;
+ chromaViewDesc.format = GetPlaneFormat(1);
+ chromaViewDesc.aspect = wgpu::TextureAspect::Plane1Only;
+ wgpu::TextureView chromaTextureView = texture.CreateView(&chromaViewDesc);
+
+ wgpu::TextureViewDescriptor alphaViewDesc;
+ alphaViewDesc.format = GetPlaneFormat(2);
+ alphaViewDesc.aspect = wgpu::TextureAspect::Plane2Only;
+ wgpu::TextureView alphaTextureView = texture.CreateView(&alphaViewDesc);
+
+ utils::ComboRenderPipelineDescriptor renderPipelineDescriptor;
+ renderPipelineDescriptor.vertex.module = GetTestVertexShaderModule();
+
+ renderPipelineDescriptor.cFragment.module = utils::CreateShaderModule(device, R"(
+ @group(0) @binding(0) var sampler0 : sampler;
+ @group(0) @binding(1) var lumaTexture : texture_2d<f32>;
+ @group(0) @binding(2) var chromaTexture : texture_2d<f32>;
+ @group(0) @binding(3) var alphaTexture : texture_2d<f32>;
+
+ @fragment
+ fn main(@location(0) texCoord : vec2f) -> @location(0) vec4f {
+ let y : f32 = textureSample(lumaTexture, sampler0, texCoord).r;
+ let u : f32 = textureSample(chromaTexture, sampler0, texCoord).r;
+ let v : f32 = textureSample(chromaTexture, sampler0, texCoord).g;
+ let a : f32 = textureSample(alphaTexture, sampler0, texCoord).r;
+ return vec4f(y, u, v, a);
+ })");
+
+ utils::BasicRenderPass renderPass =
+ utils::CreateBasicRenderPass(device, kYUVAImageDataWidthInTexels,
+ kYUVAImageDataHeightInTexels, wgpu::TextureFormat::RGBA8Unorm);
+ renderPipelineDescriptor.cTargets[0].format = renderPass.colorFormat;
+
+ wgpu::RenderPipeline renderPipeline = device.CreateRenderPipeline(&renderPipelineDescriptor);
+
+ wgpu::Sampler sampler = device.CreateSampler();
+
+ wgpu::CommandEncoder encoder = device.CreateCommandEncoder();
+ {
+ wgpu::RenderPassEncoder pass = encoder.BeginRenderPass(&renderPass.renderPassInfo);
+ pass.SetPipeline(renderPipeline);
+ pass.SetBindGroup(0, utils::MakeBindGroup(device, renderPipeline.GetBindGroupLayout(0),
+ {{0, sampler},
+ {1, lumaTextureView},
+ {2, chromaTextureView},
+ {3, alphaTextureView}}));
+ pass.Draw(6);
+ pass.End();
+ }
+
+ wgpu::CommandBuffer commands = encoder.Finish();
+ queue.Submit(1, &commands);
+
+ std::vector<uint8_t> expectedData =
+ GetTestTextureData<uint8_t>(/*isMultiPlane*/ false, /*isCheckerboard*/ true, hasAlpha);
+ std::vector<utils::RGBA8> expectedRGBA;
+ for (uint8_t i = 0; i < expectedData.size(); i += 4) {
+ expectedRGBA.push_back(
+ {expectedData[i], expectedData[i + 1], expectedData[i + 2], expectedData[i + 3]});
+ }
+
+ EXPECT_TEXTURE_EQ(expectedRGBA.data(), renderPass.color, {0, 0},
+ {kYUVAImageDataWidthInTexels, kYUVAImageDataHeightInTexels}, 0,
wgpu::TextureAspect::All, 0, kTolerance);
}
@@ -1495,6 +1995,7 @@
TEST_P(VideoViewsExtendedUsagesTests, T2BCopyPlaneAspectsSucceeds) {
switch (GetFormat()) {
case wgpu::TextureFormat::R8BG8Biplanar420Unorm:
+ case wgpu::TextureFormat::R8BG8A8Triplanar420Unorm:
RunT2BCopyPlaneAspectsTest<uint8_t>();
break;
case wgpu::TextureFormat::R10X6BG10X6Biplanar420Unorm:
@@ -1522,6 +2023,8 @@
wgpu::Extent3D copySize = {1, 1, 1};
+ const bool hasAlpha = NumPlanes(GetFormat()) > 2;
+
// Plane0
wgpu::ImageCopyTexture copySrc =
utils::CreateImageCopyTexture(srcTexture, 0, {0, 0, 0}, wgpu::TextureAspect::Plane0Only);
@@ -1529,8 +2032,8 @@
{
std::vector<ComponentType> expectedData =
VideoViewsTestsBase::GetTestTextureDataWithPlaneIndex<ComponentType>(
- kYUVLumaPlaneIndex, kYUVImageDataWidthInTexels * sizeof(ComponentType),
- kYUVImageDataHeightInTexels, false);
+ kYUVALumaPlaneIndex, kYUVAImageDataWidthInTexels * sizeof(ComponentType),
+ kYUVAImageDataHeightInTexels, false, hasAlpha);
wgpu::CommandEncoder encoder = device.CreateCommandEncoder();
encoder.CopyTextureToBuffer(©Src, ©Dst, ©Size);
@@ -1551,8 +2054,8 @@
{
std::vector<ComponentType> expectedData =
VideoViewsTestsBase::GetTestTextureDataWithPlaneIndex<ComponentType>(
- kYUVChromaPlaneIndex, kYUVImageDataWidthInTexels * sizeof(ComponentType),
- kYUVImageDataHeightInTexels / 2, false);
+ kYUVAChromaPlaneIndex, kYUVAImageDataWidthInTexels * sizeof(ComponentType),
+ kYUVAImageDataHeightInTexels / 2, false, hasAlpha);
wgpu::CommandEncoder encoder = device.CreateCommandEncoder();
encoder.CopyTextureToBuffer(©Src, ©Dst, ©Size);
@@ -1566,6 +2069,30 @@
EXPECT_BUFFER_U8_RANGE_EQ(expectedUVDataAsU8, dstBuffer, 0, sizeof(expectedUVDataAsU8));
}
+
+ if (hasAlpha) {
+ // Plane2
+ copySrc = utils::CreateImageCopyTexture(srcTexture, 0, {0, 0, 0},
+ wgpu::TextureAspect::Plane2Only);
+ {
+ std::vector<ComponentType> expectedData =
+ VideoViewsTestsBase::GetTestTextureDataWithPlaneIndex<ComponentType>(
+ kYUVAAlphaPlaneIndex, kYUVAImageDataWidthInTexels * sizeof(ComponentType),
+ kYUVAImageDataHeightInTexels, false, hasAlpha);
+
+ wgpu::CommandEncoder encoder = device.CreateCommandEncoder();
+ encoder.CopyTextureToBuffer(©Src, ©Dst, ©Size);
+
+ auto cmdBuffer = encoder.Finish();
+ device.GetQueue().Submit(1, &cmdBuffer);
+
+ // Convert 1st pixel's alpha component to array of 8 bits bytes.
+ uint8_t expectedADataAsU8[sizeof(ComponentType)];
+ memcpy(expectedADataAsU8, expectedData.data(), sizeof(expectedADataAsU8));
+
+ EXPECT_BUFFER_U8_RANGE_EQ(expectedADataAsU8, dstBuffer, 0, sizeof(expectedADataAsU8));
+ }
+ }
}
// Test copying from a multi-planar format to a buffer fails if texture aspect is not single plane.
@@ -1638,7 +2165,8 @@
{D3D11Backend(), D3D12Backend(), MetalBackend(), OpenGLBackend(),
OpenGLESBackend(), VulkanBackend()},
{wgpu::TextureFormat::R8BG8Biplanar420Unorm,
- wgpu::TextureFormat::R10X6BG10X6Biplanar420Unorm});
+ wgpu::TextureFormat::R10X6BG10X6Biplanar420Unorm,
+ wgpu::TextureFormat::R8BG8A8Triplanar420Unorm});
} // anonymous namespace
} // namespace dawn
diff --git a/src/dawn/tests/end2end/VideoViewsTests.h b/src/dawn/tests/end2end/VideoViewsTests.h
index 5a57e1f..404fac0 100644
--- a/src/dawn/tests/end2end/VideoViewsTests.h
+++ b/src/dawn/tests/end2end/VideoViewsTests.h
@@ -72,41 +72,47 @@
class VideoViewsTestsBase : public DawnTestWithParams<Params> {
public:
- // The width and height in texels are 4 for all YUV formats.
- static constexpr uint32_t kYUVImageDataWidthInTexels = 4;
- static constexpr uint32_t kYUVImageDataHeightInTexels = 4;
+ // The width and height in texels are 4 for all YUVA formats.
+ static constexpr uint32_t kYUVAImageDataWidthInTexels = 4;
+ static constexpr uint32_t kYUVAImageDataHeightInTexels = 4;
- static constexpr size_t kYUVLumaPlaneIndex = 0;
- static constexpr size_t kYUVChromaPlaneIndex = 1;
+ static constexpr size_t kYUVALumaPlaneIndex = 0;
+ static constexpr size_t kYUVAChromaPlaneIndex = 1;
+ static constexpr size_t kYUVAAlphaPlaneIndex = 2;
- // RGB colors converted into YUV (per plane), for testing.
- // RGB colors are mapped to the BT.601 definition of luma.
+ // RGBA colors converted into YUVA (per plane), for testing.
+ // RGBA colors are mapped to the BT.601 definition of luma.
// https://docs.microsoft.com/en-us/windows/win32/medfound/about-yuv-video
- static constexpr std::array<dawn::utils::RGBA8, 2> kYellowYUVColor = {
- dawn::utils::RGBA8{210, 0, 0, 0xFF}, // Y
- dawn::utils::RGBA8{16, 146, 0, 0xFF}}; // UV
+ static constexpr std::array<dawn::utils::RGBA8, 3> kYellowYUVAColor = {
+ dawn::utils::RGBA8{210, 0, 0, 0xFF}, // Y
+ dawn::utils::RGBA8{16, 146, 0, 0xFF}, // UV
+ dawn::utils::RGBA8{63, 0, 0, 0xFF}}; // A
- static constexpr std::array<dawn::utils::RGBA8, 2> kWhiteYUVColor = {
- dawn::utils::RGBA8{235, 0, 0, 0xFF}, // Y
- dawn::utils::RGBA8{128, 128, 0, 0xFF}}; // UV
+ static constexpr std::array<dawn::utils::RGBA8, 3> kWhiteYUVAColor = {
+ dawn::utils::RGBA8{235, 0, 0, 0xFF}, // Y
+ dawn::utils::RGBA8{128, 128, 0, 0xFF}, // UV
+ dawn::utils::RGBA8{127, 0, 0, 0xFF}}; // A
- static constexpr std::array<dawn::utils::RGBA8, 2> kBlueYUVColor = {
- dawn::utils::RGBA8{41, 0, 0, 0xFF}, // Y
- dawn::utils::RGBA8{240, 110, 0, 0xFF}}; // UV
+ static constexpr std::array<dawn::utils::RGBA8, 3> kBlueYUVAColor = {
+ dawn::utils::RGBA8{41, 0, 0, 0xFF}, // Y
+ dawn::utils::RGBA8{240, 110, 0, 0xFF}, // UV
+ dawn::utils::RGBA8{191, 0, 0, 0xFF}}; // A
- static constexpr std::array<dawn::utils::RGBA8, 2> kRedYUVColor = {
- dawn::utils::RGBA8{81, 0, 0, 0xFF}, // Y
- dawn::utils::RGBA8{90, 240, 0, 0xFF}}; // UV
+ static constexpr std::array<dawn::utils::RGBA8, 3> kRedYUVAColor = {
+ dawn::utils::RGBA8{81, 0, 0, 0xFF}, // Y
+ dawn::utils::RGBA8{90, 240, 0, 0xFF}, // UV
+ dawn::utils::RGBA8{255, 0, 0, 0xFF}}; // A
static constexpr dawn::utils::RGBA8 kTolerance{1, 1, 1, 0};
template <typename T>
- static std::vector<T> GetTestTextureData(wgpu::TextureFormat format, bool isCheckerboard);
+ static std::vector<T> GetTestTextureData(bool isMultiPlane, bool isCheckerboard, bool hasAlpha);
template <typename T>
static std::vector<T> GetTestTextureDataWithPlaneIndex(size_t planeIndex,
size_t bytesPerRow,
size_t height,
- bool isCheckerboard);
+ bool isCheckerboard,
+ bool hasAlpha);
static uint32_t NumPlanes(wgpu::TextureFormat format);
static std::array<Format, 2> PlaneFormats(Format textureFormat);
@@ -115,14 +121,17 @@
std::vector<wgpu::FeatureName> GetRequiredFeatures() override;
bool IsMultiPlanarFormatsSupported() const;
bool IsMultiPlanarFormatP010Supported() const;
+ bool IsMultiPlanarFormatNv12aSupported() const;
bool IsNorm16TextureFormatsSupported() const;
wgpu::ShaderModule GetTestVertexShaderModule() const;
wgpu::TextureFormat GetFormat() const;
wgpu::TextureFormat GetPlaneFormat(int plane) const;
+ wgpu::TextureAspect GetPlaneAspect(int plane) const;
bool IsFormatSupported() const;
bool mIsMultiPlanarFormatsSupported = false;
bool mIsMultiPlanarFormatP010Supported = false;
+ bool mIsMultiPlanarFormatNv12aSupported = false;
bool mIsNorm16TextureFormatsSupported = false;
};
diff --git a/src/dawn/tests/end2end/VideoViewsTests_gbm.cpp b/src/dawn/tests/end2end/VideoViewsTests_gbm.cpp
index de95178..c0fc649 100644
--- a/src/dawn/tests/end2end/VideoViewsTests_gbm.cpp
+++ b/src/dawn/tests/end2end/VideoViewsTests_gbm.cpp
@@ -156,8 +156,8 @@
// of I915_FORMAT_MOD_Y_TILED.
flags |= GBM_BO_USE_SW_WRITE_RARELY;
}
- gbm_bo* gbmBo = gbm_bo_create(mGbmDevice, VideoViewsTestsBase::kYUVImageDataWidthInTexels,
- VideoViewsTestsBase::kYUVImageDataHeightInTexels,
+ gbm_bo* gbmBo = gbm_bo_create(mGbmDevice, VideoViewsTestsBase::kYUVAImageDataWidthInTexels,
+ VideoViewsTestsBase::kYUVAImageDataHeightInTexels,
GetGbmBoFormat(format), flags);
if (gbmBo == nullptr) {
return nullptr;
@@ -166,18 +166,19 @@
if (initialized) {
void* mapHandle = nullptr;
uint32_t strideBytes = 0;
- void* addr = gbm_bo_map(gbmBo, 0, 0, VideoViewsTestsBase::kYUVImageDataWidthInTexels,
- VideoViewsTestsBase::kYUVImageDataHeightInTexels,
+ void* addr = gbm_bo_map(gbmBo, 0, 0, VideoViewsTestsBase::kYUVAImageDataWidthInTexels,
+ VideoViewsTestsBase::kYUVAImageDataHeightInTexels,
GBM_BO_TRANSFER_WRITE, &strideBytes, &mapHandle);
EXPECT_NE(addr, nullptr);
- std::vector<uint8_t> initialData =
- VideoViewsTestsBase::GetTestTextureData<uint8_t>(format, isCheckerboard);
+ std::vector<uint8_t> initialData = VideoViewsTestsBase::GetTestTextureData<uint8_t>(
+ /*isMultiPlane*/ true, isCheckerboard,
+ /*hasAlpha*/ false);
uint8_t* srcBegin = initialData.data();
uint8_t* srcEnd = srcBegin + initialData.size();
uint8_t* dstBegin = static_cast<uint8_t*>(addr);
- for (; srcBegin < srcEnd; srcBegin += VideoViewsTestsBase::kYUVImageDataWidthInTexels,
+ for (; srcBegin < srcEnd; srcBegin += VideoViewsTestsBase::kYUVAImageDataWidthInTexels,
dstBegin += strideBytes) {
- std::memcpy(dstBegin, srcBegin, VideoViewsTestsBase::kYUVImageDataWidthInTexels);
+ std::memcpy(dstBegin, srcBegin, VideoViewsTestsBase::kYUVAImageDataWidthInTexels);
}
gbm_bo_unmap(gbmBo, mapHandle);
@@ -187,8 +188,8 @@
textureDesc.format = format;
textureDesc.dimension = wgpu::TextureDimension::e2D;
textureDesc.usage = usage;
- textureDesc.size = {VideoViewsTestsBase::kYUVImageDataWidthInTexels,
- VideoViewsTestsBase::kYUVImageDataHeightInTexels, 1};
+ textureDesc.size = {VideoViewsTestsBase::kYUVAImageDataWidthInTexels,
+ VideoViewsTestsBase::kYUVAImageDataHeightInTexels, 1};
wgpu::DawnTextureInternalUsageDescriptor internalDesc;
internalDesc.internalUsage = wgpu::TextureUsage::CopySrc;
diff --git a/src/dawn/tests/end2end/VideoViewsTests_mac.cpp b/src/dawn/tests/end2end/VideoViewsTests_mac.cpp
index 43ce3b3..e4a3fe8 100644
--- a/src/dawn/tests/end2end/VideoViewsTests_mac.cpp
+++ b/src/dawn/tests/end2end/VideoViewsTests_mac.cpp
@@ -64,8 +64,8 @@
bool isCheckerboard,
bool initialized) override {
IOSurfaceRef surface =
- CreateMultiPlanarIOSurface(format, VideoViewsTestsBase::kYUVImageDataWidthInTexels,
- VideoViewsTestsBase::kYUVImageDataHeightInTexels);
+ CreateMultiPlanarIOSurface(format, VideoViewsTestsBase::kYUVAImageDataWidthInTexels,
+ VideoViewsTestsBase::kYUVAImageDataHeightInTexels);
if (initialized) {
const size_t numPlanes = VideoViewsTestsBase::NumPlanes(format);
@@ -78,13 +78,15 @@
std::vector<uint16_t> data =
VideoViewsTestsBase::GetTestTextureDataWithPlaneIndex<uint16_t>(
plane, IOSurfaceGetBytesPerRowOfPlane(surface, plane),
- IOSurfaceGetHeightOfPlane(surface, plane), isCheckerboard);
+ IOSurfaceGetHeightOfPlane(surface, plane), isCheckerboard,
+ /*hasAlpha*/ false);
memcpy(pointer, data.data(), data.size() * 2);
} else {
std::vector<uint8_t> data =
VideoViewsTestsBase::GetTestTextureDataWithPlaneIndex<uint8_t>(
plane, IOSurfaceGetBytesPerRowOfPlane(surface, plane),
- IOSurfaceGetHeightOfPlane(surface, plane), isCheckerboard);
+ IOSurfaceGetHeightOfPlane(surface, plane), isCheckerboard,
+ /*hasAlpha*/ format == wgpu::TextureFormat::R8BG8A8Triplanar420Unorm);
memcpy(pointer, data.data(), data.size());
}
}
@@ -95,8 +97,8 @@
textureDesc.format = format;
textureDesc.dimension = wgpu::TextureDimension::e2D;
textureDesc.usage = usage;
- textureDesc.size = {VideoViewsTestsBase::kYUVImageDataWidthInTexels,
- VideoViewsTestsBase::kYUVImageDataHeightInTexels, 1};
+ textureDesc.size = {VideoViewsTestsBase::kYUVAImageDataWidthInTexels,
+ VideoViewsTestsBase::kYUVAImageDataHeightInTexels, 1};
wgpu::DawnTextureInternalUsageDescriptor internalDesc;
internalDesc.internalUsage = wgpu::TextureUsage::CopySrc;
@@ -129,7 +131,8 @@
// static
std::vector<Format> VideoViewsTestBackend::Formats() {
return {wgpu::TextureFormat::R8BG8Biplanar420Unorm,
- wgpu::TextureFormat::R10X6BG10X6Biplanar420Unorm};
+ wgpu::TextureFormat::R10X6BG10X6Biplanar420Unorm,
+ wgpu::TextureFormat::R8BG8A8Triplanar420Unorm};
}
// static
diff --git a/src/dawn/tests/end2end/VideoViewsTests_win.cpp b/src/dawn/tests/end2end/VideoViewsTests_win.cpp
index 988a2be..fb7c493 100644
--- a/src/dawn/tests/end2end/VideoViewsTests_win.cpp
+++ b/src/dawn/tests/end2end/VideoViewsTests_win.cpp
@@ -115,13 +115,13 @@
textureDesc.format = format;
textureDesc.dimension = wgpu::TextureDimension::e2D;
textureDesc.usage = usage;
- textureDesc.size = {VideoViewsTestsBase::kYUVImageDataWidthInTexels,
- VideoViewsTestsBase::kYUVImageDataHeightInTexels, 1};
+ textureDesc.size = {VideoViewsTestsBase::kYUVAImageDataWidthInTexels,
+ VideoViewsTestsBase::kYUVAImageDataHeightInTexels, 1};
// Create a DX11 texture with data then wrap it in a shared handle.
D3D11_TEXTURE2D_DESC d3dDescriptor;
- d3dDescriptor.Width = VideoViewsTestsBase::kYUVImageDataWidthInTexels;
- d3dDescriptor.Height = VideoViewsTestsBase::kYUVImageDataHeightInTexels;
+ d3dDescriptor.Width = VideoViewsTestsBase::kYUVAImageDataWidthInTexels;
+ d3dDescriptor.Height = VideoViewsTestsBase::kYUVAImageDataHeightInTexels;
d3dDescriptor.MipLevels = 1;
d3dDescriptor.ArraySize = 1;
d3dDescriptor.Format = GetDXGITextureFormat(format);
@@ -133,15 +133,18 @@
d3dDescriptor.MiscFlags = D3D11_RESOURCE_MISC_SHARED_NTHANDLE | D3D11_RESOURCE_MISC_SHARED;
D3D11_SUBRESOURCE_DATA subres;
- subres.SysMemPitch = VideoViewsTestsBase::kYUVImageDataWidthInTexels;
+ subres.SysMemPitch = VideoViewsTestsBase::kYUVAImageDataWidthInTexels;
std::variant<std::vector<uint8_t>, std::vector<uint16_t>> initialData;
if (format == wgpu::TextureFormat::R10X6BG10X6Biplanar420Unorm) {
- initialData = VideoViewsTestsBase::GetTestTextureData<uint16_t>(format, isCheckerboard);
+ initialData = VideoViewsTestsBase::GetTestTextureData<uint16_t>(
+ /*isMultiPlane*/ true, isCheckerboard, /*hasAlpha*/ false);
subres.pSysMem = std::get<1>(initialData).data();
subres.SysMemPitch *= 2;
} else {
- initialData = VideoViewsTestsBase::GetTestTextureData<uint8_t>(format, isCheckerboard);
+ initialData = VideoViewsTestsBase::GetTestTextureData<uint8_t>(
+ /*isMultiPlane*/ true, isCheckerboard,
+ /*hasAlpha*/ false);
subres.pSysMem = std::get<0>(initialData).data();
}
diff --git a/src/dawn/utils/TextureUtils.cpp b/src/dawn/utils/TextureUtils.cpp
index 037bde8..9714f7b 100644
--- a/src/dawn/utils/TextureUtils.cpp
+++ b/src/dawn/utils/TextureUtils.cpp
@@ -182,6 +182,7 @@
switch (textureFormat) {
case wgpu::TextureFormat::R8BG8Biplanar420Unorm:
case wgpu::TextureFormat::R10X6BG10X6Biplanar420Unorm:
+ case wgpu::TextureFormat::R8BG8A8Triplanar420Unorm:
return true;
default:
return false;
@@ -396,6 +397,7 @@
// Block size of a multi-planar format depends on aspect.
case wgpu::TextureFormat::R8BG8Biplanar420Unorm:
case wgpu::TextureFormat::R10X6BG10X6Biplanar420Unorm:
+ case wgpu::TextureFormat::R8BG8A8Triplanar420Unorm:
case wgpu::TextureFormat::Undefined:
break;
@@ -520,6 +522,7 @@
// Block size of a multi-planar format depends on aspect.
case wgpu::TextureFormat::R8BG8Biplanar420Unorm:
case wgpu::TextureFormat::R10X6BG10X6Biplanar420Unorm:
+ case wgpu::TextureFormat::R8BG8A8Triplanar420Unorm:
case wgpu::TextureFormat::Undefined:
break;
@@ -644,6 +647,7 @@
// Block size of a multi-planar format depends on aspect.
case wgpu::TextureFormat::R8BG8Biplanar420Unorm:
case wgpu::TextureFormat::R10X6BG10X6Biplanar420Unorm:
+ case wgpu::TextureFormat::R8BG8A8Triplanar420Unorm:
case wgpu::TextureFormat::Undefined:
break;
diff --git a/src/dawn/wire/SupportedFeatures.cpp b/src/dawn/wire/SupportedFeatures.cpp
index 0ac7afb..5a3f191 100644
--- a/src/dawn/wire/SupportedFeatures.cpp
+++ b/src/dawn/wire/SupportedFeatures.cpp
@@ -67,6 +67,7 @@
case WGPUFeatureName_DawnMultiPlanarFormats:
case WGPUFeatureName_MultiPlanarFormatExtendedUsages:
case WGPUFeatureName_MultiPlanarFormatP010:
+ case WGPUFeatureName_MultiPlanarFormatNv12a:
case WGPUFeatureName_MultiPlanarRenderTargets:
case WGPUFeatureName_ChromiumExperimentalDp4a:
case WGPUFeatureName_ShaderF16: