Reland: Enable YCbCr sampler support on platforms other than Android
Previously YCbCr Vulkan samplers were supported only on Android for external images, while Vulkan requires YCbCr sampler for I420 YUV image formats such as VK_FORMAT_G8_B8_R8_3PLANE_420_UNORM, VK_FORMAT_G8_B8R8_2PLANE_420_UNORM. This CL: - Adds VK_FORMAT_G8_B8_R8_3PLANE_420_UNORM and VK_FORMAT_G8_B8R8_2PLANE_420_UNORM as supported Vulkan formats - Updates GrVkYcbcrConversionInfo to add fFormat field and allow fExternalFormat=0. - Removes assertions format=VK_FORMAT_UNDEFINED for all images that have ycbcr info. Bug: chromium:981022 Change-Id: Id4d81b20d9fda4d9ad0831f77e6025eed3db2bfd Reviewed-on: https://skia-review.googlesource.com/c/skia/+/233776 Commit-Queue: Greg Daniel <egdaniel@google.com> Reviewed-by: Greg Daniel <egdaniel@google.com> Auto-Submit: Sergey Ulanov <sergeyu@chromium.org>
This commit is contained in:
parent
c2da70fbef
commit
2739fd2f09
@ -21,3 +21,6 @@ Milestone 78
|
||||
* Modify GrBackendFormat getters to not return internal pointers. Use an enum class for GL formats.
|
||||
|
||||
* Expose GrContext::dump() when SK_ENABLE_DUMP_GPU is defined.
|
||||
|
||||
* Vulkan backend now supports YCbCr sampler for I420 Vulkan images that are not
|
||||
backed by external images.
|
||||
|
@ -303,6 +303,7 @@ tests_sources = [
|
||||
"$_tests/VkPriorityExtensionTest.cpp",
|
||||
"$_tests/VkProtectedContextTest.cpp",
|
||||
"$_tests/VkWrapTests.cpp",
|
||||
"$_tests/VkYcbcrSamplerTest.cpp",
|
||||
"$_tests/VptrTest.cpp",
|
||||
"$_tests/WindowRectanglesTest.cpp",
|
||||
"$_tests/WritePixelsTest.cpp",
|
||||
|
@ -69,9 +69,6 @@ public:
|
||||
return GrBackendFormat(format, GrVkYcbcrConversionInfo());
|
||||
}
|
||||
|
||||
// This is used for external textures and the VkFormat is assumed to be VK_FORMAT_UNDEFINED.
|
||||
// This call is only supported on Android since the GrVkYcbcrConvesionInfo contains an android
|
||||
// external format.
|
||||
static GrBackendFormat MakeVk(const GrVkYcbcrConversionInfo& ycbcrInfo);
|
||||
|
||||
#ifdef SK_DAWN
|
||||
|
@ -67,14 +67,37 @@ private:
|
||||
// object for an VkExternalFormatANDROID.
|
||||
struct GrVkYcbcrConversionInfo {
|
||||
GrVkYcbcrConversionInfo()
|
||||
: fYcbcrModel(VK_SAMPLER_YCBCR_MODEL_CONVERSION_RGB_IDENTITY)
|
||||
: fFormat(VK_FORMAT_UNDEFINED)
|
||||
, fExternalFormat(0)
|
||||
, fYcbcrModel(VK_SAMPLER_YCBCR_MODEL_CONVERSION_RGB_IDENTITY)
|
||||
, fYcbcrRange(VK_SAMPLER_YCBCR_RANGE_ITU_FULL)
|
||||
, fXChromaOffset(VK_CHROMA_LOCATION_COSITED_EVEN)
|
||||
, fYChromaOffset(VK_CHROMA_LOCATION_COSITED_EVEN)
|
||||
, fChromaFilter(VK_FILTER_NEAREST)
|
||||
, fForceExplicitReconstruction(false)
|
||||
, fExternalFormat(0)
|
||||
, fExternalFormatFeatures(0) {}
|
||||
, fForceExplicitReconstruction(false) {}
|
||||
|
||||
GrVkYcbcrConversionInfo(VkFormat format,
|
||||
int64_t externalFormat,
|
||||
VkSamplerYcbcrModelConversion ycbcrModel,
|
||||
VkSamplerYcbcrRange ycbcrRange,
|
||||
VkChromaLocation xChromaOffset,
|
||||
VkChromaLocation yChromaOffset,
|
||||
VkFilter chromaFilter,
|
||||
VkBool32 forceExplicitReconstruction,
|
||||
VkFormatFeatureFlags formatFeatures)
|
||||
: fFormat(format)
|
||||
, fExternalFormat(externalFormat)
|
||||
, fYcbcrModel(ycbcrModel)
|
||||
, fYcbcrRange(ycbcrRange)
|
||||
, fXChromaOffset(xChromaOffset)
|
||||
, fYChromaOffset(yChromaOffset)
|
||||
, fChromaFilter(chromaFilter)
|
||||
, fForceExplicitReconstruction(forceExplicitReconstruction)
|
||||
, fFormatFeatures(formatFeatures) {
|
||||
SkASSERT(fYcbcrModel != VK_SAMPLER_YCBCR_MODEL_CONVERSION_RGB_IDENTITY);
|
||||
// Either format or externalFormat must be specified.
|
||||
SkASSERT((fFormat != VK_FORMAT_UNDEFINED) ^ (externalFormat != 0));
|
||||
}
|
||||
|
||||
GrVkYcbcrConversionInfo(VkSamplerYcbcrModelConversion ycbcrModel,
|
||||
VkSamplerYcbcrRange ycbcrRange,
|
||||
@ -84,35 +107,35 @@ struct GrVkYcbcrConversionInfo {
|
||||
VkBool32 forceExplicitReconstruction,
|
||||
uint64_t externalFormat,
|
||||
VkFormatFeatureFlags externalFormatFeatures)
|
||||
: fYcbcrModel(ycbcrModel)
|
||||
, fYcbcrRange(ycbcrRange)
|
||||
, fXChromaOffset(xChromaOffset)
|
||||
, fYChromaOffset(yChromaOffset)
|
||||
, fChromaFilter(chromaFilter)
|
||||
, fForceExplicitReconstruction(forceExplicitReconstruction)
|
||||
, fExternalFormat(externalFormat)
|
||||
, fExternalFormatFeatures(externalFormatFeatures) {
|
||||
SkASSERT(fExternalFormat);
|
||||
}
|
||||
: GrVkYcbcrConversionInfo(VK_FORMAT_UNDEFINED, externalFormat, ycbcrModel, ycbcrRange,
|
||||
xChromaOffset, yChromaOffset, chromaFilter,
|
||||
forceExplicitReconstruction, externalFormatFeatures) {}
|
||||
|
||||
bool operator==(const GrVkYcbcrConversionInfo& that) const {
|
||||
// Invalid objects are not required to have all other fields intialized or matching.
|
||||
// Invalid objects are not required to have all other fields initialized or matching.
|
||||
if (!this->isValid() && !that.isValid()) {
|
||||
return true;
|
||||
}
|
||||
return this->fYcbcrModel == that.fYcbcrModel &&
|
||||
return this->fFormat == that.fFormat &&
|
||||
this->fExternalFormat == that.fExternalFormat &&
|
||||
this->fYcbcrModel == that.fYcbcrModel &&
|
||||
this->fYcbcrRange == that.fYcbcrRange &&
|
||||
this->fXChromaOffset == that.fXChromaOffset &&
|
||||
this->fYChromaOffset == that.fYChromaOffset &&
|
||||
this->fChromaFilter == that.fChromaFilter &&
|
||||
this->fForceExplicitReconstruction == that.fForceExplicitReconstruction &&
|
||||
this->fExternalFormat == that.fExternalFormat;
|
||||
// We don't check fExternalFormatFeatures here since all matching external formats must have
|
||||
// the same format features at least in terms of how they effect ycbcr sampler conversion.
|
||||
this->fForceExplicitReconstruction == that.fForceExplicitReconstruction;
|
||||
}
|
||||
bool operator!=(const GrVkYcbcrConversionInfo& that) const { return !(*this == that); }
|
||||
|
||||
bool isValid() const { return fExternalFormat != 0; }
|
||||
bool isValid() const { return fYcbcrModel != VK_SAMPLER_YCBCR_MODEL_CONVERSION_RGB_IDENTITY; }
|
||||
|
||||
// Format of the source image. Must be set to VK_FORMAT_UNDEFINED for external images or
|
||||
// a valid image format otherwise.
|
||||
VkFormat fFormat;
|
||||
|
||||
// The external format. Must be non-zero for external images, zero otherwise.
|
||||
// Should be compatible to be used in a VkExternalFormatANDROID struct.
|
||||
uint64_t fExternalFormat;
|
||||
|
||||
VkSamplerYcbcrModelConversion fYcbcrModel;
|
||||
VkSamplerYcbcrRange fYcbcrRange;
|
||||
@ -120,11 +143,10 @@ struct GrVkYcbcrConversionInfo {
|
||||
VkChromaLocation fYChromaOffset;
|
||||
VkFilter fChromaFilter;
|
||||
VkBool32 fForceExplicitReconstruction;
|
||||
// The external format should be compatible to be used in a VkExternalFormatANDROID struct
|
||||
uint64_t fExternalFormat;
|
||||
// The format features here should be those returned by a call to
|
||||
|
||||
// For external images format features here should be those returned by a call to
|
||||
// vkAndroidHardwareBufferFormatPropertiesANDROID
|
||||
VkFormatFeatureFlags fExternalFormatFeatures;
|
||||
VkFormatFeatureFlags fFormatFeatures;
|
||||
};
|
||||
|
||||
struct GrVkImageInfo {
|
||||
|
@ -138,7 +138,7 @@ GrBackendFormat GetBackendFormat(GrContext* context, AHardwareBuffer* hardwareBu
|
||||
ycbcrConversion.fYChromaOffset = hwbFormatProps.suggestedYChromaOffset;
|
||||
ycbcrConversion.fForceExplicitReconstruction = VK_FALSE;
|
||||
ycbcrConversion.fExternalFormat = hwbFormatProps.externalFormat;
|
||||
ycbcrConversion.fExternalFormatFeatures = hwbFormatProps.formatFeatures;
|
||||
ycbcrConversion.fFormatFeatures = hwbFormatProps.formatFeatures;
|
||||
if (VK_FORMAT_FEATURE_SAMPLED_IMAGE_YCBCR_CONVERSION_LINEAR_FILTER_BIT &
|
||||
hwbFormatProps.formatFeatures) {
|
||||
ycbcrConversion.fChromaFilter = VK_FILTER_LINEAR;
|
||||
|
@ -92,11 +92,8 @@ GrGLFormat GrBackendFormat::asGLFormat() const {
|
||||
}
|
||||
|
||||
GrBackendFormat GrBackendFormat::MakeVk(const GrVkYcbcrConversionInfo& ycbcrInfo) {
|
||||
#ifdef SK_BUILD_FOR_ANDROID
|
||||
return GrBackendFormat(VK_FORMAT_UNDEFINED, ycbcrInfo);
|
||||
#else
|
||||
return GrBackendFormat();
|
||||
#endif
|
||||
SkASSERT(ycbcrInfo.isValid());
|
||||
return GrBackendFormat(ycbcrInfo.fFormat, ycbcrInfo);
|
||||
}
|
||||
|
||||
GrBackendFormat::GrBackendFormat(VkFormat vkFormat, const GrVkYcbcrConversionInfo& ycbcrInfo)
|
||||
@ -109,7 +106,7 @@ GrBackendFormat::GrBackendFormat(VkFormat vkFormat, const GrVkYcbcrConversionInf
|
||||
, fTextureType(GrTextureType::k2D) {
|
||||
fVk.fFormat = vkFormat;
|
||||
fVk.fYcbcrConversionInfo = ycbcrInfo;
|
||||
if (fVk.fYcbcrConversionInfo.isValid()) {
|
||||
if (fVk.fYcbcrConversionInfo.isValid() && fVk.fYcbcrConversionInfo.fExternalFormat) {
|
||||
fTextureType = GrTextureType::kExternal;
|
||||
}
|
||||
}
|
||||
@ -564,7 +561,7 @@ GrBackendFormat GrBackendTexture::getBackendFormat() const {
|
||||
case GrBackendApi::kVulkan: {
|
||||
auto info = fVkInfo.snapImageInfo();
|
||||
if (info.fYcbcrConversionInfo.isValid()) {
|
||||
SkASSERT(info.fFormat == VK_FORMAT_UNDEFINED);
|
||||
SkASSERT(info.fFormat == info.fYcbcrConversionInfo.fFormat);
|
||||
return GrBackendFormat::MakeVk(info.fYcbcrConversionInfo);
|
||||
}
|
||||
return GrBackendFormat::MakeVk(info.fFormat);
|
||||
@ -848,7 +845,7 @@ GrBackendFormat GrBackendRenderTarget::getBackendFormat() const {
|
||||
case GrBackendApi::kVulkan: {
|
||||
auto info = fVkInfo.snapImageInfo();
|
||||
if (info.fYcbcrConversionInfo.isValid()) {
|
||||
SkASSERT(info.fFormat == VK_FORMAT_UNDEFINED);
|
||||
SkASSERT(info.fFormat == info.fYcbcrConversionInfo.fFormat);
|
||||
return GrBackendFormat::MakeVk(info.fYcbcrConversionInfo);
|
||||
}
|
||||
return GrBackendFormat::MakeVk(info.fFormat);
|
||||
|
@ -81,7 +81,6 @@ void GrPrimitiveProcessor::TextureSampler::reset(GrTextureType textureType,
|
||||
fTextureType = textureType;
|
||||
fConfig = config;
|
||||
fExtraSamplerKey = extraSamplerKey;
|
||||
SkASSERT(!fExtraSamplerKey || textureType == GrTextureType::kExternal);
|
||||
}
|
||||
|
||||
void GrPrimitiveProcessor::TextureSampler::reset(GrTextureType textureType,
|
||||
|
@ -71,7 +71,6 @@ static void add_sampler_keys(GrProcessorKeyBuilder* b, const GrFragmentProcessor
|
||||
uint32_t extraSamplerKey = gpu->getExtraSamplerKeyForProgram(
|
||||
sampler.samplerState(), sampler.proxy()->backendFormat());
|
||||
if (extraSamplerKey) {
|
||||
SkASSERT(sampler.proxy()->textureType() == GrTextureType::kExternal);
|
||||
// We first mark the normal sampler key with last bit to flag that it has an extra
|
||||
// sampler key. We then add both keys.
|
||||
SkASSERT((samplerKey & (1 << 31)) == 0);
|
||||
@ -95,7 +94,6 @@ static void add_sampler_keys(GrProcessorKeyBuilder* b, const GrPrimitiveProcesso
|
||||
sampler.textureType(), sampler.swizzle(), sampler.config(), caps);
|
||||
uint32_t extraSamplerKey = sampler.extraSamplerKey();
|
||||
if (extraSamplerKey) {
|
||||
SkASSERT(sampler.textureType() == GrTextureType::kExternal);
|
||||
// We first mark the normal sampler key with last bit to flag that it has an extra
|
||||
// sampler key. We then add both keys.
|
||||
SkASSERT((samplerKey & (1 << 31)) == 0);
|
||||
|
@ -333,18 +333,15 @@ void GrVkCaps::init(const GrContextOptions& contextOptions, const GrVkInterface*
|
||||
|
||||
auto ycbcrFeatures =
|
||||
get_extension_feature_struct<VkPhysicalDeviceSamplerYcbcrConversionFeatures>(
|
||||
features,
|
||||
VK_STRUCTURE_TYPE_PHYSICAL_DEVICE_SAMPLER_YCBCR_CONVERSION_FEATURES);
|
||||
features, VK_STRUCTURE_TYPE_PHYSICAL_DEVICE_SAMPLER_YCBCR_CONVERSION_FEATURES);
|
||||
if (ycbcrFeatures && ycbcrFeatures->samplerYcbcrConversion &&
|
||||
fSupportsAndroidHWBExternalMemory &&
|
||||
(physicalDeviceVersion >= VK_MAKE_VERSION(1, 1, 0) ||
|
||||
(extensions.hasExtension(VK_KHR_SAMPLER_YCBCR_CONVERSION_EXTENSION_NAME, 1) &&
|
||||
this->supportsMaintenance1() &&
|
||||
this->supportsBindMemory2() &&
|
||||
this->supportsMemoryRequirements2() &&
|
||||
this->supportsPhysicalDeviceProperties2()))) {
|
||||
this->supportsMaintenance1() && this->supportsBindMemory2() &&
|
||||
this->supportsMemoryRequirements2() && this->supportsPhysicalDeviceProperties2()))) {
|
||||
fSupportsYcbcrConversion = true;
|
||||
}
|
||||
|
||||
// We always push back the default GrVkYcbcrConversionInfo so that the case of no conversion
|
||||
// will return a key of 0.
|
||||
fYcbcrInfos.push_back(GrVkYcbcrConversionInfo());
|
||||
@ -648,6 +645,8 @@ static constexpr VkFormat kVkFormats[] = {
|
||||
VK_FORMAT_ETC2_R8G8B8_UNORM_BLOCK,
|
||||
VK_FORMAT_R16_UNORM,
|
||||
VK_FORMAT_R16G16_UNORM,
|
||||
VK_FORMAT_G8_B8_R8_3PLANE_420_UNORM,
|
||||
VK_FORMAT_G8_B8R8_2PLANE_420_UNORM,
|
||||
// Experimental (for Y416 and mutant P016/P010)
|
||||
VK_FORMAT_R16G16B16A16_UNORM,
|
||||
VK_FORMAT_R16G16_SFLOAT,
|
||||
@ -674,18 +673,13 @@ void GrVkCaps::initFormatTable(const GrVkInterface* interface, VkPhysicalDevice
|
||||
const VkPhysicalDeviceProperties& properties) {
|
||||
static_assert(SK_ARRAY_COUNT(kVkFormats) == GrVkCaps::kNumVkFormats,
|
||||
"Size of VkFormats array must match static value in header");
|
||||
for (size_t i = 0; i < SK_ARRAY_COUNT(kVkFormats); ++i) {
|
||||
VkFormat format = kVkFormats[i];
|
||||
if (!format_is_srgb(format) || fSRGBSupport) {
|
||||
fFormatTable[i].init(interface, physDev, properties, format);
|
||||
}
|
||||
}
|
||||
|
||||
// Go through all the formats and init their support surface and data GrColorTypes.
|
||||
|
||||
// Format: VK_FORMAT_R8G8B8A8_UNORM
|
||||
{
|
||||
auto& info = this->getFormatInfo(VK_FORMAT_R8G8B8A8_UNORM);
|
||||
info.init(interface, physDev, properties, VK_FORMAT_R8G8B8A8_UNORM);
|
||||
if (SkToBool(info.fOptimalFlags & FormatInfo::kTextureable_Flag)) {
|
||||
info.fColorTypeInfoCount = 2;
|
||||
info.fColorTypeInfos.reset(new ColorTypeInfo[info.fColorTypeInfoCount]());
|
||||
@ -709,6 +703,7 @@ void GrVkCaps::initFormatTable(const GrVkInterface* interface, VkPhysicalDevice
|
||||
// Format: VK_FORMAT_R8_UNORM
|
||||
{
|
||||
auto& info = this->getFormatInfo(VK_FORMAT_R8_UNORM);
|
||||
info.init(interface, physDev, properties, VK_FORMAT_R8_UNORM);
|
||||
if (SkToBool(info.fOptimalFlags & FormatInfo::kTextureable_Flag)) {
|
||||
info.fColorTypeInfoCount = 2;
|
||||
info.fColorTypeInfos.reset(new ColorTypeInfo[info.fColorTypeInfoCount]());
|
||||
@ -733,6 +728,7 @@ void GrVkCaps::initFormatTable(const GrVkInterface* interface, VkPhysicalDevice
|
||||
// Format: VK_FORMAT_B8G8R8A8_UNORM
|
||||
{
|
||||
auto& info = this->getFormatInfo(VK_FORMAT_B8G8R8A8_UNORM);
|
||||
info.init(interface, physDev, properties, VK_FORMAT_B8G8R8A8_UNORM);
|
||||
if (SkToBool(info.fOptimalFlags & FormatInfo::kTextureable_Flag)) {
|
||||
info.fColorTypeInfoCount = 1;
|
||||
info.fColorTypeInfos.reset(new ColorTypeInfo[info.fColorTypeInfoCount]());
|
||||
@ -748,6 +744,7 @@ void GrVkCaps::initFormatTable(const GrVkInterface* interface, VkPhysicalDevice
|
||||
// Format: VK_FORMAT_R5G6B5_UNORM_PACK16
|
||||
{
|
||||
auto& info = this->getFormatInfo(VK_FORMAT_R5G6B5_UNORM_PACK16);
|
||||
info.init(interface, physDev, properties, VK_FORMAT_R5G6B5_UNORM_PACK16);
|
||||
if (SkToBool(info.fOptimalFlags & FormatInfo::kTextureable_Flag)) {
|
||||
info.fColorTypeInfoCount = 1;
|
||||
info.fColorTypeInfos.reset(new ColorTypeInfo[info.fColorTypeInfoCount]());
|
||||
@ -763,6 +760,7 @@ void GrVkCaps::initFormatTable(const GrVkInterface* interface, VkPhysicalDevice
|
||||
// Format: VK_FORMAT_R16G16B16A16_SFLOAT
|
||||
{
|
||||
auto& info = this->getFormatInfo(VK_FORMAT_R16G16B16A16_SFLOAT);
|
||||
info.init(interface, physDev, properties, VK_FORMAT_R16G16B16A16_SFLOAT);
|
||||
if (SkToBool(info.fOptimalFlags & FormatInfo::kTextureable_Flag)) {
|
||||
info.fColorTypeInfoCount = 2;
|
||||
info.fColorTypeInfos.reset(new ColorTypeInfo[info.fColorTypeInfoCount]());
|
||||
@ -784,6 +782,7 @@ void GrVkCaps::initFormatTable(const GrVkInterface* interface, VkPhysicalDevice
|
||||
// Format: VK_FORMAT_R16_SFLOAT
|
||||
{
|
||||
auto& info = this->getFormatInfo(VK_FORMAT_R16_SFLOAT);
|
||||
info.init(interface, physDev, properties, VK_FORMAT_R16_SFLOAT);
|
||||
if (SkToBool(info.fOptimalFlags & FormatInfo::kTextureable_Flag)) {
|
||||
info.fColorTypeInfoCount = 1;
|
||||
info.fColorTypeInfos.reset(new ColorTypeInfo[info.fColorTypeInfoCount]());
|
||||
@ -801,6 +800,7 @@ void GrVkCaps::initFormatTable(const GrVkInterface* interface, VkPhysicalDevice
|
||||
// Format: VK_FORMAT_R8G8B8_UNORM
|
||||
{
|
||||
auto& info = this->getFormatInfo(VK_FORMAT_R8G8B8_UNORM);
|
||||
info.init(interface, physDev, properties, VK_FORMAT_R8G8B8_UNORM);
|
||||
if (SkToBool(info.fOptimalFlags & FormatInfo::kTextureable_Flag)) {
|
||||
info.fColorTypeInfoCount = 1;
|
||||
info.fColorTypeInfos.reset(new ColorTypeInfo[info.fColorTypeInfoCount]());
|
||||
@ -816,6 +816,7 @@ void GrVkCaps::initFormatTable(const GrVkInterface* interface, VkPhysicalDevice
|
||||
// Format: VK_FORMAT_R8G8_UNORM
|
||||
{
|
||||
auto& info = this->getFormatInfo(VK_FORMAT_R8G8_UNORM);
|
||||
info.init(interface, physDev, properties, VK_FORMAT_R8G8_UNORM);
|
||||
if (SkToBool(info.fOptimalFlags & FormatInfo::kTextureable_Flag)) {
|
||||
info.fColorTypeInfoCount = 1;
|
||||
info.fColorTypeInfos.reset(new ColorTypeInfo[info.fColorTypeInfoCount]());
|
||||
@ -831,6 +832,7 @@ void GrVkCaps::initFormatTable(const GrVkInterface* interface, VkPhysicalDevice
|
||||
// Format: VK_FORMAT_A2B10G10R10_UNORM_PACK32
|
||||
{
|
||||
auto& info = this->getFormatInfo(VK_FORMAT_A2B10G10R10_UNORM_PACK32);
|
||||
info.init(interface, physDev, properties, VK_FORMAT_A2B10G10R10_UNORM_PACK32);
|
||||
if (SkToBool(info.fOptimalFlags & FormatInfo::kTextureable_Flag)) {
|
||||
info.fColorTypeInfoCount = 1;
|
||||
info.fColorTypeInfos.reset(new ColorTypeInfo[info.fColorTypeInfoCount]());
|
||||
@ -846,6 +848,7 @@ void GrVkCaps::initFormatTable(const GrVkInterface* interface, VkPhysicalDevice
|
||||
// Format: VK_FORMAT_B4G4R4A4_UNORM_PACK16
|
||||
{
|
||||
auto& info = this->getFormatInfo(VK_FORMAT_B4G4R4A4_UNORM_PACK16);
|
||||
info.init(interface, physDev, properties, VK_FORMAT_B4G4R4A4_UNORM_PACK16);
|
||||
if (SkToBool(info.fOptimalFlags & FormatInfo::kTextureable_Flag)) {
|
||||
info.fColorTypeInfoCount = 1;
|
||||
info.fColorTypeInfos.reset(new ColorTypeInfo[info.fColorTypeInfoCount]());
|
||||
@ -863,6 +866,7 @@ void GrVkCaps::initFormatTable(const GrVkInterface* interface, VkPhysicalDevice
|
||||
// Format: VK_FORMAT_R4G4B4A4_UNORM_PACK16
|
||||
{
|
||||
auto& info = this->getFormatInfo(VK_FORMAT_R4G4B4A4_UNORM_PACK16);
|
||||
info.init(interface, physDev, properties, VK_FORMAT_R4G4B4A4_UNORM_PACK16);
|
||||
if (SkToBool(info.fOptimalFlags & FormatInfo::kTextureable_Flag)) {
|
||||
info.fColorTypeInfoCount = 1;
|
||||
info.fColorTypeInfos.reset(new ColorTypeInfo[info.fColorTypeInfoCount]());
|
||||
@ -878,6 +882,7 @@ void GrVkCaps::initFormatTable(const GrVkInterface* interface, VkPhysicalDevice
|
||||
// Format: VK_FORMAT_R32G32B32A32_SFLOAT
|
||||
{
|
||||
auto& info = this->getFormatInfo(VK_FORMAT_R32G32B32A32_SFLOAT);
|
||||
info.init(interface, physDev, properties, VK_FORMAT_R32G32B32A32_SFLOAT);
|
||||
if (SkToBool(info.fOptimalFlags & FormatInfo::kTextureable_Flag)) {
|
||||
info.fColorTypeInfoCount = 1;
|
||||
info.fColorTypeInfos.reset(new ColorTypeInfo[info.fColorTypeInfoCount]());
|
||||
@ -893,6 +898,9 @@ void GrVkCaps::initFormatTable(const GrVkInterface* interface, VkPhysicalDevice
|
||||
// Format: VK_FORMAT_R8G8B8A8_SRGB
|
||||
{
|
||||
auto& info = this->getFormatInfo(VK_FORMAT_R8G8B8A8_SRGB);
|
||||
if (fSRGBSupport) {
|
||||
info.init(interface, physDev, properties, VK_FORMAT_R8G8B8A8_SRGB);
|
||||
}
|
||||
if (SkToBool(info.fOptimalFlags & FormatInfo::kTextureable_Flag)) {
|
||||
info.fColorTypeInfoCount = 1;
|
||||
info.fColorTypeInfos.reset(new ColorTypeInfo[info.fColorTypeInfoCount]());
|
||||
@ -908,6 +916,7 @@ void GrVkCaps::initFormatTable(const GrVkInterface* interface, VkPhysicalDevice
|
||||
// Format: VK_FORMAT_R16_UNORM
|
||||
{
|
||||
auto& info = this->getFormatInfo(VK_FORMAT_R16_UNORM);
|
||||
info.init(interface, physDev, properties, VK_FORMAT_R16_UNORM);
|
||||
if (SkToBool(info.fOptimalFlags & FormatInfo::kTextureable_Flag)) {
|
||||
info.fColorTypeInfoCount = 1;
|
||||
info.fColorTypeInfos.reset(new ColorTypeInfo[info.fColorTypeInfoCount]());
|
||||
@ -923,6 +932,7 @@ void GrVkCaps::initFormatTable(const GrVkInterface* interface, VkPhysicalDevice
|
||||
// Format: VK_FORMAT_R16G16_UNORM
|
||||
{
|
||||
auto& info = this->getFormatInfo(VK_FORMAT_R16G16_UNORM);
|
||||
info.init(interface, physDev, properties, VK_FORMAT_R16G16_UNORM);
|
||||
if (SkToBool(info.fOptimalFlags & FormatInfo::kTextureable_Flag)) {
|
||||
info.fColorTypeInfoCount = 1;
|
||||
info.fColorTypeInfos.reset(new ColorTypeInfo[info.fColorTypeInfoCount]());
|
||||
@ -938,6 +948,7 @@ void GrVkCaps::initFormatTable(const GrVkInterface* interface, VkPhysicalDevice
|
||||
// Format: VK_FORMAT_R16G16B16A16_UNORM
|
||||
{
|
||||
auto& info = this->getFormatInfo(VK_FORMAT_R16G16B16A16_UNORM);
|
||||
info.init(interface, physDev, properties, VK_FORMAT_R16G16B16A16_UNORM);
|
||||
if (SkToBool(info.fOptimalFlags & FormatInfo::kTextureable_Flag)) {
|
||||
info.fColorTypeInfoCount = 1;
|
||||
info.fColorTypeInfos.reset(new ColorTypeInfo[info.fColorTypeInfoCount]());
|
||||
@ -953,6 +964,7 @@ void GrVkCaps::initFormatTable(const GrVkInterface* interface, VkPhysicalDevice
|
||||
// Format: VK_FORMAT_R16G16_SFLOAT
|
||||
{
|
||||
auto& info = this->getFormatInfo(VK_FORMAT_R16G16_SFLOAT);
|
||||
info.init(interface, physDev, properties, VK_FORMAT_R16G16_SFLOAT);
|
||||
if (SkToBool(info.fOptimalFlags & FormatInfo::kTextureable_Flag)) {
|
||||
info.fColorTypeInfoCount = 1;
|
||||
info.fColorTypeInfos.reset(new ColorTypeInfo[info.fColorTypeInfoCount]());
|
||||
@ -965,8 +977,46 @@ void GrVkCaps::initFormatTable(const GrVkInterface* interface, VkPhysicalDevice
|
||||
}
|
||||
}
|
||||
}
|
||||
// Format: VK_FORMAT_G8_B8_R8_3PLANE_420_UNORM
|
||||
{
|
||||
auto& info = this->getFormatInfo(VK_FORMAT_G8_B8_R8_3PLANE_420_UNORM);
|
||||
if (fSupportsYcbcrConversion) {
|
||||
info.init(interface, physDev, properties, VK_FORMAT_G8_B8_R8_3PLANE_420_UNORM);
|
||||
}
|
||||
if (SkToBool(info.fOptimalFlags & FormatInfo::kTextureable_Flag)) {
|
||||
info.fColorTypeInfoCount = 1;
|
||||
info.fColorTypeInfos.reset(new ColorTypeInfo[info.fColorTypeInfoCount]());
|
||||
int ctIdx = 0;
|
||||
// Format: VK_FORMAT_G8_B8_R8_3PLANE_420_UNORM, Surface: kRGB_888x
|
||||
{
|
||||
auto& ctInfo = info.fColorTypeInfos[ctIdx++];
|
||||
ctInfo.fColorType = GrColorType::kRGB_888x;
|
||||
ctInfo.fFlags = ColorTypeInfo::kUploadData_Flag;
|
||||
}
|
||||
}
|
||||
}
|
||||
// Format: VK_FORMAT_G8_B8R8_2PLANE_420_UNORM
|
||||
{
|
||||
auto& info = this->getFormatInfo(VK_FORMAT_G8_B8R8_2PLANE_420_UNORM);
|
||||
if (fSupportsYcbcrConversion) {
|
||||
info.init(interface, physDev, properties, VK_FORMAT_G8_B8R8_2PLANE_420_UNORM);
|
||||
}
|
||||
if (SkToBool(info.fOptimalFlags & FormatInfo::kTextureable_Flag)) {
|
||||
info.fColorTypeInfoCount = 1;
|
||||
info.fColorTypeInfos.reset(new ColorTypeInfo[info.fColorTypeInfoCount]());
|
||||
int ctIdx = 0;
|
||||
// Format: VK_FORMAT_G8_B8R8_2PLANE_420_UNORM, Surface: kRGB_888x
|
||||
{
|
||||
auto& ctInfo = info.fColorTypeInfos[ctIdx++];
|
||||
ctInfo.fColorType = GrColorType::kRGB_888x;
|
||||
ctInfo.fFlags = ColorTypeInfo::kUploadData_Flag;
|
||||
}
|
||||
}
|
||||
}
|
||||
// Format: VK_FORMAT_ETC2_R8G8B8_UNORM_BLOCK
|
||||
{
|
||||
auto& info = this->getFormatInfo(VK_FORMAT_ETC2_R8G8B8_UNORM_BLOCK);
|
||||
info.init(interface, physDev, properties, VK_FORMAT_ETC2_R8G8B8_UNORM_BLOCK);
|
||||
// No supported GrColorTypes.
|
||||
}
|
||||
}
|
||||
@ -1201,6 +1251,11 @@ GrCaps::SupportedWrite GrVkCaps::supportedWritePixelsColorType(GrColorType surfa
|
||||
return {GrColorType::kUnknown, 0};
|
||||
}
|
||||
|
||||
|
||||
if (GrVkFormatNeedsYcbcrSampler(vkFormat)) {
|
||||
return {GrColorType::kUnknown, 0};
|
||||
}
|
||||
|
||||
// The VkBufferImageCopy bufferOffset field must be both a multiple of 4 and of a single texel.
|
||||
size_t offsetAlignment = align_to_4(GrVkBytesPerFormat(vkFormat));
|
||||
|
||||
@ -1248,21 +1303,23 @@ bool GrVkCaps::onSurfaceSupportsWritePixels(const GrSurface* surface) const {
|
||||
}
|
||||
|
||||
static GrPixelConfig validate_image_info(VkFormat format, GrColorType ct, bool hasYcbcrConversion) {
|
||||
if (format == VK_FORMAT_UNDEFINED) {
|
||||
// If the format is undefined then it is only valid as an external image which requires that
|
||||
// we have a valid VkYcbcrConversion.
|
||||
if (hasYcbcrConversion) {
|
||||
if (hasYcbcrConversion) {
|
||||
if (GrVkFormatNeedsYcbcrSampler(format)) {
|
||||
return kRGB_888X_GrPixelConfig;
|
||||
}
|
||||
|
||||
// Format may be undefined for external images, which are required to have YCbCr conversion.
|
||||
if (VK_FORMAT_UNDEFINED == format) {
|
||||
// We don't actually care what the color type or config are since we won't use those
|
||||
// values for external textures. However, for read pixels we will draw to a non ycbcr
|
||||
// texture of this config so we set RGBA here for that.
|
||||
return kRGBA_8888_GrPixelConfig;
|
||||
} else {
|
||||
return kUnknown_GrPixelConfig;
|
||||
}
|
||||
|
||||
return kUnknown_GrPixelConfig;
|
||||
}
|
||||
|
||||
if (hasYcbcrConversion) {
|
||||
// We only support having a ycbcr conversion for external images.
|
||||
if (VK_FORMAT_UNDEFINED == format) {
|
||||
return kUnknown_GrPixelConfig;
|
||||
}
|
||||
|
||||
@ -1482,6 +1539,10 @@ GrCaps::SupportedRead GrVkCaps::onSupportedReadPixelsColorType(
|
||||
return {GrColorType::kUnknown, 0};
|
||||
}
|
||||
|
||||
if (GrVkFormatNeedsYcbcrSampler(vkFormat)) {
|
||||
return {GrColorType::kUnknown, 0};
|
||||
}
|
||||
|
||||
// The VkBufferImageCopy bufferOffset field must be both a multiple of 4 and of a single texel.
|
||||
size_t offsetAlignment = align_to_4(GrVkBytesPerFormat(vkFormat));
|
||||
|
||||
|
@ -253,7 +253,7 @@ private:
|
||||
std::unique_ptr<ColorTypeInfo[]> fColorTypeInfos;
|
||||
int fColorTypeInfoCount = 0;
|
||||
};
|
||||
static const size_t kNumVkFormats = 18;
|
||||
static const size_t kNumVkFormats = 20;
|
||||
FormatInfo fFormatTable[kNumVkFormats];
|
||||
|
||||
FormatInfo& getFormatInfo(VkFormat);
|
||||
|
@ -1136,7 +1136,7 @@ static bool check_image_info(const GrVkCaps& caps,
|
||||
}
|
||||
|
||||
if (info.fYcbcrConversionInfo.isValid()) {
|
||||
if (!caps.supportsYcbcrConversion() || info.fFormat != VK_NULL_HANDLE) {
|
||||
if (!caps.supportsYcbcrConversion()) {
|
||||
return false;
|
||||
}
|
||||
}
|
||||
@ -1874,6 +1874,11 @@ GrBackendTexture GrVkGpu::createBackendTexture(int w, int h,
|
||||
return GrBackendTexture();
|
||||
}
|
||||
|
||||
if (GrVkFormatNeedsYcbcrSampler(vkFormat)) {
|
||||
SkDebugf("Can't create BackendTexture that requires Ycbcb sampler.\n");
|
||||
return GrBackendTexture();
|
||||
}
|
||||
|
||||
GrVkImageInfo info;
|
||||
if (!this->createVkImageForBackendSurface(vkFormat, w, h, true,
|
||||
GrRenderable::kYes == renderable, mipMapped, srcData,
|
||||
@ -2629,7 +2634,11 @@ uint32_t GrVkGpu::getExtraSamplerKeyForProgram(const GrSamplerState& samplerStat
|
||||
const GrVkSampler* sampler = this->resourceProvider().findOrCreateCompatibleSampler(
|
||||
samplerState, *ycbcrInfo);
|
||||
|
||||
return sampler->uniqueID();
|
||||
uint32_t result = sampler->uniqueID();
|
||||
|
||||
sampler->unref(this);
|
||||
|
||||
return result;
|
||||
}
|
||||
|
||||
void GrVkGpu::storeVkPipelineCacheData() {
|
||||
|
@ -57,7 +57,7 @@ public:
|
||||
VkFormat imageFormat() const { return fInfo.fFormat; }
|
||||
GrBackendFormat getBackendFormat() const {
|
||||
if (fResource && this->ycbcrConversionInfo().isValid()) {
|
||||
SkASSERT(this->imageFormat() == VK_FORMAT_UNDEFINED);
|
||||
SkASSERT(this->imageFormat() == this->ycbcrConversionInfo().fFormat);
|
||||
return GrBackendFormat::MakeVk(this->ycbcrConversionInfo());
|
||||
}
|
||||
SkASSERT(this->imageFormat() != VK_FORMAT_UNDEFINED);
|
||||
|
@ -19,7 +19,7 @@ const GrVkImageView* GrVkImageView::Create(GrVkGpu* gpu, VkImage image, VkFormat
|
||||
GrVkSamplerYcbcrConversion* ycbcrConversion = nullptr;
|
||||
|
||||
if (ycbcrInfo.isValid()) {
|
||||
SkASSERT(gpu->vkCaps().supportsYcbcrConversion() && format == VK_FORMAT_UNDEFINED);
|
||||
SkASSERT(gpu->vkCaps().supportsYcbcrConversion() && format == ycbcrInfo.fFormat);
|
||||
|
||||
ycbcrConversion =
|
||||
gpu->resourceProvider().findOrCreateCompatibleSamplerYcbcrConversion(ycbcrInfo);
|
||||
|
@ -388,12 +388,16 @@ void GrVkResourceProvider::destroyResources(bool deviceLost) {
|
||||
fExternalRenderPasses.reset();
|
||||
|
||||
// Iterate through all store GrVkSamplers and unref them before resetting the hash.
|
||||
SkTDynamicHash<GrVkSampler, GrVkSampler::Key>::Iter iter(&fSamplers);
|
||||
for (; !iter.done(); ++iter) {
|
||||
for (decltype(fSamplers)::Iter iter(&fSamplers); !iter.done(); ++iter) {
|
||||
(*iter).unref(fGpu);
|
||||
}
|
||||
fSamplers.reset();
|
||||
|
||||
for (decltype(fYcbcrConversions)::Iter iter(&fYcbcrConversions); !iter.done(); ++iter) {
|
||||
(*iter).unref(fGpu);
|
||||
}
|
||||
fYcbcrConversions.reset();
|
||||
|
||||
fPipelineStateCache->release();
|
||||
|
||||
GR_VK_CALL(fGpu->vkInterface(), DestroyPipelineCache(fGpu->device(), fPipelineCache, nullptr));
|
||||
@ -462,6 +466,11 @@ void GrVkResourceProvider::abandonResources() {
|
||||
}
|
||||
fSamplers.reset();
|
||||
|
||||
for (decltype(fYcbcrConversions)::Iter iter(&fYcbcrConversions); !iter.done(); ++iter) {
|
||||
(*iter).unrefAndAbandon();
|
||||
}
|
||||
fYcbcrConversions.reset();
|
||||
|
||||
fPipelineStateCache->abandon();
|
||||
|
||||
fPipelineCache = VK_NULL_HANDLE;
|
||||
|
@ -82,8 +82,7 @@ GrVkSampler* GrVkSampler::Create(GrVkGpu* gpu, const GrSamplerState& samplerStat
|
||||
|
||||
createInfo.pNext = &conversionInfo;
|
||||
|
||||
const VkFormatFeatureFlags& flags = ycbcrInfo.fExternalFormatFeatures;
|
||||
|
||||
VkFormatFeatureFlags flags = ycbcrInfo.fFormatFeatures;
|
||||
if (!SkToBool(flags & VK_FORMAT_FEATURE_SAMPLED_IMAGE_YCBCR_CONVERSION_LINEAR_FILTER_BIT)) {
|
||||
createInfo.magFilter = VK_FILTER_NEAREST;
|
||||
createInfo.minFilter = VK_FILTER_NEAREST;
|
||||
|
@ -14,11 +14,9 @@ GrVkSamplerYcbcrConversion* GrVkSamplerYcbcrConversion::Create(
|
||||
if (!gpu->vkCaps().supportsYcbcrConversion()) {
|
||||
return nullptr;
|
||||
}
|
||||
// We only support creating ycbcr conversion for external formats;
|
||||
SkASSERT(info.fExternalFormat);
|
||||
|
||||
#ifdef SK_DEBUG
|
||||
const VkFormatFeatureFlags& featureFlags = info.fExternalFormatFeatures;
|
||||
const VkFormatFeatureFlags& featureFlags = info.fFormatFeatures;
|
||||
if (info.fXChromaOffset == VK_CHROMA_LOCATION_MIDPOINT ||
|
||||
info.fYChromaOffset == VK_CHROMA_LOCATION_MIDPOINT) {
|
||||
SkASSERT(featureFlags & VK_FORMAT_FEATURE_MIDPOINT_CHROMA_SAMPLES_BIT);
|
||||
@ -36,36 +34,50 @@ GrVkSamplerYcbcrConversion* GrVkSamplerYcbcrConversion::Create(
|
||||
}
|
||||
#endif
|
||||
|
||||
#ifdef SK_BUILD_FOR_ANDROID
|
||||
VkExternalFormatANDROID externalFormat;
|
||||
externalFormat.sType = VK_STRUCTURE_TYPE_EXTERNAL_FORMAT_ANDROID;
|
||||
externalFormat.pNext = nullptr;
|
||||
externalFormat.externalFormat = info.fExternalFormat;
|
||||
|
||||
VkSamplerYcbcrConversionCreateInfo ycbcrCreateInfo;
|
||||
memset(&ycbcrCreateInfo, 0, sizeof(VkSamplerYcbcrConversionCreateInfo));
|
||||
ycbcrCreateInfo.sType = VK_STRUCTURE_TYPE_SAMPLER_YCBCR_CONVERSION_CREATE_INFO;
|
||||
ycbcrCreateInfo.pNext = &externalFormat;
|
||||
ycbcrCreateInfo.format = VK_FORMAT_UNDEFINED;
|
||||
ycbcrCreateInfo.pNext = nullptr;
|
||||
ycbcrCreateInfo.format = info.fFormat;
|
||||
ycbcrCreateInfo.ycbcrModel = info.fYcbcrModel;
|
||||
ycbcrCreateInfo.ycbcrRange = info.fYcbcrRange;
|
||||
// Componets is ignored for external format conversions;
|
||||
// ycbcrCreateInfo.components = {0, 0, 0, 0};
|
||||
|
||||
// Components is ignored for external format conversions. For all other formats identity swizzle
|
||||
// is used. It can be added to GrVkYcbcrConversionInfo if necessary.
|
||||
ycbcrCreateInfo.components = {VK_COMPONENT_SWIZZLE_IDENTITY, VK_COMPONENT_SWIZZLE_IDENTITY,
|
||||
VK_COMPONENT_SWIZZLE_IDENTITY, VK_COMPONENT_SWIZZLE_IDENTITY};
|
||||
ycbcrCreateInfo.xChromaOffset = info.fXChromaOffset;
|
||||
ycbcrCreateInfo.yChromaOffset = info.fYChromaOffset;
|
||||
ycbcrCreateInfo.chromaFilter = info.fChromaFilter;
|
||||
ycbcrCreateInfo.forceExplicitReconstruction = info.fForceExplicitReconstruction;
|
||||
|
||||
#ifdef SK_BUILD_FOR_ANDROID
|
||||
VkExternalFormatANDROID externalFormat;
|
||||
if (info.fExternalFormat) {
|
||||
// Format must not be specified for external images.
|
||||
SkASSERT(info.fFormat == VK_FORMAT_UNDEFINED);
|
||||
externalFormat.sType = VK_STRUCTURE_TYPE_EXTERNAL_FORMAT_ANDROID;
|
||||
externalFormat.pNext = nullptr;
|
||||
externalFormat.externalFormat = info.fExternalFormat;
|
||||
ycbcrCreateInfo.pNext = &externalFormat;
|
||||
}
|
||||
#else
|
||||
// External images are supported only on Android;
|
||||
SkASSERT(!info.fExternalFormat);
|
||||
#endif
|
||||
|
||||
if (!info.fExternalFormat) {
|
||||
SkASSERT(info.fFormat != VK_FORMAT_UNDEFINED);
|
||||
}
|
||||
|
||||
VkSamplerYcbcrConversion conversion;
|
||||
GR_VK_CALL(gpu->vkInterface(), CreateSamplerYcbcrConversion(gpu->device(), &ycbcrCreateInfo,
|
||||
nullptr, &conversion));
|
||||
if (conversion == VK_NULL_HANDLE) {
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
return new GrVkSamplerYcbcrConversion(conversion, GenerateKey(info));
|
||||
#else
|
||||
return nullptr;
|
||||
#endif
|
||||
}
|
||||
|
||||
void GrVkSamplerYcbcrConversion::freeGPUData(GrVkGpu* gpu) const {
|
||||
@ -96,6 +108,5 @@ GrVkSamplerYcbcrConversion::Key GrVkSamplerYcbcrConversion::GenerateKey(
|
||||
ycbcrKey |= (static_cast<uint8_t>(ycbcrInfo.fChromaFilter) << kChromaFilterShift);
|
||||
ycbcrKey |= (static_cast<uint8_t>(ycbcrInfo.fForceExplicitReconstruction) << kReconShift);
|
||||
|
||||
return {ycbcrInfo.fExternalFormat, ycbcrKey};
|
||||
return Key{ycbcrInfo.fFormat, ycbcrInfo.fExternalFormat, ycbcrKey};
|
||||
}
|
||||
|
||||
|
@ -22,18 +22,21 @@ public:
|
||||
VkSamplerYcbcrConversion ycbcrConversion() const { return fYcbcrConversion; }
|
||||
|
||||
struct Key {
|
||||
Key() : fExternalFormat(0), fConversionKey(0) {}
|
||||
Key(uint64_t externalFormat, uint8_t conversionKey) {
|
||||
Key() : fVkFormat(VK_FORMAT_UNDEFINED), fExternalFormat(0), fConversionKey(0) {}
|
||||
Key(VkFormat vkFormat, uint64_t externalFormat, uint8_t conversionKey) {
|
||||
memset(this, 0, sizeof(Key));
|
||||
fVkFormat = vkFormat;
|
||||
fExternalFormat = externalFormat;
|
||||
fConversionKey = conversionKey;
|
||||
}
|
||||
|
||||
VkFormat fVkFormat;
|
||||
uint64_t fExternalFormat;
|
||||
uint8_t fConversionKey;
|
||||
|
||||
bool operator==(const Key& that) const {
|
||||
return this->fExternalFormat == that.fExternalFormat &&
|
||||
return this->fVkFormat == that.fVkFormat &&
|
||||
this->fExternalFormat == that.fExternalFormat &&
|
||||
this->fConversionKey == that.fConversionKey;
|
||||
}
|
||||
};
|
||||
|
@ -202,6 +202,16 @@ static void get_ubo_aligned_offset(uint32_t* uniformOffset,
|
||||
}
|
||||
}
|
||||
|
||||
GrVkUniformHandler::~GrVkUniformHandler() {
|
||||
GrVkGpu* gpu = static_cast<GrVkPipelineStateBuilder*>(fProgramBuilder)->gpu();
|
||||
for (decltype(fSamplers)::Iter iter(&fSamplers); iter.next();) {
|
||||
if (iter->fImmutableSampler) {
|
||||
iter->fImmutableSampler->unref(gpu);
|
||||
iter->fImmutableSampler = nullptr;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
GrGLSLUniformHandler::UniformHandle GrVkUniformHandler::internalAddUniformArray(
|
||||
uint32_t visibility,
|
||||
GrSLType type,
|
||||
@ -280,10 +290,9 @@ GrGLSLUniformHandler::SamplerHandle GrVkUniformHandler::addSampler(const GrTextu
|
||||
info.fVisibility = kFragment_GrShaderFlag;
|
||||
info.fUBOffset = 0;
|
||||
|
||||
// Check if we are dealing with an external texture and store the needed information if so
|
||||
// Check if we are dealing with an external texture and store the needed information if so.
|
||||
const GrVkTexture* vkTexture = static_cast<const GrVkTexture*>(texture);
|
||||
if (vkTexture->ycbcrConversionInfo().isValid()) {
|
||||
SkASSERT(type == GrTextureType::kExternal);
|
||||
GrVkGpu* gpu = static_cast<GrVkPipelineStateBuilder*>(fProgramBuilder)->gpu();
|
||||
info.fImmutableSampler = gpu->resourceProvider().findOrCreateCompatibleSampler(
|
||||
state, vkTexture->ycbcrConversionInfo());
|
||||
|
@ -39,12 +39,13 @@ public:
|
||||
uint32_t fVisibility;
|
||||
// fUBOffset is only valid if the GrSLType of the fVariable is not a sampler
|
||||
uint32_t fUBOffset;
|
||||
// The SamplerState, maxMipLevel, and ycbcrInfo are only valid if the GrSLType is a sampler
|
||||
// and that sampler is used for sampling an external image with a ycbcr conversion.
|
||||
// fImmutableSampler is used for sampling an image with a ycbcr conversion.
|
||||
const GrVkSampler* fImmutableSampler = nullptr;
|
||||
};
|
||||
typedef GrTAllocator<UniformInfo> UniformInfoArray;
|
||||
|
||||
~GrVkUniformHandler() override;
|
||||
|
||||
const GrShaderVar& getUniformVariable(UniformHandle u) const override {
|
||||
return fUniforms[u.toIndex()].fVariable;
|
||||
}
|
||||
|
@ -100,30 +100,32 @@ bool GrPixelConfigToVkFormat(GrPixelConfig config, VkFormat* format) {
|
||||
#ifdef SK_DEBUG
|
||||
bool GrVkFormatColorTypePairIsValid(VkFormat format, GrColorType colorType) {
|
||||
switch (format) {
|
||||
case VK_FORMAT_R8G8B8A8_UNORM: return GrColorType::kRGBA_8888 == colorType ||
|
||||
GrColorType::kRGB_888x == colorType;
|
||||
case VK_FORMAT_B8G8R8A8_UNORM: return GrColorType::kBGRA_8888 == colorType;
|
||||
case VK_FORMAT_R8G8B8A8_SRGB: return GrColorType::kRGBA_8888_SRGB == colorType;
|
||||
case VK_FORMAT_R8G8B8_UNORM: return GrColorType::kRGB_888x == colorType;
|
||||
case VK_FORMAT_R8G8_UNORM: return GrColorType::kRG_88 == colorType;
|
||||
case VK_FORMAT_A2B10G10R10_UNORM_PACK32: return GrColorType::kRGBA_1010102 == colorType;
|
||||
case VK_FORMAT_R5G6B5_UNORM_PACK16: return GrColorType::kBGR_565 == colorType;
|
||||
case VK_FORMAT_R8G8B8A8_UNORM: return GrColorType::kRGBA_8888 == colorType ||
|
||||
GrColorType::kRGB_888x == colorType;
|
||||
case VK_FORMAT_B8G8R8A8_UNORM: return GrColorType::kBGRA_8888 == colorType;
|
||||
case VK_FORMAT_R8G8B8A8_SRGB: return GrColorType::kRGBA_8888_SRGB == colorType;
|
||||
case VK_FORMAT_R8G8B8_UNORM: return GrColorType::kRGB_888x == colorType;
|
||||
case VK_FORMAT_R8G8_UNORM: return GrColorType::kRG_88 == colorType;
|
||||
case VK_FORMAT_A2B10G10R10_UNORM_PACK32: return GrColorType::kRGBA_1010102 == colorType;
|
||||
case VK_FORMAT_R5G6B5_UNORM_PACK16: return GrColorType::kBGR_565 == colorType;
|
||||
// R4G4B4A4 is not required to be supported so we actually
|
||||
// store RGBA_4444 data as B4G4R4A4.
|
||||
case VK_FORMAT_B4G4R4A4_UNORM_PACK16: return GrColorType::kABGR_4444 == colorType;
|
||||
case VK_FORMAT_R4G4B4A4_UNORM_PACK16: return GrColorType::kABGR_4444 == colorType;
|
||||
case VK_FORMAT_B4G4R4A4_UNORM_PACK16: return GrColorType::kABGR_4444 == colorType;
|
||||
case VK_FORMAT_R4G4B4A4_UNORM_PACK16: return GrColorType::kABGR_4444 == colorType;
|
||||
case VK_FORMAT_R8_UNORM: return GrColorType::kAlpha_8 == colorType ||
|
||||
GrColorType::kGray_8 == colorType;
|
||||
case VK_FORMAT_R32G32B32A32_SFLOAT: return GrColorType::kRGBA_F32 == colorType;
|
||||
case VK_FORMAT_R16G16B16A16_SFLOAT: return GrColorType::kRGBA_F16 == colorType ||
|
||||
GrColorType::kRGBA_F16_Clamped == colorType;
|
||||
case VK_FORMAT_R16_SFLOAT: return GrColorType::kAlpha_F16 == colorType;
|
||||
case VK_FORMAT_R16_UNORM: return GrColorType::kR_16 == colorType;
|
||||
case VK_FORMAT_R16G16_UNORM: return GrColorType::kRG_1616 == colorType;
|
||||
GrColorType::kGray_8 == colorType;
|
||||
case VK_FORMAT_R32G32B32A32_SFLOAT: return GrColorType::kRGBA_F32 == colorType;
|
||||
case VK_FORMAT_R16G16B16A16_SFLOAT: return GrColorType::kRGBA_F16 == colorType ||
|
||||
GrColorType::kRGBA_F16_Clamped == colorType;
|
||||
case VK_FORMAT_R16_SFLOAT: return GrColorType::kAlpha_F16 == colorType;
|
||||
case VK_FORMAT_R16_UNORM: return GrColorType::kR_16 == colorType;
|
||||
case VK_FORMAT_R16G16_UNORM: return GrColorType::kRG_1616 == colorType;
|
||||
case VK_FORMAT_G8_B8_R8_3PLANE_420_UNORM: return GrColorType::kRGB_888x == colorType;
|
||||
case VK_FORMAT_G8_B8R8_2PLANE_420_UNORM: return GrColorType::kRGB_888x == colorType;
|
||||
// Experimental (for Y416 and mutant P016/P010)
|
||||
case VK_FORMAT_R16G16B16A16_UNORM: return GrColorType::kRGBA_16161616 == colorType;
|
||||
case VK_FORMAT_R16G16_SFLOAT: return GrColorType::kRG_F16 == colorType;
|
||||
default: return false;
|
||||
case VK_FORMAT_R16G16B16A16_UNORM: return GrColorType::kRGBA_16161616 == colorType;
|
||||
case VK_FORMAT_R16G16_SFLOAT: return GrColorType::kRG_F16 == colorType;
|
||||
default: return false;
|
||||
}
|
||||
|
||||
SkUNREACHABLE;
|
||||
@ -148,6 +150,8 @@ bool GrVkFormatIsSupported(VkFormat format) {
|
||||
case VK_FORMAT_R16_SFLOAT:
|
||||
case VK_FORMAT_R16_UNORM:
|
||||
case VK_FORMAT_R16G16_UNORM:
|
||||
case VK_FORMAT_G8_B8_R8_3PLANE_420_UNORM:
|
||||
case VK_FORMAT_G8_B8R8_2PLANE_420_UNORM:
|
||||
// Experimental (for Y416 and mutant P016/P010)
|
||||
case VK_FORMAT_R16G16B16A16_UNORM:
|
||||
case VK_FORMAT_R16G16_SFLOAT:
|
||||
@ -157,6 +161,11 @@ bool GrVkFormatIsSupported(VkFormat format) {
|
||||
}
|
||||
}
|
||||
|
||||
bool GrVkFormatNeedsYcbcrSampler(VkFormat format) {
|
||||
return format == VK_FORMAT_G8_B8R8_2PLANE_420_UNORM ||
|
||||
format == VK_FORMAT_G8_B8_R8_3PLANE_420_UNORM;
|
||||
}
|
||||
|
||||
bool GrSampleCountToVkSampleCount(uint32_t samples, VkSampleCountFlagBits* vkSamples) {
|
||||
SkASSERT(samples >= 1);
|
||||
switch (samples) {
|
||||
|
@ -36,6 +36,8 @@ bool GrPixelConfigToVkFormat(GrPixelConfig config, VkFormat* format);
|
||||
|
||||
bool GrVkFormatIsSupported(VkFormat);
|
||||
|
||||
bool GrVkFormatNeedsYcbcrSampler(VkFormat format);
|
||||
|
||||
#ifdef SK_DEBUG
|
||||
/**
|
||||
* Returns true if the passed in VkFormat and GrColorType are compatible with each other.
|
||||
|
430
tests/VkYcbcrSamplerTest.cpp
Normal file
430
tests/VkYcbcrSamplerTest.cpp
Normal file
@ -0,0 +1,430 @@
|
||||
/*
|
||||
* Copyright 2019 Google Inc.
|
||||
*
|
||||
* Use of this source code is governed by a BSD-style license that can be
|
||||
* found in the LICENSE file.
|
||||
*/
|
||||
|
||||
#include "include/core/SkTypes.h"
|
||||
|
||||
#if SK_SUPPORT_GPU && defined(SK_VULKAN)
|
||||
|
||||
#include "include/core/SkImage.h"
|
||||
#include "include/core/SkSurface.h"
|
||||
#include "include/gpu/GrContext.h"
|
||||
#include "include/gpu/vk/GrVkBackendContext.h"
|
||||
#include "include/gpu/vk/GrVkExtensions.h"
|
||||
#include "tests/Test.h"
|
||||
#include "tools/gpu/vk/VkTestUtils.h"
|
||||
|
||||
const size_t kImageWidth = 8;
|
||||
const size_t kImageHeight = 8;
|
||||
|
||||
static int getY(size_t x, size_t y) {
|
||||
return 16 + (x + y) * 219 / (kImageWidth + kImageHeight - 2);
|
||||
}
|
||||
static int getU(size_t x, size_t y) { return 16 + x * 224 / (kImageWidth - 1); }
|
||||
static int getV(size_t x, size_t y) { return 16 + y * 224 / (kImageHeight - 1); }
|
||||
|
||||
#define DECLARE_VK_PROC(name) PFN_vk##name fVk##name
|
||||
|
||||
#define ACQUIRE_INST_VK_PROC(name) \
|
||||
fVk##name = reinterpret_cast<PFN_vk##name>(getProc("vk" #name, fBackendContext.fInstance,\
|
||||
VK_NULL_HANDLE)); \
|
||||
if (fVk##name == nullptr) { \
|
||||
ERRORF(reporter, "Function ptr for vk%s could not be acquired\n", #name); \
|
||||
return false; \
|
||||
}
|
||||
|
||||
#define ACQUIRE_DEVICE_VK_PROC(name) \
|
||||
fVk##name = reinterpret_cast<PFN_vk##name>(getProc("vk" #name, VK_NULL_HANDLE, fDevice)); \
|
||||
if (fVk##name == nullptr) { \
|
||||
ERRORF(reporter, "Function ptr for vk%s could not be acquired\n", #name); \
|
||||
return false; \
|
||||
}
|
||||
|
||||
class VkYcbcrSamplerTestHelper {
|
||||
public:
|
||||
VkYcbcrSamplerTestHelper() {}
|
||||
~VkYcbcrSamplerTestHelper();
|
||||
|
||||
bool init(skiatest::Reporter* reporter);
|
||||
|
||||
sk_sp<SkImage> createI420Image(skiatest::Reporter* reporter);
|
||||
|
||||
GrContext* getGrContext() { return fGrContext.get(); }
|
||||
|
||||
private:
|
||||
GrVkExtensions fExtensions;
|
||||
VkPhysicalDeviceFeatures2 fFeatures = {};
|
||||
VkDebugReportCallbackEXT fDebugCallback = VK_NULL_HANDLE;
|
||||
|
||||
DECLARE_VK_PROC(DestroyInstance);
|
||||
DECLARE_VK_PROC(DeviceWaitIdle);
|
||||
DECLARE_VK_PROC(DestroyDevice);
|
||||
|
||||
DECLARE_VK_PROC(GetPhysicalDeviceFormatProperties);
|
||||
DECLARE_VK_PROC(GetPhysicalDeviceMemoryProperties);
|
||||
|
||||
DECLARE_VK_PROC(CreateImage);
|
||||
DECLARE_VK_PROC(DestroyImage);
|
||||
DECLARE_VK_PROC(GetImageMemoryRequirements);
|
||||
DECLARE_VK_PROC(AllocateMemory);
|
||||
DECLARE_VK_PROC(FreeMemory);
|
||||
DECLARE_VK_PROC(BindImageMemory);
|
||||
DECLARE_VK_PROC(MapMemory);
|
||||
DECLARE_VK_PROC(UnmapMemory);
|
||||
DECLARE_VK_PROC(FlushMappedMemoryRanges);
|
||||
DECLARE_VK_PROC(GetImageSubresourceLayout);
|
||||
|
||||
VkDevice fDevice = VK_NULL_HANDLE;
|
||||
|
||||
PFN_vkDestroyDebugReportCallbackEXT fDestroyDebugCallback = nullptr;
|
||||
|
||||
GrVkBackendContext fBackendContext;
|
||||
sk_sp<GrContext> fGrContext;
|
||||
|
||||
VkImage fImage = VK_NULL_HANDLE;
|
||||
VkDeviceMemory fImageMemory = VK_NULL_HANDLE;
|
||||
GrBackendTexture texture;
|
||||
};
|
||||
|
||||
VkYcbcrSamplerTestHelper::~VkYcbcrSamplerTestHelper() {
|
||||
fGrContext.reset();
|
||||
|
||||
if (fImage != VK_NULL_HANDLE) {
|
||||
fVkDestroyImage(fDevice, fImage, nullptr);
|
||||
fImage = VK_NULL_HANDLE;
|
||||
}
|
||||
if (fImageMemory != VK_NULL_HANDLE) {
|
||||
fVkFreeMemory(fDevice, fImageMemory, nullptr);
|
||||
fImageMemory = VK_NULL_HANDLE;
|
||||
}
|
||||
|
||||
fBackendContext.fMemoryAllocator.reset();
|
||||
if (fDevice != VK_NULL_HANDLE) {
|
||||
fVkDeviceWaitIdle(fDevice);
|
||||
fVkDestroyDevice(fDevice, nullptr);
|
||||
fDevice = VK_NULL_HANDLE;
|
||||
}
|
||||
if (fDebugCallback != VK_NULL_HANDLE) {
|
||||
fDestroyDebugCallback(fBackendContext.fInstance, fDebugCallback, nullptr);
|
||||
}
|
||||
if (fBackendContext.fInstance != VK_NULL_HANDLE) {
|
||||
fVkDestroyInstance(fBackendContext.fInstance, nullptr);
|
||||
fBackendContext.fInstance = VK_NULL_HANDLE;
|
||||
}
|
||||
|
||||
sk_gpu_test::FreeVulkanFeaturesStructs(&fFeatures);
|
||||
}
|
||||
|
||||
bool VkYcbcrSamplerTestHelper::init(skiatest::Reporter* reporter) {
|
||||
PFN_vkGetInstanceProcAddr instProc;
|
||||
PFN_vkGetDeviceProcAddr devProc;
|
||||
if (!sk_gpu_test::LoadVkLibraryAndGetProcAddrFuncs(&instProc, &devProc)) {
|
||||
ERRORF(reporter, "Failed to load Vulkan");
|
||||
return false;
|
||||
}
|
||||
auto getProc = [&instProc, &devProc](const char* proc_name,
|
||||
VkInstance instance, VkDevice device) {
|
||||
if (device != VK_NULL_HANDLE) {
|
||||
return devProc(device, proc_name);
|
||||
}
|
||||
return instProc(instance, proc_name);
|
||||
};
|
||||
|
||||
fFeatures.sType = VK_STRUCTURE_TYPE_PHYSICAL_DEVICE_FEATURES_2;
|
||||
fFeatures.pNext = nullptr;
|
||||
|
||||
fBackendContext.fInstance = VK_NULL_HANDLE;
|
||||
fBackendContext.fDevice = VK_NULL_HANDLE;
|
||||
|
||||
if (!sk_gpu_test::CreateVkBackendContext(getProc, &fBackendContext, &fExtensions, &fFeatures,
|
||||
&fDebugCallback, nullptr, sk_gpu_test::CanPresentFn(),
|
||||
false)) {
|
||||
return false;
|
||||
}
|
||||
fDevice = fBackendContext.fDevice;
|
||||
|
||||
if (fDebugCallback != VK_NULL_HANDLE) {
|
||||
fDestroyDebugCallback = reinterpret_cast<PFN_vkDestroyDebugReportCallbackEXT>(
|
||||
instProc(fBackendContext.fInstance, "vkDestroyDebugReportCallbackEXT"));
|
||||
}
|
||||
ACQUIRE_INST_VK_PROC(DestroyInstance)
|
||||
ACQUIRE_INST_VK_PROC(DeviceWaitIdle)
|
||||
ACQUIRE_INST_VK_PROC(DestroyDevice)
|
||||
|
||||
ACQUIRE_INST_VK_PROC(GetPhysicalDeviceFormatProperties)
|
||||
ACQUIRE_INST_VK_PROC(GetPhysicalDeviceMemoryProperties)
|
||||
|
||||
ACQUIRE_DEVICE_VK_PROC(CreateImage)
|
||||
ACQUIRE_DEVICE_VK_PROC(DestroyImage)
|
||||
ACQUIRE_DEVICE_VK_PROC(GetImageMemoryRequirements)
|
||||
ACQUIRE_DEVICE_VK_PROC(AllocateMemory)
|
||||
ACQUIRE_DEVICE_VK_PROC(FreeMemory)
|
||||
ACQUIRE_DEVICE_VK_PROC(BindImageMemory)
|
||||
ACQUIRE_DEVICE_VK_PROC(MapMemory)
|
||||
ACQUIRE_DEVICE_VK_PROC(UnmapMemory)
|
||||
ACQUIRE_DEVICE_VK_PROC(FlushMappedMemoryRanges)
|
||||
ACQUIRE_DEVICE_VK_PROC(GetImageSubresourceLayout)
|
||||
|
||||
bool ycbcrSupported = false;
|
||||
VkBaseOutStructure* feature = reinterpret_cast<VkBaseOutStructure*>(fFeatures.pNext);
|
||||
while (feature) {
|
||||
if (feature->sType == VK_STRUCTURE_TYPE_PHYSICAL_DEVICE_SAMPLER_YCBCR_CONVERSION_FEATURES) {
|
||||
VkPhysicalDeviceSamplerYcbcrConversionFeatures* ycbcrFeatures =
|
||||
reinterpret_cast<VkPhysicalDeviceSamplerYcbcrConversionFeatures*>(feature);
|
||||
ycbcrSupported = ycbcrFeatures->samplerYcbcrConversion;
|
||||
break;
|
||||
}
|
||||
feature = feature->pNext;
|
||||
}
|
||||
if (!ycbcrSupported) {
|
||||
return false;
|
||||
}
|
||||
|
||||
fGrContext = GrContext::MakeVulkan(fBackendContext);
|
||||
if (!fGrContext) {
|
||||
return false;
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
sk_sp<SkImage> VkYcbcrSamplerTestHelper::createI420Image(skiatest::Reporter* reporter) {
|
||||
// Verify that the image format is supported.
|
||||
VkFormatProperties formatProperties;
|
||||
fVkGetPhysicalDeviceFormatProperties(fBackendContext.fPhysicalDevice,
|
||||
VK_FORMAT_G8_B8R8_2PLANE_420_UNORM, &formatProperties);
|
||||
if (!(formatProperties.linearTilingFeatures & VK_FORMAT_FEATURE_SAMPLED_IMAGE_BIT)) {
|
||||
// VK_FORMAT_G8_B8R8_2PLANE_420_UNORM is not supported
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
// Create YCbCr image.
|
||||
VkImageCreateInfo vkImageInfo = {};
|
||||
vkImageInfo.sType = VK_STRUCTURE_TYPE_IMAGE_CREATE_INFO;
|
||||
vkImageInfo.imageType = VK_IMAGE_TYPE_2D;
|
||||
vkImageInfo.format = VK_FORMAT_G8_B8R8_2PLANE_420_UNORM;
|
||||
vkImageInfo.extent = VkExtent3D{kImageWidth, kImageHeight, 1};
|
||||
vkImageInfo.mipLevels = 1;
|
||||
vkImageInfo.arrayLayers = 1;
|
||||
vkImageInfo.samples = VK_SAMPLE_COUNT_1_BIT;
|
||||
vkImageInfo.tiling = VK_IMAGE_TILING_LINEAR;
|
||||
vkImageInfo.usage = VK_IMAGE_USAGE_SAMPLED_BIT;
|
||||
vkImageInfo.sharingMode = VK_SHARING_MODE_EXCLUSIVE;
|
||||
vkImageInfo.initialLayout = VK_IMAGE_LAYOUT_UNDEFINED;
|
||||
|
||||
REPORTER_ASSERT(reporter, fImage == VK_NULL_HANDLE);
|
||||
if (fVkCreateImage(fDevice, &vkImageInfo, nullptr, &fImage) != VK_SUCCESS) {
|
||||
ERRORF(reporter, "Failed to allocate I420 image");
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
VkMemoryRequirements requirements;
|
||||
fVkGetImageMemoryRequirements(fDevice, fImage, &requirements);
|
||||
|
||||
uint32_t memoryTypeIndex = 0;
|
||||
bool foundHeap = false;
|
||||
VkPhysicalDeviceMemoryProperties phyDevMemProps;
|
||||
fVkGetPhysicalDeviceMemoryProperties(fBackendContext.fPhysicalDevice, &phyDevMemProps);
|
||||
for (uint32_t i = 0; i < phyDevMemProps.memoryTypeCount && !foundHeap; ++i) {
|
||||
if (requirements.memoryTypeBits & (1 << i)) {
|
||||
// Map host-visible memory.
|
||||
if (phyDevMemProps.memoryTypes[i].propertyFlags & VK_MEMORY_PROPERTY_HOST_VISIBLE_BIT) {
|
||||
memoryTypeIndex = i;
|
||||
foundHeap = true;
|
||||
}
|
||||
}
|
||||
}
|
||||
if (!foundHeap) {
|
||||
ERRORF(reporter, "Failed to find valid heap for imported memory");
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
VkMemoryAllocateInfo allocInfo = {};
|
||||
allocInfo.sType = VK_STRUCTURE_TYPE_MEMORY_ALLOCATE_INFO;
|
||||
allocInfo.allocationSize = requirements.size;
|
||||
allocInfo.memoryTypeIndex = memoryTypeIndex;
|
||||
|
||||
REPORTER_ASSERT(reporter, fImageMemory == VK_NULL_HANDLE);
|
||||
if (fVkAllocateMemory(fDevice, &allocInfo, nullptr, &fImageMemory) != VK_SUCCESS) {
|
||||
ERRORF(reporter, "Failed to allocate VkDeviceMemory.");
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
void* mappedBuffer;
|
||||
if (fVkMapMemory(fDevice, fImageMemory, 0u, requirements.size, 0u, &mappedBuffer) !=
|
||||
VK_SUCCESS) {
|
||||
ERRORF(reporter, "Failed to map Vulkan memory.");
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
// Write Y channel.
|
||||
VkImageSubresource subresource;
|
||||
subresource.aspectMask = VK_IMAGE_ASPECT_PLANE_0_BIT;
|
||||
subresource.mipLevel = 0;
|
||||
subresource.arrayLayer = 0;
|
||||
|
||||
VkSubresourceLayout yLayout;
|
||||
fVkGetImageSubresourceLayout(fDevice, fImage, &subresource, &yLayout);
|
||||
uint8_t* bufferData = reinterpret_cast<uint8_t*>(mappedBuffer) + yLayout.offset;
|
||||
for (size_t y = 0; y < kImageHeight; ++y) {
|
||||
for (size_t x = 0; x < kImageWidth; ++x) {
|
||||
bufferData[y * yLayout.rowPitch + x] = getY(x, y);
|
||||
}
|
||||
}
|
||||
|
||||
// Write UV channels.
|
||||
subresource.aspectMask = VK_IMAGE_ASPECT_PLANE_1_BIT;
|
||||
VkSubresourceLayout uvLayout;
|
||||
fVkGetImageSubresourceLayout(fDevice, fImage, &subresource, &uvLayout);
|
||||
bufferData = reinterpret_cast<uint8_t*>(mappedBuffer) + uvLayout.offset;
|
||||
for (size_t y = 0; y < kImageHeight / 2; ++y) {
|
||||
for (size_t x = 0; x < kImageWidth / 2; ++x) {
|
||||
bufferData[y * uvLayout.rowPitch + x * 2] = getU(x * 2, y * 2);
|
||||
bufferData[y * uvLayout.rowPitch + x * 2 + 1] = getV(x * 2, y * 2);
|
||||
}
|
||||
}
|
||||
|
||||
VkMappedMemoryRange flushRange;
|
||||
flushRange.sType = VK_STRUCTURE_TYPE_MAPPED_MEMORY_RANGE;
|
||||
flushRange.pNext = nullptr;
|
||||
flushRange.memory = fImageMemory;
|
||||
flushRange.offset = 0;
|
||||
flushRange.size = VK_WHOLE_SIZE;
|
||||
if (fVkFlushMappedMemoryRanges(fDevice, 1, &flushRange) != VK_SUCCESS) {
|
||||
ERRORF(reporter, "Failed to flush buffer memory.");
|
||||
return nullptr;
|
||||
}
|
||||
fVkUnmapMemory(fDevice, fImageMemory);
|
||||
|
||||
// Bind image memory.
|
||||
if (fVkBindImageMemory(fDevice, fImage, fImageMemory, 0u) != VK_SUCCESS) {
|
||||
ERRORF(reporter, "Failed to bind VkImage memory.");
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
// Wrap the image into SkImage.
|
||||
GrVkYcbcrConversionInfo ycbcrInfo(vkImageInfo.format,
|
||||
/*externalFormat=*/0,
|
||||
VK_SAMPLER_YCBCR_MODEL_CONVERSION_YCBCR_709,
|
||||
VK_SAMPLER_YCBCR_RANGE_ITU_NARROW,
|
||||
VK_CHROMA_LOCATION_COSITED_EVEN,
|
||||
VK_CHROMA_LOCATION_COSITED_EVEN,
|
||||
VK_FILTER_LINEAR,
|
||||
false,
|
||||
formatProperties.linearTilingFeatures);
|
||||
GrVkAlloc alloc(fImageMemory, 0 /* offset */, requirements.size, 0 /* flags */);
|
||||
GrVkImageInfo imageInfo(fImage, alloc, VK_IMAGE_TILING_LINEAR, VK_IMAGE_LAYOUT_UNDEFINED,
|
||||
vkImageInfo.format, 1 /* levelCount */, VK_QUEUE_FAMILY_IGNORED,
|
||||
GrProtected::kNo, ycbcrInfo);
|
||||
|
||||
texture = GrBackendTexture(kImageWidth, kImageHeight, imageInfo);
|
||||
sk_sp<SkImage> image = SkImage::MakeFromTexture(fGrContext.get(),
|
||||
texture,
|
||||
kTopLeft_GrSurfaceOrigin,
|
||||
kRGB_888x_SkColorType,
|
||||
kPremul_SkAlphaType,
|
||||
nullptr);
|
||||
|
||||
if (!image) {
|
||||
ERRORF(reporter, "Failed to wrap VkImage with SkImage");
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
return image;
|
||||
}
|
||||
|
||||
static int round_and_clamp(float x) {
|
||||
int r = static_cast<int>(round(x));
|
||||
if (r > 255) return 255;
|
||||
if (r < 0) return 0;
|
||||
return r;
|
||||
}
|
||||
|
||||
DEF_GPUTEST(VkYCbcrSampler_DrawImageWithYcbcrSampler, reporter, options) {
|
||||
VkYcbcrSamplerTestHelper helper;
|
||||
if (!helper.init(reporter)) {
|
||||
return;
|
||||
}
|
||||
|
||||
sk_sp<SkImage> srcImage = helper.createI420Image(reporter);
|
||||
if (!srcImage) {
|
||||
return;
|
||||
}
|
||||
|
||||
sk_sp<SkSurface> surface = SkSurface::MakeRenderTarget(
|
||||
helper.getGrContext(), SkBudgeted::kNo,
|
||||
SkImageInfo::Make(kImageWidth, kImageHeight, kN32_SkColorType, kPremul_SkAlphaType));
|
||||
if (!surface) {
|
||||
ERRORF(reporter, "Failed to create target SkSurface");
|
||||
return;
|
||||
}
|
||||
surface->getCanvas()->drawImage(srcImage, 0, 0);
|
||||
surface->flush();
|
||||
|
||||
std::vector<uint8_t> readbackData(kImageWidth * kImageHeight * 4);
|
||||
if (!surface->readPixels(SkImageInfo::Make(kImageWidth, kImageHeight, kRGBA_8888_SkColorType,
|
||||
kOpaque_SkAlphaType),
|
||||
readbackData.data(), kImageWidth * 4, 0, 0)) {
|
||||
ERRORF(reporter, "Readback failed");
|
||||
return;
|
||||
}
|
||||
|
||||
// Allow resulting color to be off by 1 in each channel as some Vulkan implementations do not
|
||||
// round YCbCr sampler result properly.
|
||||
const int kColorTolerance = 1;
|
||||
|
||||
// Verify results only for pixels with even coordinates, since others use
|
||||
// interpolated U & V channels.
|
||||
for (size_t y = 0; y < kImageHeight; y += 2) {
|
||||
for (size_t x = 0; x < kImageWidth; x += 2) {
|
||||
// createI420Image() initializes the image with VK_SAMPLER_YCBCR_RANGE_ITU_NARROW.
|
||||
float yChannel = (static_cast<float>(getY(x, y)) - 16.0) / 219.0;
|
||||
float uChannel = (static_cast<float>(getU(x, y)) - 128.0) / 224.0;
|
||||
float vChannel = (static_cast<float>(getV(x, y)) - 128.0) / 224.0;
|
||||
|
||||
// BR.709 conversion as specified in
|
||||
// https://www.khronos.org/registry/DataFormat/specs/1.2/dataformat.1.2.html#MODEL_YUV
|
||||
int expectedR = round_and_clamp((yChannel + 1.5748f * vChannel) * 255.0);
|
||||
int expectedG = round_and_clamp((yChannel - 0.13397432f / 0.7152f * uChannel -
|
||||
0.33480248f / 0.7152f * vChannel) *
|
||||
255.0);
|
||||
int expectedB = round_and_clamp((yChannel + 1.8556f * uChannel) * 255.0);
|
||||
|
||||
int r = readbackData[(y * kImageWidth + x) * 4];
|
||||
if (abs(r - expectedR) > kColorTolerance) {
|
||||
ERRORF(reporter, "R should be %d, but is %d at (%d, %d)", expectedR, r, x, y);
|
||||
}
|
||||
|
||||
int g = readbackData[(y * kImageWidth + x) * 4 + 1];
|
||||
if (abs(g - expectedG) > kColorTolerance) {
|
||||
ERRORF(reporter, "G should be %d, but is %d at (%d, %d)", expectedG, g, x, y);
|
||||
}
|
||||
|
||||
int b = readbackData[(y * kImageWidth + x) * 4 + 2];
|
||||
if (abs(b - expectedB) > kColorTolerance) {
|
||||
ERRORF(reporter, "B should be %d, but is %d at (%d, %d)", expectedB, b, x, y);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// Verifies that it's not possible to allocate Ycbcr texture directly.
|
||||
DEF_GPUTEST(VkYCbcrSampler_NoYcbcrSurface, reporter, options) {
|
||||
VkYcbcrSamplerTestHelper helper;
|
||||
if (!helper.init(reporter)) {
|
||||
return;
|
||||
}
|
||||
|
||||
GrBackendTexture texture = helper.getGrContext()->createBackendTexture(
|
||||
kImageWidth, kImageHeight, GrBackendFormat::MakeVk(VK_FORMAT_G8_B8R8_2PLANE_420_UNORM),
|
||||
GrMipMapped::kNo, GrRenderable::kNo, GrProtected::kNo);
|
||||
if (texture.isValid()) {
|
||||
ERRORF(reporter,
|
||||
"GrContext::createBackendTexture() didn't fail as expected for Ycbcr format.");
|
||||
}
|
||||
}
|
||||
|
||||
#endif // SK_SUPPORT_GPU && defined(SK_VULKAN)
|
@ -381,6 +381,7 @@ static bool setup_features(GrVkGetProc getProc, VkInstance inst, VkPhysicalDevic
|
||||
sizeof(VkPhysicalDeviceSamplerYcbcrConversionFeatures));
|
||||
ycbcrFeature->sType = VK_STRUCTURE_TYPE_PHYSICAL_DEVICE_SAMPLER_YCBCR_CONVERSION_FEATURES;
|
||||
ycbcrFeature->pNext = nullptr;
|
||||
ycbcrFeature->samplerYcbcrConversion = VK_TRUE;
|
||||
*tailPNext = ycbcrFeature;
|
||||
tailPNext = &ycbcrFeature->pNext;
|
||||
}
|
||||
|
Loading…
Reference in New Issue
Block a user