Reland "Add support for Ycbcr Conversion Samplers in vulkan."
This is a reland of 6cd74900dab22d2b616cbbeb97bef9608819beee
Original change's description:
> Add support for Ycbcr Conversion Samplers in vulkan.
>
> The only thing missing from this CL is that we need to bake the
> ycbcr conversion samplers into the VkPipeline when we create it. As that
> is a larger change, that will be broken up into a few follow on CLs.
>
> Currently this only supports ycbcr conversion samplers when used with
> external textures.
>
> Bug: skia:
> Change-Id: I23e95b19469093072589ebbbfb7926ab79dcdea9
> Reviewed-on: https://skia-review.googlesource.com/c/164602
> Reviewed-by: Brian Salomon <bsalomon@google.com>
> Reviewed-by: Jim Van Verth <jvanverth@google.com>
> Commit-Queue: Greg Daniel <egdaniel@google.com>
Bug: skia:
Change-Id: I943398077775ef6396fbe5cb9196d23a29128669
Reviewed-on: https://skia-review.googlesource.com/c/173986
Reviewed-by: Greg Daniel <egdaniel@google.com>
Commit-Queue: Greg Daniel <egdaniel@google.com>
diff --git a/src/gpu/vk/GrVkCaps.cpp b/src/gpu/vk/GrVkCaps.cpp
index 6f4cb8d..4797d39 100644
--- a/src/gpu/vk/GrVkCaps.cpp
+++ b/src/gpu/vk/GrVkCaps.cpp
@@ -194,6 +194,27 @@
srcConfig, SkToBool(src->asTextureProxy()));
}
+template<typename T> T* get_extension_feature_struct(const VkPhysicalDeviceFeatures2& features,
+ VkStructureType type) {
+ // All Vulkan structs that could be part of the features chain will start with the
+ // structure type followed by the pNext pointer. We cast to the CommonVulkanHeader
+ // so we can get access to the pNext for the next struct.
+ struct CommonVulkanHeader {
+ VkStructureType sType;
+ void* pNext;
+ };
+
+ void* pNext = features.pNext;
+ while (pNext) {
+ CommonVulkanHeader* header = static_cast<CommonVulkanHeader*>(pNext);
+ if (header->sType == type) {
+ return static_cast<T*>(pNext);
+ }
+ pNext = header->pNext;
+ }
+ return nullptr;
+}
+
void GrVkCaps::init(const GrContextOptions& contextOptions, const GrVkInterface* vkInterface,
VkPhysicalDevice physDev, const VkPhysicalDeviceFeatures2& features,
const GrVkExtensions& extensions) {
@@ -263,6 +284,24 @@
}
#endif
+ auto ycbcrFeatures =
+ get_extension_feature_struct<VkPhysicalDeviceSamplerYcbcrConversionFeatures>(
+ 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()))) {
+ 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());
+
this->initGrCaps(vkInterface, physDev, properties, memoryProperties, features, extensions);
this->initShaderCaps(properties, features);
@@ -376,27 +415,6 @@
return 64;
}
-template<typename T> T* get_extension_feature_struct(const VkPhysicalDeviceFeatures2& features,
- VkStructureType type) {
- // All Vulkan structs that could be part of the features chain will start with the
- // structure type followed by the pNext pointer. We cast to the CommonVulkanHeader
- // so we can get access to the pNext for the next struct.
- struct CommonVulkanHeader {
- VkStructureType sType;
- void* pNext;
- };
-
- void* pNext = features.pNext;
- while (pNext) {
- CommonVulkanHeader* header = static_cast<CommonVulkanHeader*>(pNext);
- if (header->sType == type) {
- return static_cast<T*>(pNext);
- }
- pNext = header->pNext;
- }
- return nullptr;
-}
-
void GrVkCaps::initGrCaps(const GrVkInterface* vkInterface,
VkPhysicalDevice physDev,
const VkPhysicalDeviceProperties& properties,
@@ -828,6 +846,10 @@
const GrBackendTexture& backendTex) const {
GrVkImageInfo vkInfo;
SkAssertResult(backendTex.getVkImageInfo(&vkInfo));
+ if (vkInfo.fYcbcrConversionInfo.isValid()) {
+ SkASSERT(vkInfo.fFormat == VK_FORMAT_UNDEFINED);
+ return GrBackendFormat::MakeVk(vkInfo.fYcbcrConversionInfo);
+ }
return GrBackendFormat::MakeVk(vkInfo.fFormat);
}
diff --git a/src/gpu/vk/GrVkCaps.h b/src/gpu/vk/GrVkCaps.h
index 4a48df8..a20a104 100644
--- a/src/gpu/vk/GrVkCaps.h
+++ b/src/gpu/vk/GrVkCaps.h
@@ -130,6 +130,9 @@
// Returns true if the device supports importing Android hardware buffers into Vulkan memory.
bool supportsAndroidHWBExternalMemory() const { return fSupportsAndroidHWBExternalMemory; }
+ // Returns true if it supports ycbcr conversion for samplers
+ bool supportsYcbcrConversion() const { return fSupportsYcbcrConversion; }
+
/**
* Helpers used by canCopySurface. In all cases if the SampleCnt parameter is zero that means
* the surface is not a render target, otherwise it is the number of samples in the render
@@ -194,6 +197,8 @@
void initConfigTable(const GrVkInterface*, VkPhysicalDevice, const VkPhysicalDeviceProperties&);
void initStencilFormat(const GrVkInterface* iface, VkPhysicalDevice physDev);
+ uint8_t getYcbcrKeyFromYcbcrInfo(const GrVkYcbcrConversionInfo& info);
+
void applyDriverCorrectnessWorkarounds(const VkPhysicalDeviceProperties&);
struct ConfigInfo {
@@ -221,6 +226,8 @@
StencilFormat fPreferredStencilFormat;
+ SkSTArray<1, GrVkYcbcrConversionInfo> fYcbcrInfos;
+
bool fMustDoCopiesFromOrigin = false;
bool fMustSubmitCommandsBeforeCopyOp = false;
bool fMustSleepOnTearDown = false;
@@ -238,6 +245,8 @@
bool fSupportsExternalMemory = false;
bool fSupportsAndroidHWBExternalMemory = false;
+ bool fSupportsYcbcrConversion = false;
+
typedef GrCaps INHERITED;
};
diff --git a/src/gpu/vk/GrVkCopyManager.cpp b/src/gpu/vk/GrVkCopyManager.cpp
index 9d873d5..fd91507 100644
--- a/src/gpu/vk/GrVkCopyManager.cpp
+++ b/src/gpu/vk/GrVkCopyManager.cpp
@@ -261,7 +261,8 @@
GrSamplerState samplerState = GrSamplerState::ClampNearest();
- GrVkSampler* sampler = resourceProv.findOrCreateCompatibleSampler(samplerState);
+ GrVkSampler* sampler = resourceProv.findOrCreateCompatibleSampler(
+ samplerState, GrVkYcbcrConversionInfo());
VkDescriptorImageInfo imageInfo;
memset(&imageInfo, 0, sizeof(VkDescriptorImageInfo));
diff --git a/src/gpu/vk/GrVkGpu.cpp b/src/gpu/vk/GrVkGpu.cpp
index e0b1885..87af8dc 100644
--- a/src/gpu/vk/GrVkGpu.cpp
+++ b/src/gpu/vk/GrVkGpu.cpp
@@ -870,15 +870,17 @@
////////////////////////////////////////////////////////////////////////////////
-static bool check_backend_texture(const GrBackendTexture& backendTex,
- GrPixelConfig config) {
- GrVkImageInfo info;
- if (!backendTex.getVkImageInfo(&info)) {
+static bool check_image_info(const GrVkCaps& caps,
+ const GrVkImageInfo& info,
+ GrPixelConfig config) {
+ if (VK_NULL_HANDLE == info.fImage || VK_NULL_HANDLE == info.fAlloc.fMemory) {
return false;
}
- if (VK_NULL_HANDLE == info.fImage || VK_NULL_HANDLE == info.fAlloc.fMemory) {
- return false;
+ if (info.fYcbcrConversionInfo.isValid()) {
+ if (!caps.supportsYcbcrConversion() || info.fFormat != VK_NULL_HANDLE) {
+ return false;
+ }
}
SkASSERT(GrVkFormatPixelConfigPairIsValid(info.fFormat, config));
@@ -887,7 +889,12 @@
sk_sp<GrTexture> GrVkGpu::onWrapBackendTexture(const GrBackendTexture& backendTex,
GrWrapOwnership ownership, bool purgeImmediately) {
- if (!check_backend_texture(backendTex, backendTex.config())) {
+ GrVkImageInfo imageInfo;
+ if (!backendTex.getVkImageInfo(&imageInfo)) {
+ return nullptr;
+ }
+
+ if (!check_image_info(this->vkCaps(), imageInfo, backendTex.config())) {
return nullptr;
}
@@ -898,10 +905,6 @@
surfDesc.fConfig = backendTex.config();
surfDesc.fSampleCnt = 1;
- GrVkImageInfo imageInfo;
- if (!backendTex.getVkImageInfo(&imageInfo)) {
- return nullptr;
- }
sk_sp<GrVkImageLayout> layout = backendTex.getGrVkImageLayout();
SkASSERT(layout);
return GrVkTexture::MakeWrappedTexture(this, surfDesc, ownership, purgeImmediately,
@@ -911,7 +914,12 @@
sk_sp<GrTexture> GrVkGpu::onWrapRenderableBackendTexture(const GrBackendTexture& backendTex,
int sampleCnt,
GrWrapOwnership ownership) {
- if (!check_backend_texture(backendTex, backendTex.config())) {
+ GrVkImageInfo imageInfo;
+ if (!backendTex.getVkImageInfo(&imageInfo)) {
+ return nullptr;
+ }
+
+ if (!check_image_info(this->vkCaps(), imageInfo, backendTex.config())) {
return nullptr;
}
@@ -922,10 +930,6 @@
surfDesc.fConfig = backendTex.config();
surfDesc.fSampleCnt = this->caps()->getRenderTargetSampleCount(sampleCnt, backendTex.config());
- GrVkImageInfo imageInfo;
- if (!backendTex.getVkImageInfo(&imageInfo)) {
- return nullptr;
- }
sk_sp<GrVkImageLayout> layout = backendTex.getGrVkImageLayout();
SkASSERT(layout);
diff --git a/src/gpu/vk/GrVkImage.h b/src/gpu/vk/GrVkImage.h
index ad2bedb..33e1dfd 100644
--- a/src/gpu/vk/GrVkImage.h
+++ b/src/gpu/vk/GrVkImage.h
@@ -48,6 +48,7 @@
return GrBackendFormat::MakeVk(this->imageFormat());
}
uint32_t mipLevels() const { return fInfo.fLevelCount; }
+ GrVkYcbcrConversionInfo ycbcrConversionInfo() const { return fInfo.fYcbcrConversionInfo; }
const Resource* resource() const { return fResource; }
bool isLinearTiled() const {
return SkToBool(VK_IMAGE_TILING_LINEAR == fInfo.fImageTiling);
diff --git a/src/gpu/vk/GrVkImageView.cpp b/src/gpu/vk/GrVkImageView.cpp
index b737df5..342ce46 100644
--- a/src/gpu/vk/GrVkImageView.cpp
+++ b/src/gpu/vk/GrVkImageView.cpp
@@ -7,16 +7,37 @@
#include "GrVkImageView.h"
#include "GrVkGpu.h"
+#include "GrVkSamplerYcbcrConversion.h"
#include "GrVkUtil.h"
-const GrVkImageView* GrVkImageView::Create(const GrVkGpu* gpu, VkImage image, VkFormat format,
- Type viewType, uint32_t miplevels) {
- VkImageView imageView;
+const GrVkImageView* GrVkImageView::Create(GrVkGpu* gpu, VkImage image, VkFormat format,
+ Type viewType, uint32_t miplevels,
+ const GrVkYcbcrConversionInfo& ycbcrInfo) {
+ void* pNext = nullptr;
+ VkSamplerYcbcrConversionInfo conversionInfo;
+ GrVkSamplerYcbcrConversion* ycbcrConversion = nullptr;
+
+ if (ycbcrInfo.isValid()) {
+ SkASSERT(gpu->vkCaps().supportsYcbcrConversion() && format == VK_FORMAT_UNDEFINED);
+
+ ycbcrConversion =
+ gpu->resourceProvider().findOrCreateCompatibleSamplerYcbcrConversion(ycbcrInfo);
+ if (!ycbcrConversion) {
+ return nullptr;
+ }
+
+ conversionInfo.sType = VK_STRUCTURE_TYPE_SAMPLER_YCBCR_CONVERSION_INFO;
+ conversionInfo.pNext = nullptr;
+ conversionInfo.conversion = ycbcrConversion->ycbcrConversion();
+ pNext = &conversionInfo;
+ }
+
+ VkImageView imageView;
// Create the VkImageView
VkImageViewCreateInfo viewInfo = {
VK_STRUCTURE_TYPE_IMAGE_VIEW_CREATE_INFO, // sType
- NULL, // pNext
+ pNext, // pNext
0, // flags
image, // image
VK_IMAGE_VIEW_TYPE_2D, // viewType
@@ -37,9 +58,20 @@
return nullptr;
}
- return new GrVkImageView(imageView);
+ return new GrVkImageView(imageView, ycbcrConversion);
}
void GrVkImageView::freeGPUData(const GrVkGpu* gpu) const {
GR_VK_CALL(gpu->vkInterface(), DestroyImageView(gpu->device(), fImageView, nullptr));
+
+ if (fYcbcrConversion) {
+ fYcbcrConversion->unref(gpu);
+ }
}
+
+void GrVkImageView::abandonGPUData() const {
+ if (fYcbcrConversion) {
+ fYcbcrConversion->unrefAndAbandon();
+ }
+}
+
diff --git a/src/gpu/vk/GrVkImageView.h b/src/gpu/vk/GrVkImageView.h
index 85fa855..d953249 100644
--- a/src/gpu/vk/GrVkImageView.h
+++ b/src/gpu/vk/GrVkImageView.h
@@ -13,6 +13,9 @@
#include "GrTypes.h"
#include "GrVkResource.h"
+class GrVkSamplerYcbcrConversion;
+struct GrVkYcbcrConversionInfo;
+
class GrVkImageView : public GrVkResource {
public:
enum Type {
@@ -20,8 +23,9 @@
kStencil_Type
};
- static const GrVkImageView* Create(const GrVkGpu* gpu, VkImage image, VkFormat format,
- Type viewType, uint32_t miplevels);
+ static const GrVkImageView* Create(GrVkGpu* gpu, VkImage image, VkFormat format,
+ Type viewType, uint32_t miplevels,
+ const GrVkYcbcrConversionInfo& ycbcrInfo);
VkImageView imageView() const { return fImageView; }
@@ -32,14 +36,17 @@
#endif
private:
- GrVkImageView(VkImageView imageView) : INHERITED(), fImageView(imageView) {}
+ GrVkImageView(VkImageView imageView, GrVkSamplerYcbcrConversion* ycbcrConversion)
+ : INHERITED(), fImageView(imageView), fYcbcrConversion(ycbcrConversion) {}
GrVkImageView(const GrVkImageView&);
GrVkImageView& operator=(const GrVkImageView&);
void freeGPUData(const GrVkGpu* gpu) const override;
+ void abandonGPUData() const override;
VkImageView fImageView;
+ GrVkSamplerYcbcrConversion* fYcbcrConversion;
typedef GrVkResource INHERITED;
};
diff --git a/src/gpu/vk/GrVkPipelineState.cpp b/src/gpu/vk/GrVkPipelineState.cpp
index 1ee2383..53a5a3a 100644
--- a/src/gpu/vk/GrVkPipelineState.cpp
+++ b/src/gpu/vk/GrVkPipelineState.cpp
@@ -243,7 +243,8 @@
GrVkTexture* texture = samplerBindings[i].fTexture;
const GrVkImageView* textureView = texture->textureView();
- GrVkSampler* sampler = gpu->resourceProvider().findOrCreateCompatibleSampler(state);
+ GrVkSampler* sampler = gpu->resourceProvider().findOrCreateCompatibleSampler(
+ state, texture->ycbcrConversionInfo());
VkDescriptorImageInfo imageInfo;
memset(&imageInfo, 0, sizeof(VkDescriptorImageInfo));
diff --git a/src/gpu/vk/GrVkRenderTarget.cpp b/src/gpu/vk/GrVkRenderTarget.cpp
index bca72d5..d1393f9 100644
--- a/src/gpu/vk/GrVkRenderTarget.cpp
+++ b/src/gpu/vk/GrVkRenderTarget.cpp
@@ -148,7 +148,8 @@
// Create Resolve attachment view
resolveAttachmentView = GrVkImageView::Create(gpu, info.fImage, pixelFormat,
- GrVkImageView::kColor_Type, 1);
+ GrVkImageView::kColor_Type, 1,
+ GrVkYcbcrConversionInfo());
if (!resolveAttachmentView) {
GrVkImage::DestroyImageInfo(gpu, &msInfo);
return nullptr;
@@ -161,7 +162,8 @@
// Get color attachment view
const GrVkImageView* colorAttachmentView = GrVkImageView::Create(gpu, colorImage, pixelFormat,
- GrVkImageView::kColor_Type, 1);
+ GrVkImageView::kColor_Type, 1,
+ GrVkYcbcrConversionInfo());
if (!colorAttachmentView) {
if (desc.fSampleCnt > 1) {
resolveAttachmentView->unref(gpu);
diff --git a/src/gpu/vk/GrVkResourceProvider.cpp b/src/gpu/vk/GrVkResourceProvider.cpp
index 9743f31..823c64a 100644
--- a/src/gpu/vk/GrVkResourceProvider.cpp
+++ b/src/gpu/vk/GrVkResourceProvider.cpp
@@ -13,7 +13,6 @@
#include "GrVkGpu.h"
#include "GrVkPipeline.h"
#include "GrVkRenderTarget.h"
-#include "GrVkSampler.h"
#include "GrVkUniformBuffer.h"
#include "GrVkUtil.h"
@@ -166,10 +165,14 @@
return new GrVkDescriptorPool(fGpu, type, count);
}
-GrVkSampler* GrVkResourceProvider::findOrCreateCompatibleSampler(const GrSamplerState& params) {
- GrVkSampler* sampler = fSamplers.find(GrVkSampler::GenerateKey(params));
+GrVkSampler* GrVkResourceProvider::findOrCreateCompatibleSampler(
+ const GrSamplerState& params, const GrVkYcbcrConversionInfo& ycbcrInfo) {
+ GrVkSampler* sampler = fSamplers.find(GrVkSampler::GenerateKey(params, ycbcrInfo));
if (!sampler) {
- sampler = GrVkSampler::Create(fGpu, params);
+ sampler = GrVkSampler::Create(fGpu, params, ycbcrInfo);
+ if (!sampler) {
+ return nullptr;
+ }
fSamplers.add(sampler);
}
SkASSERT(sampler);
@@ -177,6 +180,22 @@
return sampler;
}
+GrVkSamplerYcbcrConversion* GrVkResourceProvider::findOrCreateCompatibleSamplerYcbcrConversion(
+ const GrVkYcbcrConversionInfo& ycbcrInfo) {
+ GrVkSamplerYcbcrConversion* ycbcrConversion =
+ fYcbcrConversions.find(GrVkSamplerYcbcrConversion::GenerateKey(ycbcrInfo));
+ if (!ycbcrConversion) {
+ ycbcrConversion = GrVkSamplerYcbcrConversion::Create(fGpu, ycbcrInfo);
+ if (!ycbcrConversion) {
+ return nullptr;
+ }
+ fYcbcrConversions.add(ycbcrConversion);
+ }
+ SkASSERT(ycbcrConversion);
+ ycbcrConversion->ref();
+ return ycbcrConversion;
+}
+
GrVkPipelineState* GrVkResourceProvider::findOrCreateCompatiblePipelineState(
const GrPipeline& pipeline, const GrPrimitiveProcessor& proc, GrPrimitiveType primitiveType,
VkRenderPass compatibleRenderPass) {
@@ -349,7 +368,7 @@
fRenderPassArray.reset();
// Iterate through all store GrVkSamplers and unref them before resetting the hash.
- SkTDynamicHash<GrVkSampler, uint8_t>::Iter iter(&fSamplers);
+ SkTDynamicHash<GrVkSampler, GrVkSampler::Key>::Iter iter(&fSamplers);
for (; !iter.done(); ++iter) {
(*iter).unref(fGpu);
}
@@ -408,7 +427,7 @@
fRenderPassArray.reset();
// Iterate through all store GrVkSamplers and unrefAndAbandon them before resetting the hash.
- SkTDynamicHash<GrVkSampler, uint8_t>::Iter iter(&fSamplers);
+ SkTDynamicHash<GrVkSampler, GrVkSampler::Key>::Iter iter(&fSamplers);
for (; !iter.done(); ++iter) {
(*iter).unrefAndAbandon();
}
diff --git a/src/gpu/vk/GrVkResourceProvider.h b/src/gpu/vk/GrVkResourceProvider.h
index 4ad49e7..8bfafc2 100644
--- a/src/gpu/vk/GrVkResourceProvider.h
+++ b/src/gpu/vk/GrVkResourceProvider.h
@@ -16,6 +16,8 @@
#include "GrVkPipelineStateBuilder.h"
#include "GrVkRenderPass.h"
#include "GrVkResource.h"
+#include "GrVkSampler.h"
+#include "GrVkSamplerYcbcrConversion.h"
#include "GrVkUtil.h"
#include "SkLRUCache.h"
#include "SkTArray.h"
@@ -31,7 +33,6 @@
class GrVkPipelineState;
class GrVkPrimaryCommandBuffer;
class GrVkRenderTarget;
-class GrVkSampler;
class GrVkSecondaryCommandBuffer;
class GrVkUniformHandler;
@@ -97,9 +98,15 @@
// of our cache of GrVkDescriptorPools.
GrVkDescriptorPool* findOrCreateCompatibleDescriptorPool(VkDescriptorType type, uint32_t count);
- // Finds or creates a compatible GrVkSampler based on the GrSamplerState.
- // The refcount is incremented and a pointer returned.
- GrVkSampler* findOrCreateCompatibleSampler(const GrSamplerState&);
+ // Finds or creates a compatible GrVkSampler based on the GrSamplerState and
+ // GrVkYcbcrConversionInfo. The refcount is incremented and a pointer returned.
+ GrVkSampler* findOrCreateCompatibleSampler(const GrSamplerState&,
+ const GrVkYcbcrConversionInfo& ycbcrInfo);
+
+ // Finds or creates a compatible GrVkSamplerYcbcrConversion based on the GrSamplerState and
+ // GrVkYcbcrConversionInfo. The refcount is incremented and a pointer returned.
+ GrVkSamplerYcbcrConversion* findOrCreateCompatibleSamplerYcbcrConversion(
+ const GrVkYcbcrConversionInfo& ycbcrInfo);
GrVkPipelineState* findOrCreateCompatiblePipelineState(const GrPipeline&,
const GrPrimitiveProcessor&,
@@ -252,7 +259,10 @@
// Stores GrVkSampler objects that we've already created so we can reuse them across multiple
// GrVkPipelineStates
- SkTDynamicHash<GrVkSampler, uint8_t> fSamplers;
+ SkTDynamicHash<GrVkSampler, GrVkSampler::Key> fSamplers;
+
+ // Stores GrVkSamplerYcbcrConversion objects that we've already created so we can reuse them.
+ SkTDynamicHash<GrVkSamplerYcbcrConversion, GrVkSamplerYcbcrConversion::Key> fYcbcrConversions;
// Cache of GrVkPipelineStates
PipelineStateCache* fPipelineStateCache;
diff --git a/src/gpu/vk/GrVkSampler.cpp b/src/gpu/vk/GrVkSampler.cpp
index 2104cb5..97ceadd 100644
--- a/src/gpu/vk/GrVkSampler.cpp
+++ b/src/gpu/vk/GrVkSampler.cpp
@@ -8,6 +8,7 @@
#include "GrVkSampler.h"
#include "GrVkGpu.h"
+#include "GrVkSamplerYcbcrConversion.h"
static inline VkSamplerAddressMode wrap_mode_to_vk_sampler_address(
GrSamplerState::WrapMode wrapMode) {
@@ -23,7 +24,8 @@
return VK_SAMPLER_ADDRESS_MODE_CLAMP_TO_EDGE;
}
-GrVkSampler* GrVkSampler::Create(const GrVkGpu* gpu, const GrSamplerState& samplerState) {
+GrVkSampler* GrVkSampler::Create(GrVkGpu* gpu, const GrSamplerState& samplerState,
+ const GrVkYcbcrConversionInfo& ycbcrInfo) {
static VkFilter vkMinFilterModes[] = {
VK_FILTER_NEAREST,
VK_FILTER_LINEAR,
@@ -38,7 +40,7 @@
VkSamplerCreateInfo createInfo;
memset(&createInfo, 0, sizeof(VkSamplerCreateInfo));
createInfo.sType = VK_STRUCTURE_TYPE_SAMPLER_CREATE_INFO;
- createInfo.pNext = 0;
+ createInfo.pNext = nullptr;
createInfo.flags = 0;
createInfo.magFilter = vkMagFilterModes[static_cast<int>(samplerState.filter())];
createInfo.minFilter = vkMinFilterModes[static_cast<int>(samplerState.filter())];
@@ -62,32 +64,80 @@
createInfo.borderColor = VK_BORDER_COLOR_FLOAT_TRANSPARENT_BLACK;
createInfo.unnormalizedCoordinates = VK_FALSE;
+ VkSamplerYcbcrConversionInfo conversionInfo;
+ GrVkSamplerYcbcrConversion* ycbcrConversion = nullptr;
+ if (ycbcrInfo.isValid()) {
+ SkASSERT(gpu->vkCaps().supportsYcbcrConversion());
+
+ ycbcrConversion =
+ gpu->resourceProvider().findOrCreateCompatibleSamplerYcbcrConversion(ycbcrInfo);
+ if (!ycbcrConversion) {
+ return nullptr;
+ }
+
+ conversionInfo.sType = VK_STRUCTURE_TYPE_SAMPLER_YCBCR_CONVERSION_INFO;
+ conversionInfo.pNext = nullptr;
+ conversionInfo.conversion = ycbcrConversion->ycbcrConversion();
+
+ createInfo.pNext = &conversionInfo;
+
+ const VkFormatFeatureFlags& flags = ycbcrInfo.fExternalFormatFeatures;
+
+ if (!SkToBool(flags & VK_FORMAT_FEATURE_SAMPLED_IMAGE_YCBCR_CONVERSION_LINEAR_FILTER_BIT)) {
+ createInfo.magFilter = VK_FILTER_NEAREST;
+ createInfo.minFilter = VK_FILTER_NEAREST;
+ } else if (
+ !(flags &
+ VK_FORMAT_FEATURE_SAMPLED_IMAGE_YCBCR_CONVERSION_SEPARATE_RECONSTRUCTION_FILTER_BIT)) {
+ createInfo.magFilter = ycbcrInfo.fChromaFilter;
+ createInfo.minFilter = ycbcrInfo.fChromaFilter;
+ }
+
+ // Required values when using ycbcr conversion
+ createInfo.addressModeU = VK_SAMPLER_ADDRESS_MODE_CLAMP_TO_EDGE;
+ createInfo.addressModeV = VK_SAMPLER_ADDRESS_MODE_CLAMP_TO_EDGE;
+ createInfo.addressModeW = VK_SAMPLER_ADDRESS_MODE_CLAMP_TO_EDGE;
+ createInfo.anisotropyEnable = VK_FALSE;
+ createInfo.unnormalizedCoordinates = VK_FALSE;
+ }
+
VkSampler sampler;
GR_VK_CALL_ERRCHECK(gpu->vkInterface(), CreateSampler(gpu->device(),
&createInfo,
nullptr,
&sampler));
- return new GrVkSampler(sampler, GenerateKey(samplerState));
+ return new GrVkSampler(sampler, ycbcrConversion, GenerateKey(samplerState, ycbcrInfo));
}
void GrVkSampler::freeGPUData(const GrVkGpu* gpu) const {
SkASSERT(fSampler);
GR_VK_CALL(gpu->vkInterface(), DestroySampler(gpu->device(), fSampler, nullptr));
+ if (fYcbcrConversion) {
+ fYcbcrConversion->unref(gpu);
+ }
}
-uint8_t GrVkSampler::GenerateKey(const GrSamplerState& samplerState) {
+void GrVkSampler::abandonGPUData() const {
+ if (fYcbcrConversion) {
+ fYcbcrConversion->unrefAndAbandon();
+ }
+}
+
+GrVkSampler::Key GrVkSampler::GenerateKey(const GrSamplerState& samplerState,
+ const GrVkYcbcrConversionInfo& ycbcrInfo) {
const int kTileModeXShift = 2;
const int kTileModeYShift = 4;
SkASSERT(static_cast<int>(samplerState.filter()) <= 3);
- uint8_t key = static_cast<uint8_t>(samplerState.filter());
+ uint8_t samplerKey = static_cast<uint16_t>(samplerState.filter());
SkASSERT(static_cast<int>(samplerState.wrapModeX()) <= 3);
- key |= (static_cast<uint8_t>(samplerState.wrapModeX()) << kTileModeXShift);
+ samplerKey |= (static_cast<uint8_t>(samplerState.wrapModeX()) << kTileModeXShift);
SkASSERT(static_cast<int>(samplerState.wrapModeY()) <= 3);
- key |= (static_cast<uint8_t>(samplerState.wrapModeY()) << kTileModeYShift);
+ samplerKey |= (static_cast<uint8_t>(samplerState.wrapModeY()) << kTileModeYShift);
- return key;
+ return {samplerKey, GrVkSamplerYcbcrConversion::GenerateKey(ycbcrInfo)};
}
+
diff --git a/src/gpu/vk/GrVkSampler.h b/src/gpu/vk/GrVkSampler.h
index ef6e7b8..713bdbc 100644
--- a/src/gpu/vk/GrVkSampler.h
+++ b/src/gpu/vk/GrVkSampler.h
@@ -1,9 +1,9 @@
/*
-* Copyright 2016 Google Inc.
-*
-* Use of this source code is governed by a BSD-style license that can be
-* found in the LICENSE file.
-*/
+ * Copyright 2016 Google Inc.
+ *
+ * Use of this source code is governed by a BSD-style license that can be
+ * found in the LICENSE file.
+ */
#ifndef GrVkSampler_DEFINED
#define GrVkSampler_DEFINED
@@ -11,22 +11,43 @@
#include "GrVkVulkan.h"
#include "GrVkResource.h"
+#include "GrVkSamplerYcbcrConversion.h"
+#include "SkOpts.h"
+#include "vk/GrVkTypes.h"
class GrSamplerState;
class GrVkGpu;
-
class GrVkSampler : public GrVkResource {
public:
- static GrVkSampler* Create(const GrVkGpu* gpu, const GrSamplerState&);
+ static GrVkSampler* Create(GrVkGpu* gpu, const GrSamplerState&, const GrVkYcbcrConversionInfo&);
VkSampler sampler() const { return fSampler; }
- // Helpers for hashing GrVkSampler
- static uint8_t GenerateKey(const GrSamplerState&);
+ struct Key {
+ Key(uint16_t samplerKey, const GrVkSamplerYcbcrConversion::Key& ycbcrKey) {
+ // We must memset here since the GrVkSamplerYcbcrConversion has a 64 bit value which may
+ // force alignment padding to occur in the middle of the Key struct.
+ memset(this, 0, sizeof(Key));
+ fSamplerKey = samplerKey;
+ fYcbcrKey = ycbcrKey;
+ }
+ uint16_t fSamplerKey;
+ GrVkSamplerYcbcrConversion::Key fYcbcrKey;
- static const uint8_t& GetKey(const GrVkSampler& sampler) { return sampler.fKey; }
- static uint32_t Hash(const uint16_t& key) { return key; }
+ bool operator==(const Key& that) const {
+ return this->fSamplerKey == that.fSamplerKey &&
+ this->fYcbcrKey == that.fYcbcrKey;
+ }
+ };
+
+ // Helpers for hashing GrVkSampler
+ static Key GenerateKey(const GrSamplerState&, const GrVkYcbcrConversionInfo&);
+
+ static const Key& GetKey(const GrVkSampler& sampler) { return sampler.fKey; }
+ static uint32_t Hash(const Key& key) {
+ return SkOpts::hash(reinterpret_cast<const uint32_t*>(&key), sizeof(Key));
+ }
#ifdef SK_TRACE_VK_RESOURCES
void dumpInfo() const override {
@@ -35,12 +56,15 @@
#endif
private:
- GrVkSampler(VkSampler sampler, uint16_t key) : INHERITED(), fSampler(sampler), fKey(key) {}
+ GrVkSampler(VkSampler sampler, GrVkSamplerYcbcrConversion* ycbcrConversion, Key key)
+ : INHERITED(), fSampler(sampler), fYcbcrConversion(ycbcrConversion), fKey(key) {}
void freeGPUData(const GrVkGpu* gpu) const override;
+ void abandonGPUData() const override;
- VkSampler fSampler;
- uint8_t fKey;
+ VkSampler fSampler;
+ GrVkSamplerYcbcrConversion* fYcbcrConversion;
+ Key fKey;
typedef GrVkResource INHERITED;
};
diff --git a/src/gpu/vk/GrVkSamplerYcbcrConversion.cpp b/src/gpu/vk/GrVkSamplerYcbcrConversion.cpp
new file mode 100644
index 0000000..bc08ac1
--- /dev/null
+++ b/src/gpu/vk/GrVkSamplerYcbcrConversion.cpp
@@ -0,0 +1,101 @@
+/*
+ * Copyright 2018 Google Inc.
+ *
+ * Use of this source code is governed by a BSD-style license that can be
+ * found in the LICENSE file.
+ */
+
+#include "GrVkSamplerYcbcrConversion.h"
+
+#include "GrVkGpu.h"
+
+GrVkSamplerYcbcrConversion* GrVkSamplerYcbcrConversion::Create(
+ const GrVkGpu* gpu, const GrVkYcbcrConversionInfo& info) {
+ 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;
+ if (info.fXChromaOffset == VK_CHROMA_LOCATION_MIDPOINT ||
+ info.fYChromaOffset == VK_CHROMA_LOCATION_MIDPOINT) {
+ SkASSERT(featureFlags & VK_FORMAT_FEATURE_MIDPOINT_CHROMA_SAMPLES_BIT);
+ }
+ if (info.fXChromaOffset == VK_CHROMA_LOCATION_COSITED_EVEN ||
+ info.fYChromaOffset == VK_CHROMA_LOCATION_COSITED_EVEN) {
+ SkASSERT(featureFlags & VK_FORMAT_FEATURE_COSITED_CHROMA_SAMPLES_BIT);
+ }
+ if (info.fChromaFilter == VK_FILTER_LINEAR) {
+ SkASSERT(featureFlags & VK_FORMAT_FEATURE_SAMPLED_IMAGE_YCBCR_CONVERSION_LINEAR_FILTER_BIT);
+ }
+ if (info.fForceExplicitReconstruction) {
+ SkASSERT(featureFlags &
+ VK_FORMAT_FEATURE_SAMPLED_IMAGE_YCBCR_CONVERSION_CHROMA_RECONSTRUCTION_EXPLICIT_FORCEABLE_BIT);
+ }
+#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.ycbcrModel = info.fYcbcrModel;
+ ycbcrCreateInfo.ycbcrRange = info.fYcbcrRange;
+ // Componets is ignored for external format conversions;
+ // ycbcrCreateInfo.components = {0, 0, 0, 0};
+ ycbcrCreateInfo.xChromaOffset = info.fXChromaOffset;
+ ycbcrCreateInfo.yChromaOffset = info.fYChromaOffset;
+ ycbcrCreateInfo.chromaFilter = info.fChromaFilter;
+ ycbcrCreateInfo.forceExplicitReconstruction = info.fForceExplicitReconstruction;
+
+ 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(const GrVkGpu* gpu) const {
+ SkASSERT(fYcbcrConversion);
+ GR_VK_CALL(gpu->vkInterface(), DestroySamplerYcbcrConversion(gpu->device(), fYcbcrConversion,
+ nullptr));
+}
+
+GrVkSamplerYcbcrConversion::Key GrVkSamplerYcbcrConversion::GenerateKey(
+ const GrVkYcbcrConversionInfo& ycbcrInfo) {
+ SkASSERT(static_cast<int>(ycbcrInfo.fYcbcrModel <= 7));
+ static const int kRangeShift = 3;
+ SkASSERT(static_cast<int>(ycbcrInfo.fYcbcrRange) <= 1);
+ static const int kXChromaOffsetShift = kRangeShift + 1;
+ SkASSERT(static_cast<int>(ycbcrInfo.fXChromaOffset) <= 1);
+ static const int kYChromaOffsetShift = kXChromaOffsetShift + 1;
+ SkASSERT(static_cast<int>(ycbcrInfo.fXChromaOffset) <= 1);
+ static const int kChromaFilterShift = kYChromaOffsetShift + 1;
+ SkASSERT(static_cast<int>(ycbcrInfo.fChromaFilter) <= 1);
+ static const int kReconShift = kChromaFilterShift + 1;
+ SkASSERT(static_cast<int>(ycbcrInfo.fForceExplicitReconstruction) <= 1);
+ GR_STATIC_ASSERT(kReconShift <= 7);
+
+ uint8_t ycbcrKey = static_cast<uint8_t>(ycbcrInfo.fYcbcrModel);
+ ycbcrKey |= (static_cast<uint8_t>(ycbcrInfo.fYcbcrRange) << kRangeShift);
+ ycbcrKey |= (static_cast<uint8_t>(ycbcrInfo.fXChromaOffset) << kXChromaOffsetShift);
+ ycbcrKey |= (static_cast<uint8_t>(ycbcrInfo.fYChromaOffset) << kYChromaOffsetShift);
+ ycbcrKey |= (static_cast<uint8_t>(ycbcrInfo.fChromaFilter) << kChromaFilterShift);
+ ycbcrKey |= (static_cast<uint8_t>(ycbcrInfo.fForceExplicitReconstruction) << kReconShift);
+
+ return {ycbcrInfo.fExternalFormat, ycbcrKey};
+}
+
diff --git a/src/gpu/vk/GrVkSamplerYcbcrConversion.h b/src/gpu/vk/GrVkSamplerYcbcrConversion.h
new file mode 100644
index 0000000..91f6480
--- /dev/null
+++ b/src/gpu/vk/GrVkSamplerYcbcrConversion.h
@@ -0,0 +1,74 @@
+/*
+ * Copyright 2018 Google Inc.
+ *
+ * Use of this source code is governed by a BSD-style license that can be
+ * found in the LICENSE file.
+ */
+
+#ifndef GrVkSamplerYcbcrConverison_DEFINED
+#define GrVkSamplerYcbcrConverison_DEFINED
+
+#include "GrVkVulkan.h"
+
+#include "GrVkResource.h"
+
+#include "SkOpts.h"
+#include "vk/GrVkTypes.h"
+
+class GrVkGpu;
+
+class GrVkSamplerYcbcrConversion : public GrVkResource {
+public:
+ static GrVkSamplerYcbcrConversion* Create(const GrVkGpu* gpu, const GrVkYcbcrConversionInfo&);
+
+ VkSamplerYcbcrConversion ycbcrConversion() const { return fYcbcrConversion; }
+
+ struct Key {
+ Key() : fExternalFormat(0), fConversionKey(0) {}
+ Key(uint64_t externalFormat, uint8_t conversionKey) {
+ memset(this, 0, sizeof(Key));
+ fExternalFormat = externalFormat;
+ fConversionKey = conversionKey;
+ }
+
+ uint64_t fExternalFormat;
+ uint8_t fConversionKey;
+
+ bool operator==(const Key& that) const {
+ return this->fExternalFormat == that.fExternalFormat &&
+ this->fConversionKey == that.fConversionKey;
+ }
+ };
+
+ // Helpers for hashing GrVkSamplerYcbcrConversion
+ static Key GenerateKey(const GrVkYcbcrConversionInfo& ycbcrInfo);
+
+ static const Key& GetKey(const GrVkSamplerYcbcrConversion& ycbcrConversion) {
+ return ycbcrConversion.fKey;
+ }
+ static uint32_t Hash(const Key& key) {
+ return SkOpts::hash(reinterpret_cast<const uint32_t*>(&key), sizeof(Key));
+ }
+
+#ifdef SK_TRACE_VK_RESOURCES
+ void dumpInfo() const override {
+ SkDebugf("GrVkSamplerYcbcrConversion: %d (%d refs)\n", fYcbcrConversion, this->getRefCnt());
+ }
+#endif
+
+private:
+ GrVkSamplerYcbcrConversion(VkSamplerYcbcrConversion ycbcrConversion, Key key)
+ : INHERITED()
+ , fYcbcrConversion(ycbcrConversion)
+ , fKey(key) {}
+
+ void freeGPUData(const GrVkGpu* gpu) const override;
+
+ VkSamplerYcbcrConversion fYcbcrConversion;
+ Key fKey;
+
+ typedef GrVkResource INHERITED;
+};
+
+#endif
+
diff --git a/src/gpu/vk/GrVkStencilAttachment.cpp b/src/gpu/vk/GrVkStencilAttachment.cpp
index 7c5e83e..22549b4 100644
--- a/src/gpu/vk/GrVkStencilAttachment.cpp
+++ b/src/gpu/vk/GrVkStencilAttachment.cpp
@@ -51,7 +51,8 @@
const GrVkImageView* imageView = GrVkImageView::Create(gpu, info.fImage,
format.fInternalFormat,
- GrVkImageView::kStencil_Type, 1);
+ GrVkImageView::kStencil_Type, 1,
+ GrVkYcbcrConversionInfo());
if (!imageView) {
GrVkImage::DestroyImageInfo(gpu, &info);
return nullptr;
diff --git a/src/gpu/vk/GrVkTexture.cpp b/src/gpu/vk/GrVkTexture.cpp
index 0ee7f68..8157433 100644
--- a/src/gpu/vk/GrVkTexture.cpp
+++ b/src/gpu/vk/GrVkTexture.cpp
@@ -76,9 +76,9 @@
return nullptr;
}
- const GrVkImageView* imageView = GrVkImageView::Create(gpu, info.fImage, info.fFormat,
- GrVkImageView::kColor_Type,
- info.fLevelCount);
+ const GrVkImageView* imageView = GrVkImageView::Create(
+ gpu, info.fImage, info.fFormat, GrVkImageView::kColor_Type, info.fLevelCount,
+ info.fYcbcrConversionInfo);
if (!imageView) {
GrVkImage::DestroyImageInfo(gpu, &info);
return nullptr;
@@ -98,9 +98,9 @@
// Wrapped textures require both image and allocation (because they can be mapped)
SkASSERT(VK_NULL_HANDLE != info.fImage && VK_NULL_HANDLE != info.fAlloc.fMemory);
- const GrVkImageView* imageView = GrVkImageView::Create(gpu, info.fImage, info.fFormat,
- GrVkImageView::kColor_Type,
- info.fLevelCount);
+ const GrVkImageView* imageView = GrVkImageView::Create(
+ gpu, info.fImage, info.fFormat, GrVkImageView::kColor_Type, info.fLevelCount,
+ info.fYcbcrConversionInfo);
if (!imageView) {
return nullptr;
}
diff --git a/src/gpu/vk/GrVkTextureRenderTarget.cpp b/src/gpu/vk/GrVkTextureRenderTarget.cpp
index 581dcc0..3656c82 100644
--- a/src/gpu/vk/GrVkTextureRenderTarget.cpp
+++ b/src/gpu/vk/GrVkTextureRenderTarget.cpp
@@ -100,9 +100,9 @@
bool isWrapped) {
VkImage image = info.fImage;
// Create the texture ImageView
- const GrVkImageView* imageView = GrVkImageView::Create(gpu, image, info.fFormat,
- GrVkImageView::kColor_Type,
- info.fLevelCount);
+ const GrVkImageView* imageView = GrVkImageView::Create(
+ gpu, image, info.fFormat, GrVkImageView::kColor_Type, info.fLevelCount,
+ info.fYcbcrConversionInfo);
if (!imageView) {
return nullptr;
}
@@ -141,7 +141,7 @@
// Create resolve attachment view.
resolveAttachmentView = GrVkImageView::Create(gpu, image, pixelFormat,
GrVkImageView::kColor_Type,
- info.fLevelCount);
+ info.fLevelCount, GrVkYcbcrConversionInfo());
if (!resolveAttachmentView) {
GrVkImage::DestroyImageInfo(gpu, &msInfo);
imageView->unref(gpu);
@@ -154,7 +154,8 @@
}
const GrVkImageView* colorAttachmentView = GrVkImageView::Create(gpu, colorImage, pixelFormat,
- GrVkImageView::kColor_Type, 1);
+ GrVkImageView::kColor_Type, 1,
+ GrVkYcbcrConversionInfo());
if (!colorAttachmentView) {
if (desc.fSampleCnt > 1) {
resolveAttachmentView->unref(gpu);
@@ -251,7 +252,8 @@
newInfo.fImage,
pixelFormat,
GrVkImageView::kColor_Type,
- newInfo.fLevelCount);
+ newInfo.fLevelCount,
+ GrVkYcbcrConversionInfo());
if (!resolveAttachmentView) {
return false;
}
@@ -262,7 +264,8 @@
newInfo.fImage,
pixelFormat,
GrVkImageView::kColor_Type,
- 1);
+ 1,
+ GrVkYcbcrConversionInfo());
if (!colorAttachmentView) {
return false;
}
diff --git a/src/gpu/vk/GrVkUtil.cpp b/src/gpu/vk/GrVkUtil.cpp
index 71279cd..674a1b9 100644
--- a/src/gpu/vk/GrVkUtil.cpp
+++ b/src/gpu/vk/GrVkUtil.cpp
@@ -75,6 +75,7 @@
return false;
}
+#ifdef SK_DEBUG
bool GrVkFormatPixelConfigPairIsValid(VkFormat format, GrPixelConfig config) {
switch (format) {
case VK_FORMAT_R8G8B8A8_UNORM:
@@ -113,6 +114,7 @@
return false;
}
}
+#endif
bool GrVkFormatIsSupported(VkFormat format) {
switch (format) {
diff --git a/src/gpu/vk/GrVkUtil.h b/src/gpu/vk/GrVkUtil.h
index 34802fe..78683c5 100644
--- a/src/gpu/vk/GrVkUtil.h
+++ b/src/gpu/vk/GrVkUtil.h
@@ -36,10 +36,12 @@
bool GrVkFormatIsSupported(VkFormat);
+#ifdef SK_DEBUG
/**
* Returns true if the passed in VkFormat and GrPixelConfig are compatible with each other.
*/
bool GrVkFormatPixelConfigPairIsValid(VkFormat, GrPixelConfig);
+#endif
bool GrSampleCountToVkSampleCount(uint32_t samples, VkSampleCountFlagBits* vkSamples);