diff --git a/include/platform/FeaturesVk.h b/include/platform/FeaturesVk.h index bb0c1d40a..6bf477b08 100644 --- a/include/platform/FeaturesVk.h +++ b/include/platform/FeaturesVk.h @@ -122,11 +122,11 @@ struct FeaturesVk : FeatureSetBase // Whether the VkDevice supports the VK_FUCHSIA_external_memory // extension, on which the GL_ANGLE_memory_object_fuchsia extension can be layered. - angle::Feature supportsExternalMemoryFuchsia = { + Feature supportsExternalMemoryFuchsia = { "supports_external_memory_fuchsia", FeatureCategory::VulkanFeatures, "VkDevice supports the VK_FUCHSIA_external_memory extension", &members}; - angle::Feature supportsFilteringPrecision = { + Feature supportsFilteringPrecision = { "supports_filtering_precision_google", FeatureCategory::VulkanFeatures, "VkDevice supports the VK_GOOGLE_sampler_filtering_precision extension", &members}; @@ -172,6 +172,12 @@ struct FeaturesVk : FeatureSetBase "supports_shader_stencil_export", FeatureCategory::VulkanFeatures, "VkDevice supports the VK_EXT_shader_stencil_export extension", &members}; + // Whether the VkDevice supports the VK_KHR_sampler_ycbcr_conversion extension, which is needed + // to support Ycbcr conversion with external images. + Feature supportsYUVSamplerConversion = { + "supports_yuv_sampler_conversion", FeatureCategory::VulkanFeatures, + "VkDevice supports the VK_KHR_sampler_ycbcr_conversion extension", &members}; + // Where VK_EXT_transform_feedback is not support, an emulation path is used. // http://anglebug.com/3205 Feature emulateTransformFeedback = { diff --git a/scripts/code_generation_hashes/Vulkan_format.json b/scripts/code_generation_hashes/Vulkan_format.json index 90dc06e28..1d911820f 100644 --- a/scripts/code_generation_hashes/Vulkan_format.json +++ b/scripts/code_generation_hashes/Vulkan_format.json @@ -4,9 +4,9 @@ "src/libANGLE/renderer/angle_format_map.json": "c79d833aea7007c7d0d51cdaa9b265a6", "src/libANGLE/renderer/vulkan/gen_vk_format_table.py": - "d8a0f2278c09a49049a73930b9da3719", + "54a7374f93f17da1386600027acca7a3", "src/libANGLE/renderer/vulkan/vk_format_map.json": "738c8dc36fbe212669944e88ae918f9c", "src/libANGLE/renderer/vulkan/vk_format_table_autogen.cpp": - "383749c786e957bdbbbce9d8b5c2441a" + "c7e01cbf1eded87f4ed9f3bc8b7a567c" } \ No newline at end of file diff --git a/src/common/vulkan/vk_headers.h b/src/common/vulkan/vk_headers.h index 87316ec8c..cb0edffb9 100644 --- a/src/common/vulkan/vk_headers.h +++ b/src/common/vulkan/vk_headers.h @@ -64,6 +64,10 @@ extern PFN_vkImportFenceFdKHR vkImportFenceFdKHR; extern PFN_vkGetPhysicalDeviceExternalSemaphorePropertiesKHR vkGetPhysicalDeviceExternalSemaphorePropertiesKHR; +// VK_KHR_sampler_ycbcr_conversion +extern PFN_vkCreateSamplerYcbcrConversionKHR vkCreateSamplerYcbcrConversionKHR; +extern PFN_vkDestroySamplerYcbcrConversionKHR vkDestroySamplerYcbcrConversionKHR; + # if defined(ANGLE_PLATFORM_FUCHSIA) // VK_FUCHSIA_imagepipe_surface extern PFN_vkCreateImagePipeSurfaceFUCHSIA vkCreateImagePipeSurfaceFUCHSIA; diff --git a/src/libANGLE/renderer/vulkan/ContextVk.cpp b/src/libANGLE/renderer/vulkan/ContextVk.cpp index c23f9fe91..b37f9f67e 100644 --- a/src/libANGLE/renderer/vulkan/ContextVk.cpp +++ b/src/libANGLE/renderer/vulkan/ContextVk.cpp @@ -3748,6 +3748,7 @@ angle::Result ContextVk::updateActiveTextures(const gl::Context *context) const gl::ActiveTextureMask &activeTextures = executable->getActiveSamplersMask(); const gl::ActiveTextureTypeArray &textureTypes = executable->getActiveSamplerTypes(); + bool haveImmutableSampler = false; for (size_t textureUnit : activeTextures) { gl::Texture *texture = textures[textureUnit]; @@ -3776,6 +3777,11 @@ angle::Result ContextVk::updateActiveTextures(const gl::Context *context) samplerSerial = samplerVk->getSerial(); } + if (textureVk->getImage().hasImmutableSampler()) + { + haveImmutableSampler = true; + } + mActiveTextures[textureUnit].texture = textureVk; mActiveTextures[textureUnit].sampler = samplerVk; // Cache serials from sampler and texture, but re-use texture if no sampler bound @@ -3783,6 +3789,12 @@ angle::Result ContextVk::updateActiveTextures(const gl::Context *context) mActiveTexturesDesc.update(textureUnit, textureVk->getSerial(), samplerSerial); } + if (haveImmutableSampler) + { + ANGLE_TRY(mExecutable->updatePipelineLayout(context, &mActiveTextures)); + invalidateCurrentGraphicsPipeline(); + } + return angle::Result::Continue; } diff --git a/src/libANGLE/renderer/vulkan/ImageVk.cpp b/src/libANGLE/renderer/vulkan/ImageVk.cpp index 18867750b..368196412 100644 --- a/src/libANGLE/renderer/vulkan/ImageVk.cpp +++ b/src/libANGLE/renderer/vulkan/ImageVk.cpp @@ -97,9 +97,13 @@ egl::Error ImageVk::initialize(const egl::Display *display) return egl::EglBadAccess(); } + // start with some reasonable alignment that's safe for the case where intendedFormatID is + // FormatID::NONE + size_t alignment = mImage->getFormat().getValidImageCopyBufferAlignment(); + // Make sure a staging buffer is ready to use to upload data - mImage->initStagingBuffer(renderer, mImage->getFormat().getImageCopyBufferAlignment(), - vk::kStagingBufferFlags, vk::kStagingBufferSize); + mImage->initStagingBuffer(renderer, alignment, vk::kStagingBufferFlags, + vk::kStagingBufferSize); mOwnsImage = false; diff --git a/src/libANGLE/renderer/vulkan/MemoryObjectVk.cpp b/src/libANGLE/renderer/vulkan/MemoryObjectVk.cpp index 0104178f4..8eea7e02e 100644 --- a/src/libANGLE/renderer/vulkan/MemoryObjectVk.cpp +++ b/src/libANGLE/renderer/vulkan/MemoryObjectVk.cpp @@ -245,7 +245,7 @@ angle::Result MemoryObjectVk::createImage(ContextVk *contextVk, VkMemoryPropertyFlags flags = 0; ANGLE_TRY(image->initExternalMemory(contextVk, renderer->getMemoryProperties(), - externalMemoryRequirements, importMemoryInfo, + externalMemoryRequirements, nullptr, importMemoryInfo, renderer->getQueueFamilyIndex(), flags)); return angle::Result::Continue; diff --git a/src/libANGLE/renderer/vulkan/ProgramExecutableVk.cpp b/src/libANGLE/renderer/vulkan/ProgramExecutableVk.cpp index 2063455d7..10a308003 100644 --- a/src/libANGLE/renderer/vulkan/ProgramExecutableVk.cpp +++ b/src/libANGLE/renderer/vulkan/ProgramExecutableVk.cpp @@ -446,9 +446,11 @@ void ProgramExecutableVk::addImageDescriptorSetDesc(const gl::ProgramExecutable } } -void ProgramExecutableVk::addTextureDescriptorSetDesc(const gl::ProgramState &programState, - bool useOldRewriteStructSamplers, - vk::DescriptorSetLayoutDesc *descOut) +void ProgramExecutableVk::addTextureDescriptorSetDesc( + const gl::ProgramState &programState, + bool useOldRewriteStructSamplers, + const gl::ActiveTextureArray *activeTextures, + vk::DescriptorSetLayoutDesc *descOut) { const std::vector &samplerBindings = programState.getSamplerBindings(); const std::vector &uniforms = programState.getUniforms(); @@ -493,8 +495,25 @@ void ProgramExecutableVk::addTextureDescriptorSetDesc(const gl::ProgramState &pr ShaderInterfaceVariableInfo &info = mVariableInfoMap[shaderType][samplerName]; VkShaderStageFlags activeStages = gl_vk::kShaderStageMap[shaderType]; - descOut->update(info.binding, VK_DESCRIPTOR_TYPE_COMBINED_IMAGE_SAMPLER, arraySize, - activeStages, nullptr); + // TODO: https://issuetracker.google.com/issues/158215272: how do we handle array of + // immutable samplers? + GLuint textureUnit = samplerBinding.boundTextureUnits[0]; + if (activeTextures && + ((*activeTextures)[textureUnit].texture->getImage().hasImmutableSampler())) + { + ASSERT(samplerBinding.boundTextureUnits.size() == 1); + // Always take the texture's sampler, that's only way to get to yuv conversion for + // externalFormat + const vk::Sampler &immutableSampler = + (*activeTextures)[textureUnit].texture->getSampler(); + descOut->update(info.binding, VK_DESCRIPTOR_TYPE_COMBINED_IMAGE_SAMPLER, arraySize, + activeStages, &immutableSampler); + } + else + { + descOut->update(info.binding, VK_DESCRIPTOR_TYPE_COMBINED_IMAGE_SAMPLER, arraySize, + activeStages, nullptr); + } } } } @@ -610,7 +629,11 @@ angle::Result ProgramExecutableVk::getComputePipeline(ContextVk *contextVk, return shaderProgram->getComputePipeline(contextVk, getPipelineLayout(), pipelineOut); } -angle::Result ProgramExecutableVk::createPipelineLayout(const gl::Context *glContext) +// updatePipelineLayout is used to create the DescriptorSetLayout(s) and PipelineLayout and update +// them when we discover that an immutable sampler is in use. +angle::Result ProgramExecutableVk::updatePipelineLayout( + const gl::Context *glContext, + gl::ActiveTextureArray *activeTextures) { const gl::State &glState = glContext->getState(); ContextVk *contextVk = vk::GetImpl(glContext); @@ -621,13 +644,12 @@ angle::Result ProgramExecutableVk::createPipelineLayout(const gl::Context *glCon gl::ShaderMap programStates; fillProgramStateMap(contextVk, &programStates); - reset(contextVk); - // Store a reference to the pipeline and descriptor set layouts. This will create them if they // don't already exist in the cache. // Default uniforms and transform feedback: vk::DescriptorSetLayoutDesc uniformsAndXfbSetDesc; + mNumDefaultUniformDescriptors = 0; for (const gl::ShaderType shaderType : linkedShaderStages) { const std::string uniformBlockName = kDefaultUniformNames[shaderType]; @@ -693,7 +715,7 @@ angle::Result ProgramExecutableVk::createPipelineLayout(const gl::Context *glCon const gl::ProgramState *programState = programStates[shaderType]; ASSERT(programState); addTextureDescriptorSetDesc(*programState, contextVk->useOldRewriteStructSamplers(), - &texturesSetDesc); + activeTextures, &texturesSetDesc); } ANGLE_TRY(renderer->getDescriptorSetLayout(contextVk, texturesSetDesc, @@ -721,6 +743,22 @@ angle::Result ProgramExecutableVk::createPipelineLayout(const gl::Context *glCon ANGLE_TRY(renderer->getPipelineLayout(contextVk, pipelineLayoutDesc, mDescriptorSetLayouts, &mPipelineLayout)); + return angle::Result::Continue; +} + +angle::Result ProgramExecutableVk::createPipelineLayout(const gl::Context *glContext) +{ + ContextVk *contextVk = vk::GetImpl(glContext); + RendererVk *renderer = contextVk->getRenderer(); + const gl::ProgramExecutable &glExecutable = getGlExecutable(); + const gl::ShaderBitSet &linkedShaderStages = glExecutable.getLinkedShaderStages(); + gl::ShaderMap programStates; + fillProgramStateMap(contextVk, &programStates); + + reset(contextVk); + + ANGLE_TRY(updatePipelineLayout(glContext, nullptr)); + // Initialize descriptor pools. std::array uniformAndXfbSetSize = { {{VK_DESCRIPTOR_TYPE_UNIFORM_BUFFER_DYNAMIC, @@ -1302,6 +1340,10 @@ angle::Result ProgramExecutableVk::updateTexturesDescriptorSet(ContextVk *contex textureVk->getReadImageViewAndRecordUse(contextVk).getHandle(); } + if (textureVk->getImage().hasImmutableSampler()) + { + imageInfos[arrayElement].sampler = textureVk->getSampler().getHandle(); + } ShaderInterfaceVariableInfoMap &variableInfoMap = mVariableInfoMap[shaderType]; const std::string samplerName = contextVk->getRenderer()->getFeatures().forceOldRewriteStructSamplers.enabled diff --git a/src/libANGLE/renderer/vulkan/ProgramExecutableVk.h b/src/libANGLE/renderer/vulkan/ProgramExecutableVk.h index 4e586e5b1..a1dd65ba0 100644 --- a/src/libANGLE/renderer/vulkan/ProgramExecutableVk.h +++ b/src/libANGLE/renderer/vulkan/ProgramExecutableVk.h @@ -137,6 +137,8 @@ class ProgramExecutableVk const vk::PipelineLayout &getPipelineLayout() const { return mPipelineLayout.get(); } angle::Result createPipelineLayout(const gl::Context *glContext); + angle::Result updatePipelineLayout(const gl::Context *glContext, + gl::ActiveTextureArray *activeTextures); angle::Result updateTexturesDescriptorSet(ContextVk *contextVk); angle::Result updateShaderResourcesDescriptorSet(ContextVk *contextVk, @@ -182,6 +184,7 @@ class ProgramExecutableVk vk::DescriptorSetLayoutDesc *descOut); void addTextureDescriptorSetDesc(const gl::ProgramState &programState, bool useOldRewriteStructSamplers, + const gl::ActiveTextureArray *activeTextures, vk::DescriptorSetLayoutDesc *descOut); void updateDefaultUniformsDescriptorSet( diff --git a/src/libANGLE/renderer/vulkan/RendererVk.cpp b/src/libANGLE/renderer/vulkan/RendererVk.cpp index 9501c991c..d8f2832d3 100644 --- a/src/libANGLE/renderer/vulkan/RendererVk.cpp +++ b/src/libANGLE/renderer/vulkan/RendererVk.cpp @@ -492,6 +492,7 @@ void RendererVk::onDestroy() mPipelineCache.destroy(mDevice); mSamplerCache.destroy(this); + mYuvConversionCache.destroy(this); mTheNullBuffer.destroy(this); mAllocator.destroy(); @@ -886,6 +887,10 @@ void RendererVk::queryDeviceExtensionFeatures(const ExtensionNameList &deviceExt mExternalSemaphoreProperties = {}; mExternalSemaphoreProperties.sType = VK_STRUCTURE_TYPE_EXTERNAL_SEMAPHORE_PROPERTIES; + mSamplerYcbcrConversionFeatures = {}; + mSamplerYcbcrConversionFeatures.sType = + VK_STRUCTURE_TYPE_PHYSICAL_DEVICE_SAMPLER_YCBCR_CONVERSION_FEATURES; + if (!vkGetPhysicalDeviceProperties2KHR || !vkGetPhysicalDeviceFeatures2KHR) { return; @@ -935,6 +940,12 @@ void RendererVk::queryDeviceExtensionFeatures(const ExtensionNameList &deviceExt vk::AddToPNextChain(&deviceProperties, &mExternalMemoryHostProperties); } + // Query Ycbcr conversion properties + if (ExtensionFound(VK_KHR_SAMPLER_YCBCR_CONVERSION_EXTENSION_NAME, deviceExtensionNames)) + { + vk::AddToPNextChain(&deviceFeatures, &mSamplerYcbcrConversionFeatures); + } + // Query subgroup properties vk::AddToPNextChain(&deviceProperties, &mSubgroupProperties); @@ -972,6 +983,13 @@ void RendererVk::queryDeviceExtensionFeatures(const ExtensionNameList &deviceExt mIndexTypeUint8Features.pNext = nullptr; mSubgroupProperties.pNext = nullptr; mExternalMemoryHostProperties.pNext = nullptr; + mLineRasterizationFeatures.pNext = nullptr; + mProvokingVertexFeatures.pNext = nullptr; + mVertexAttributeDivisorFeatures.pNext = nullptr; + mVertexAttributeDivisorProperties.pNext = nullptr; + mTransformFeedbackFeatures.pNext = nullptr; + mIndexTypeUint8Features.pNext = nullptr; + mSamplerYcbcrConversionFeatures.pNext = nullptr; } angle::Result RendererVk::initializeDevice(DisplayVk *displayVk, uint32_t queueFamilyIndex) @@ -1274,6 +1292,12 @@ angle::Result RendererVk::initializeDevice(DisplayVk *displayVk, uint32_t queueF #endif // !defined(ANGLE_SHARED_LIBVULKAN) } + if (getFeatures().supportsYUVSamplerConversion.enabled) + { + enabledDeviceExtensions.push_back(VK_KHR_SAMPLER_YCBCR_CONVERSION_EXTENSION_NAME); + vk::AddToPNextChain(&createInfo, &mSamplerYcbcrConversionFeatures); + } + createInfo.sType = VK_STRUCTURE_TYPE_DEVICE_CREATE_INFO; createInfo.flags = 0; createInfo.queueCreateInfoCount = 1; @@ -1325,6 +1349,10 @@ angle::Result RendererVk::initializeDevice(DisplayVk *displayVk, uint32_t queueF { InitTransformFeedbackEXTFunctions(mDevice); } + if (getFeatures().supportsYUVSamplerConversion.enabled) + { + InitSamplerYcbcrKHRFunctions(mDevice); + } #endif // !defined(ANGLE_SHARED_LIBVULKAN) // Initialize the vulkan pipeline cache. @@ -1730,6 +1758,9 @@ void RendererVk::initFeatures(DisplayVk *displayVk, const ExtensionNameList &dev // Currently disabled by default: http://anglebug.com/4324 ANGLE_FEATURE_CONDITION(&mFeatures, enableCommandProcessingThread, false); + ANGLE_FEATURE_CONDITION(&mFeatures, supportsYUVSamplerConversion, + mSamplerYcbcrConversionFeatures.samplerYcbcrConversion != VK_FALSE); + angle::PlatformMethods *platform = ANGLEPlatformCurrent(); platform->overrideFeaturesVk(platform, &mFeatures); diff --git a/src/libANGLE/renderer/vulkan/RendererVk.h b/src/libANGLE/renderer/vulkan/RendererVk.h index d79528303..cd559f6f5 100644 --- a/src/libANGLE/renderer/vulkan/RendererVk.h +++ b/src/libANGLE/renderer/vulkan/RendererVk.h @@ -247,6 +247,7 @@ class RendererVk : angle::NonCopyable bool enableDebugUtils() const { return mEnableDebugUtils; } SamplerCache &getSamplerCache() { return mSamplerCache; } + SamplerYcbcrConversionCache &getYuvConversionCache() { return mYuvConversionCache; } vk::ActiveHandleCounter &getActiveHandleCounts() { return mActiveHandleCounts; } // Queue commands to worker thread for processing @@ -309,6 +310,7 @@ class RendererVk : angle::NonCopyable VkPhysicalDeviceExternalMemoryHostPropertiesEXT mExternalMemoryHostProperties; VkExternalFenceProperties mExternalFenceProperties; VkExternalSemaphoreProperties mExternalSemaphoreProperties; + VkPhysicalDeviceSamplerYcbcrConversionFeatures mSamplerYcbcrConversionFeatures; std::vector mQueueFamilyProperties; std::mutex mQueueMutex; angle::PackedEnumMap mQueues; @@ -382,6 +384,7 @@ class RendererVk : angle::NonCopyable vk::Allocator mAllocator; SamplerCache mSamplerCache; + SamplerYcbcrConversionCache mYuvConversionCache; vk::ActiveHandleCounter mActiveHandleCounts; // Vulkan does not allow binding a null vertex buffer. We use a dummy as a placeholder. diff --git a/src/libANGLE/renderer/vulkan/SamplerVk.cpp b/src/libANGLE/renderer/vulkan/SamplerVk.cpp index 3351aae6a..25f15caf5 100644 --- a/src/libANGLE/renderer/vulkan/SamplerVk.cpp +++ b/src/libANGLE/renderer/vulkan/SamplerVk.cpp @@ -39,7 +39,7 @@ angle::Result SamplerVk::syncState(const gl::Context *context, const bool dirty) mSampler.reset(); } - vk::SamplerDesc desc(mState, false); + vk::SamplerDesc desc(mState, false, 0); ANGLE_TRY(renderer->getSamplerCache().getSampler(contextVk, desc, &mSampler)); // Regenerate the serial on a sampler change. diff --git a/src/libANGLE/renderer/vulkan/SurfaceVk.cpp b/src/libANGLE/renderer/vulkan/SurfaceVk.cpp index b14f7cf14..17a6f6432 100644 --- a/src/libANGLE/renderer/vulkan/SurfaceVk.cpp +++ b/src/libANGLE/renderer/vulkan/SurfaceVk.cpp @@ -210,9 +210,9 @@ angle::Result OffscreenSurfaceVk::AttachmentImage::initializeWithExternalMemory( image.getImage().getMemoryRequirements(renderer->getDevice(), &externalMemoryRequirements); VkMemoryPropertyFlags flags = 0; - ANGLE_TRY(image.initExternalMemory(displayVk, renderer->getMemoryProperties(), - externalMemoryRequirements, &importMemoryHostPointerInfo, - VK_QUEUE_FAMILY_EXTERNAL, flags)); + ANGLE_TRY(image.initExternalMemory( + displayVk, renderer->getMemoryProperties(), externalMemoryRequirements, nullptr, + &importMemoryHostPointerInfo, VK_QUEUE_FAMILY_EXTERNAL, flags)); return angle::Result::Continue; } diff --git a/src/libANGLE/renderer/vulkan/TextureVk.cpp b/src/libANGLE/renderer/vulkan/TextureVk.cpp index 358d85893..82b1790cc 100644 --- a/src/libANGLE/renderer/vulkan/TextureVk.cpp +++ b/src/libANGLE/renderer/vulkan/TextureVk.cpp @@ -1884,7 +1884,8 @@ angle::Result TextureVk::syncState(const gl::Context *context, mImage->getLevelCount(), layerCount)); } - vk::SamplerDesc samplerDesc(mState.getSamplerState(), mState.isStencilMode()); + vk::SamplerDesc samplerDesc(mState.getSamplerState(), mState.isStencilMode(), + mImage->getExternalFormat()); ANGLE_TRY(renderer->getSamplerCache().getSampler(contextVk, samplerDesc, &mSampler)); // Regenerate the serial on a sampler change. diff --git a/src/libANGLE/renderer/vulkan/TextureVk.h b/src/libANGLE/renderer/vulkan/TextureVk.h index 9d3e1bdd4..559505745 100644 --- a/src/libANGLE/renderer/vulkan/TextureVk.h +++ b/src/libANGLE/renderer/vulkan/TextureVk.h @@ -413,7 +413,7 @@ class TextureVk : public TextureImpl, public angle::ObserverInterface // reallocated independently of |mImage| on state changes. vk::ImageViewHelper mImageViews; - // |mSampler| contains the relevant Vulkan sampler states reprensenting the OpenGL Texture + // |mSampler| contains the relevant Vulkan sampler states representing the OpenGL Texture // sampling states for the Texture. vk::BindingPointer mSampler; diff --git a/src/libANGLE/renderer/vulkan/android/HardwareBufferImageSiblingVkAndroid.cpp b/src/libANGLE/renderer/vulkan/android/HardwareBufferImageSiblingVkAndroid.cpp index d0a604d53..01cc8673a 100644 --- a/src/libANGLE/renderer/vulkan/android/HardwareBufferImageSiblingVkAndroid.cpp +++ b/src/libANGLE/renderer/vulkan/android/HardwareBufferImageSiblingVkAndroid.cpp @@ -70,7 +70,18 @@ egl::Error HardwareBufferImageSiblingVkAndroid::ValidateHardwareBuffer(RendererV return egl::EglBadParameter() << "Failed to query AHardwareBuffer properties"; } - if (!HasFullTextureFormatSupport(renderer, bufferFormatProperties.format)) + if (bufferFormatProperties.format == VK_FORMAT_UNDEFINED) + { + ASSERT(bufferFormatProperties.externalFormat != 0); + // We must have an external format, check that it supports texture sampling + if (!(bufferFormatProperties.formatFeatures & VK_FORMAT_FEATURE_SAMPLED_IMAGE_BIT)) + { + return egl::EglBadParameter() + << "Sampling from AHardwareBuffer externalFormat 0x" << std::hex + << bufferFormatProperties.externalFormat << " is unsupported "; + } + } + else if (!HasFullTextureFormatSupport(renderer, bufferFormatProperties.format)) { return egl::EglBadParameter() << "AHardwareBuffer format does not support enough features to use as a texture."; @@ -151,18 +162,35 @@ angle::Result HardwareBufferImageSiblingVkAndroid::initImpl(DisplayVk *displayVk ANGLE_VK_TRY(displayVk, vkGetAndroidHardwareBufferPropertiesANDROID(device, hardwareBuffer, &bufferProperties)); - const vk::Format &vkFormat = renderer->getFormat(internalFormat); - const angle::Format &imageFormat = vkFormat.actualImageFormat(); VkExternalFormatANDROID externalFormat = {}; externalFormat.sType = VK_STRUCTURE_TYPE_EXTERNAL_FORMAT_ANDROID; externalFormat.externalFormat = 0; + const vk::Format &vkFormat = renderer->getFormat(internalFormat); + const vk::Format &externalVkFormat = renderer->getFormat(angle::FormatID::NONE); + const angle::Format &imageFormat = vkFormat.actualImageFormat(); + bool isDepthOrStencilFormat = imageFormat.hasDepthOrStencilBits(); + + // Query AHB description and do the following - + // 1. Derive VkImageTiling mode based on AHB usage flags + // 2. Map AHB usage flags to VkImageUsageFlags + AHardwareBuffer_Desc ahbDescription; + AHardwareBuffer_describe(hardwareBuffer, &ahbDescription); + VkImageTiling imageTilingMode = AhbDescUsageToVkImageTiling(ahbDescription); + VkImageUsageFlags usage = AhbDescUsageToVkImageUsage(ahbDescription, isDepthOrStencilFormat); + if (bufferFormatProperties.format == VK_FORMAT_UNDEFINED) { externalFormat.externalFormat = bufferFormatProperties.externalFormat; + + // VkImageCreateInfo struct: If the pNext chain includes a VkExternalFormatANDROID structure + // whose externalFormat member is not 0, usage must not include any usages except + // VK_IMAGE_USAGE_SAMPLED_BIT + usage = VK_IMAGE_USAGE_SAMPLED_BIT; } VkExternalMemoryImageCreateInfo externalMemoryImageCreateInfo = {}; + externalMemoryImageCreateInfo.sType = VK_STRUCTURE_TYPE_EXTERNAL_MEMORY_IMAGE_CREATE_INFO; externalMemoryImageCreateInfo.pNext = &externalFormat; externalMemoryImageCreateInfo.handleTypes = @@ -173,19 +201,12 @@ angle::Result HardwareBufferImageSiblingVkAndroid::initImpl(DisplayVk *displayVk mImage = new vk::ImageHelper(); - // Query AHB description and do the following - - // 1. Derive VkImageTiling mode based on AHB usage flags - // 2. Map AHB usage flags to VkImageUsageFlags - AHardwareBuffer_Desc ahbDescription; - AHardwareBuffer_describe(hardwareBuffer, &ahbDescription); - VkImageTiling imageTilingMode = AhbDescUsageToVkImageTiling(ahbDescription); - VkImageUsageFlags usage = AhbDescUsageToVkImageUsage( - ahbDescription, (imageFormat.depthBits > 0 || imageFormat.stencilBits > 0)); - mImage->setTilingMode(imageTilingMode); ANGLE_TRY(mImage->initExternal( - displayVk, gl::TextureType::_2D, vkExtents, vkFormat, 1, usage, vk::kVkImageCreateFlagsNone, - vk::ImageLayout::ExternalPreInitialized, &externalMemoryImageCreateInfo, 0, 0, 1, 1)); + displayVk, gl::TextureType::_2D, vkExtents, + bufferFormatProperties.format == VK_FORMAT_UNDEFINED ? externalVkFormat : vkFormat, 1, + usage, vk::kVkImageCreateFlagsNone, vk::ImageLayout::ExternalPreInitialized, + &externalMemoryImageCreateInfo, 0, 0, 1, 1)); VkImportAndroidHardwareBufferInfoANDROID importHardwareBufferInfo = {}; importHardwareBufferInfo.sType = VK_STRUCTURE_TYPE_IMPORT_ANDROID_HARDWARE_BUFFER_INFO_ANDROID; @@ -203,9 +224,35 @@ angle::Result HardwareBufferImageSiblingVkAndroid::initImpl(DisplayVk *displayVk externalMemoryRequirements.memoryTypeBits = bufferProperties.memoryTypeBits; VkMemoryPropertyFlags flags = VK_MEMORY_PROPERTY_DEVICE_LOCAL_BIT; - ANGLE_TRY(mImage->initExternalMemory(displayVk, renderer->getMemoryProperties(), - externalMemoryRequirements, &dedicatedAllocInfo, - VK_QUEUE_FAMILY_FOREIGN_EXT, flags)); + if (bufferFormatProperties.format == VK_FORMAT_UNDEFINED) + { + // Note from Vulkan spec: Since GL_OES_EGL_image_external does not require the same sampling + // and conversion calculations as Vulkan does, achieving identical results between APIs may + // not be possible on some implementations. + ANGLE_VK_CHECK(displayVk, renderer->getFeatures().supportsYUVSamplerConversion.enabled, + VK_ERROR_FEATURE_NOT_PRESENT); + ASSERT(externalFormat.pNext == nullptr); + VkSamplerYcbcrConversionCreateInfo yuvConversionInfo = {}; + yuvConversionInfo.sType = VK_STRUCTURE_TYPE_SAMPLER_YCBCR_CONVERSION_CREATE_INFO; + yuvConversionInfo.pNext = &externalFormat; + yuvConversionInfo.format = VK_FORMAT_UNDEFINED; + yuvConversionInfo.xChromaOffset = bufferFormatProperties.suggestedXChromaOffset; + yuvConversionInfo.yChromaOffset = bufferFormatProperties.suggestedYChromaOffset; + yuvConversionInfo.ycbcrModel = bufferFormatProperties.suggestedYcbcrModel; + yuvConversionInfo.ycbcrRange = bufferFormatProperties.suggestedYcbcrRange; + yuvConversionInfo.chromaFilter = VK_FILTER_LINEAR; + yuvConversionInfo.components = bufferFormatProperties.samplerYcbcrConversionComponents; + + ANGLE_TRY(mImage->initExternalMemory( + displayVk, renderer->getMemoryProperties(), externalMemoryRequirements, + &yuvConversionInfo, &dedicatedAllocInfo, VK_QUEUE_FAMILY_FOREIGN_EXT, flags)); + } + else + { + ANGLE_TRY(mImage->initExternalMemory( + displayVk, renderer->getMemoryProperties(), externalMemoryRequirements, nullptr, + &dedicatedAllocInfo, VK_QUEUE_FAMILY_FOREIGN_EXT, flags)); + } constexpr uint32_t kColorRenderableRequiredBits = VK_FORMAT_FEATURE_COLOR_ATTACHMENT_BIT; constexpr uint32_t kDepthStencilRenderableRequiredBits = VK_FORMAT_FEATURE_COLOR_ATTACHMENT_BIT; diff --git a/src/libANGLE/renderer/vulkan/gen_vk_format_table.py b/src/libANGLE/renderer/vulkan/gen_vk_format_table.py index 8981df7d9..1dee510bf 100644 --- a/src/libANGLE/renderer/vulkan/gen_vk_format_table.py +++ b/src/libANGLE/renderer/vulkan/gen_vk_format_table.py @@ -144,7 +144,7 @@ def gen_format_case(angle, internal_format, vk_json_data): args = dict( format_id=angle, internal_format=internal_format, image_template="", buffer_template="") - if ((angle not in vk_map) and (angle not in vk_fallbacks)) or angle == 'NONE': + if ((angle not in vk_map) and (angle not in vk_fallbacks)): return empty_format_entry_template.format(**args) # get_formats returns override format (if any) + fallbacks diff --git a/src/libANGLE/renderer/vulkan/vk_cache_utils.cpp b/src/libANGLE/renderer/vulkan/vk_cache_utils.cpp index 839fe52f5..b187a665e 100644 --- a/src/libANGLE/renderer/vulkan/vk_cache_utils.cpp +++ b/src/libANGLE/renderer/vulkan/vk_cache_utils.cpp @@ -14,6 +14,7 @@ #include "common/vulkan/vk_google_filtering_precision.h" #include "libANGLE/BlobCache.h" #include "libANGLE/VertexAttribute.h" +#include "libANGLE/renderer/vulkan/DisplayVk.h" #include "libANGLE/renderer/vulkan/FramebufferVk.h" #include "libANGLE/renderer/vulkan/ProgramVk.h" #include "libANGLE/renderer/vulkan/RendererVk.h" @@ -1741,9 +1742,11 @@ SamplerDesc::SamplerDesc(const SamplerDesc &other) = default; SamplerDesc &SamplerDesc::operator=(const SamplerDesc &rhs) = default; -SamplerDesc::SamplerDesc(const gl::SamplerState &samplerState, bool stencilMode) +SamplerDesc::SamplerDesc(const gl::SamplerState &samplerState, + bool stencilMode, + uint64_t externalFormat) { - update(samplerState, stencilMode); + update(samplerState, stencilMode, externalFormat); } void SamplerDesc::reset() @@ -1752,6 +1755,7 @@ void SamplerDesc::reset() mMaxAnisotropy = 0.0f; mMinLod = 0.0f; mMaxLod = 0.0f; + mExternalFormat = 0; mMagFilter = 0; mMinFilter = 0; mMipmapMode = 0; @@ -1760,16 +1764,23 @@ void SamplerDesc::reset() mAddressModeW = 0; mCompareEnabled = 0; mCompareOp = 0; - mReserved = 0; + mReserved[0] = 0; + mReserved[1] = 0; + mReserved[2] = 0; } -void SamplerDesc::update(const gl::SamplerState &samplerState, bool stencilMode) +void SamplerDesc::update(const gl::SamplerState &samplerState, + bool stencilMode, + uint64_t externalFormat) { mMipLodBias = 0.0f; mMaxAnisotropy = samplerState.getMaxAnisotropy(); mMinLod = samplerState.getMinLod(); mMaxLod = samplerState.getMaxLod(); + // GL has no notion of external format, this must be provided from metadata from the image + mExternalFormat = externalFormat; + bool compareEnable = samplerState.getCompareMode() == GL_COMPARE_REF_TO_TEXTURE; VkCompareOp compareOp = gl_vk::GetCompareOp(samplerState.getCompareFunc()); // When sampling from stencil, deqp tests expect texture compare to have no effect @@ -1799,7 +1810,9 @@ void SamplerDesc::update(const gl::SamplerState &samplerState, bool stencilMode) mMaxLod = 0.25f; } - mReserved = 0; + mReserved[0] = 0; + mReserved[1] = 0; + mReserved[2] = 0; } angle::Result SamplerDesc::init(ContextVk *contextVk, vk::Sampler *sampler) const @@ -1841,6 +1854,25 @@ angle::Result SamplerDesc::init(ContextVk *contextVk, vk::Sampler *sampler) cons vk::AddToPNextChain(&createInfo, &filteringInfo); } + VkSamplerYcbcrConversionInfo yuvConversionInfo = {}; + if (mExternalFormat) + { + ASSERT((contextVk->getRenderer()->getFeatures().supportsYUVSamplerConversion.enabled)); + yuvConversionInfo.sType = VK_STRUCTURE_TYPE_SAMPLER_YCBCR_CONVERSION_INFO; + yuvConversionInfo.pNext = nullptr; + yuvConversionInfo.conversion = + contextVk->getRenderer()->getYuvConversionCache().getYuvConversionFromExternalFormat( + mExternalFormat); + vk::AddToPNextChain(&createInfo, &yuvConversionInfo); + + // Vulkan spec requires these settings: + 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; + } + ANGLE_VK_TRY(contextVk, sampler->init(contextVk->getDevice(), createInfo)); return angle::Result::Continue; @@ -2167,6 +2199,73 @@ angle::Result PipelineLayoutCache::getPipelineLayout( return angle::Result::Continue; } +// YuvConversionCache implementation +SamplerYcbcrConversionCache::SamplerYcbcrConversionCache() = default; + +SamplerYcbcrConversionCache::~SamplerYcbcrConversionCache() +{ + ASSERT(mPayload.empty()); +} + +void SamplerYcbcrConversionCache::destroy(RendererVk *renderer) +{ + VkDevice device = renderer->getDevice(); + + for (auto &iter : mPayload) + { + vk::RefCountedSamplerYcbcrConversion &yuvSampler = iter.second; + ASSERT(!yuvSampler.isReferenced()); + yuvSampler.get().destroy(device); + + renderer->getActiveHandleCounts().onDeallocate(vk::HandleType::SamplerYcbcrConversion); + } + + mPayload.clear(); +} + +angle::Result SamplerYcbcrConversionCache::getYuvConversion( + vk::Context *context, + uint64_t externalFormat, + const VkSamplerYcbcrConversionCreateInfo &yuvConversionCreateInfo, + vk::BindingPointer *yuvConversionOut) +{ + const auto iter = mPayload.find(externalFormat); + if (iter != mPayload.end()) + { + vk::RefCountedSamplerYcbcrConversion &yuvConversion = iter->second; + yuvConversionOut->set(&yuvConversion); + return angle::Result::Continue; + } + + vk::SamplerYcbcrConversion wrappedYuvConversion; + ANGLE_VK_TRY(context, wrappedYuvConversion.init(context->getDevice(), yuvConversionCreateInfo)); + + auto insertedItem = mPayload.emplace( + externalFormat, vk::RefCountedSamplerYcbcrConversion(std::move(wrappedYuvConversion))); + vk::RefCountedSamplerYcbcrConversion &insertedYuvConversion = insertedItem.first->second; + yuvConversionOut->set(&insertedYuvConversion); + + context->getRenderer()->getActiveHandleCounts().onAllocate( + vk::HandleType::SamplerYcbcrConversion); + + return angle::Result::Continue; +} + +VkSamplerYcbcrConversion SamplerYcbcrConversionCache::getYuvConversionFromExternalFormat( + uint64_t externalFormat) const +{ + const auto iter = mPayload.find(externalFormat); + if (iter != mPayload.end()) + { + const vk::RefCountedSamplerYcbcrConversion &yuvConversion = iter->second; + return yuvConversion.get().getHandle(); + } + + // Should never get here if we have a valid externalFormat. + UNREACHABLE(); + return VK_NULL_HANDLE; +} + // SamplerCache implementation. SamplerCache::SamplerCache() = default; diff --git a/src/libANGLE/renderer/vulkan/vk_cache_utils.h b/src/libANGLE/renderer/vulkan/vk_cache_utils.h index 77b075558..e2b74cfd9 100644 --- a/src/libANGLE/renderer/vulkan/vk_cache_utils.h +++ b/src/libANGLE/renderer/vulkan/vk_cache_utils.h @@ -26,9 +26,10 @@ enum class ImageLayout; using RenderPassAndSerial = ObjectAndSerial; using PipelineAndSerial = ObjectAndSerial; -using RefCountedDescriptorSetLayout = RefCounted; -using RefCountedPipelineLayout = RefCounted; -using RefCountedSampler = RefCounted; +using RefCountedDescriptorSetLayout = RefCounted; +using RefCountedPipelineLayout = RefCounted; +using RefCountedSampler = RefCounted; +using RefCountedSamplerYcbcrConversion = RefCounted; // Helper macro that casts to a bitfield type then verifies no bits were dropped. #define SetBitField(lhs, rhs) \ @@ -609,13 +610,13 @@ class SamplerDesc final { public: SamplerDesc(); - explicit SamplerDesc(const gl::SamplerState &samplerState, bool stencilMode); + SamplerDesc(const gl::SamplerState &samplerState, bool stencilMode, uint64_t externalFormat); ~SamplerDesc(); SamplerDesc(const SamplerDesc &other); SamplerDesc &operator=(const SamplerDesc &rhs); - void update(const gl::SamplerState &samplerState, bool stencilMode); + void update(const gl::SamplerState &samplerState, bool stencilMode, uint64_t externalFormat); void reset(); angle::Result init(ContextVk *contextVk, vk::Sampler *sampler) const; @@ -630,6 +631,14 @@ class SamplerDesc final float mMinLod; float mMaxLod; + // If the sampler needs to convert the image content (e.g. from YUV to RGB) then mExternalFormat + // will be non-zero and match the external format as returned from + // vkGetAndroidHardwareBufferPropertiesANDROID. + // The externalFormat is guaranteed to be unique and any image with the same externalFormat can + // use the same conversion sampler. Thus externalFormat works as a Serial() used elsewhere in + // ANGLE. + uint64_t mExternalFormat; + // 16 bits for modes + states. // 1 bit per filter (only 2 possible values in GL: linear/nearest) uint16_t mMagFilter : 1; @@ -649,12 +658,11 @@ class SamplerDesc final // Border color and unnormalized coordinates implicitly set to contants. - // 16 extra bits reserved for future use. - uint16_t mReserved; + // 48 extra bits reserved for future use. + uint16_t mReserved[3]; }; -// Total size: 160 bits == 20 bytes. -static_assert(sizeof(SamplerDesc) == 20, "Unexpected SamplerDesc size"); +static_assert(sizeof(SamplerDesc) == 32, "Unexpected SamplerDesc size"); // Disable warnings about struct padding. ANGLE_DISABLE_STRUCT_PADDING_WARNINGS @@ -1051,6 +1059,26 @@ class SamplerCache final : angle::NonCopyable std::unordered_map mPayload; }; +// YuvConversion Cache +class SamplerYcbcrConversionCache final : angle::NonCopyable +{ + public: + SamplerYcbcrConversionCache(); + ~SamplerYcbcrConversionCache(); + + void destroy(RendererVk *render); + + angle::Result getYuvConversion( + vk::Context *context, + uint64_t externalFormat, + const VkSamplerYcbcrConversionCreateInfo &yuvConversionCreateInfo, + vk::BindingPointer *yuvConversionOut); + VkSamplerYcbcrConversion getYuvConversionFromExternalFormat(uint64_t externalFormat) const; + + private: + std::unordered_map mPayload; +}; + // Some descriptor set and pipeline layout constants. // // The set/binding assignment is done as following: diff --git a/src/libANGLE/renderer/vulkan/vk_format_table_autogen.cpp b/src/libANGLE/renderer/vulkan/vk_format_table_autogen.cpp index f7fb1e77b..3640bece4 100644 --- a/src/libANGLE/renderer/vulkan/vk_format_table_autogen.cpp +++ b/src/libANGLE/renderer/vulkan/vk_format_table_autogen.cpp @@ -1145,7 +1145,15 @@ void Format::initialize(RendererVk *renderer, const angle::Format &angleFormat) break; case angle::FormatID::NONE: - // This format is not implemented in Vulkan. + internalFormat = GL_NONE; + actualImageFormatID = angle::FormatID::NONE; + vkImageFormat = VK_FORMAT_UNDEFINED; + imageInitializerFunction = nullptr; + actualBufferFormatID = angle::FormatID::NONE; + vkBufferFormat = VK_FORMAT_UNDEFINED; + vkBufferFormatIsPacked = false; + vertexLoadFunction = nullptr; + vertexLoadRequiresConversion = false; break; case angle::FormatID::PVRTC1_RGBA_2BPP_UNORM_BLOCK: diff --git a/src/libANGLE/renderer/vulkan/vk_format_utils.cpp b/src/libANGLE/renderer/vulkan/vk_format_utils.cpp index 761b18e1b..0df178ac1 100644 --- a/src/libANGLE/renderer/vulkan/vk_format_utils.cpp +++ b/src/libANGLE/renderer/vulkan/vk_format_utils.cpp @@ -189,6 +189,13 @@ size_t Format::getImageCopyBufferAlignment() const return alignment; } +size_t Format::getValidImageCopyBufferAlignment() const +{ + constexpr size_t kMinimumAlignment = 16; + return (intendedFormatID == angle::FormatID::NONE) ? kMinimumAlignment + : getImageCopyBufferAlignment(); +} + bool Format::hasEmulatedImageChannels() const { const angle::Format &angleFmt = intendedFormat(); diff --git a/src/libANGLE/renderer/vulkan/vk_format_utils.h b/src/libANGLE/renderer/vulkan/vk_format_utils.h index 2b3cad487..2b5914cd1 100644 --- a/src/libANGLE/renderer/vulkan/vk_format_utils.h +++ b/src/libANGLE/renderer/vulkan/vk_format_utils.h @@ -84,6 +84,7 @@ struct Format final : private angle::NonCopyable // Returns buffer alignment for image-copy operations (to or from a buffer). size_t getImageCopyBufferAlignment() const; + size_t getValidImageCopyBufferAlignment() const; // Returns true if the Image format has more channels than the ANGLE format. bool hasEmulatedImageChannels() const; diff --git a/src/libANGLE/renderer/vulkan/vk_helpers.cpp b/src/libANGLE/renderer/vulkan/vk_helpers.cpp index 154639975..606f2632f 100644 --- a/src/libANGLE/renderer/vulkan/vk_helpers.cpp +++ b/src/libANGLE/renderer/vulkan/vk_helpers.cpp @@ -2469,6 +2469,8 @@ ImageHelper::ImageHelper(ImageHelper &&other) mCurrentQueueFamilyIndex(other.mCurrentQueueFamilyIndex), mLastNonShaderReadOnlyLayout(other.mLastNonShaderReadOnlyLayout), mCurrentShaderReadStageMask(other.mCurrentShaderReadStageMask), + mYuvConversionSampler(std::move(other.mYuvConversionSampler)), + mExternalFormat(other.mExternalFormat), mBaseLevel(other.mBaseLevel), mMaxLevel(other.mMaxLevel), mLayerCount(other.mLayerCount), @@ -2503,6 +2505,7 @@ void ImageHelper::resetCachedProperties() mLayerCount = 0; mLevelCount = 0; mCurrentSingleClearValue.reset(); + mExternalFormat = 0; } void ImageHelper::initStagingBuffer(RendererVk *renderer, @@ -2582,6 +2585,9 @@ angle::Result ImageHelper::initExternal(Context *context, mCurrentLayout = initialLayout; + mYuvConversionSampler.reset(); + mExternalFormat = 0; + ANGLE_VK_TRY(context, mImage.init(context->getDevice(), imageInfo)); stageClearIfEmulatedFormat(context); @@ -2736,18 +2742,34 @@ angle::Result ImageHelper::initMemory(Context *context, return angle::Result::Continue; } -angle::Result ImageHelper::initExternalMemory(Context *context, - const MemoryProperties &memoryProperties, - const VkMemoryRequirements &memoryRequirements, - const void *extraAllocationInfo, - uint32_t currentQueueFamilyIndex, +angle::Result ImageHelper::initExternalMemory( + Context *context, + const MemoryProperties &memoryProperties, + const VkMemoryRequirements &memoryRequirements, + const VkSamplerYcbcrConversionCreateInfo *samplerYcbcrConversionCreateInfo, + const void *extraAllocationInfo, + uint32_t currentQueueFamilyIndex, - VkMemoryPropertyFlags flags) + VkMemoryPropertyFlags flags) { // TODO(jmadill): Memory sub-allocation. http://anglebug.com/2162 ANGLE_TRY(AllocateImageMemoryWithRequirements(context, flags, memoryRequirements, extraAllocationInfo, &mImage, &mDeviceMemory)); mCurrentQueueFamilyIndex = currentQueueFamilyIndex; + +#ifdef VK_USE_PLATFORM_ANDROID_KHR + if (samplerYcbcrConversionCreateInfo) + { + const VkExternalFormatANDROID *vkExternalFormat = + reinterpret_cast( + samplerYcbcrConversionCreateInfo->pNext); + ASSERT(vkExternalFormat->sType == VK_STRUCTURE_TYPE_EXTERNAL_FORMAT_ANDROID); + mExternalFormat = vkExternalFormat->externalFormat; + + ANGLE_TRY(context->getRenderer()->getYuvConversionCache().getYuvConversion( + context, mExternalFormat, *samplerYcbcrConversionCreateInfo, &mYuvConversionSampler)); + } +#endif return angle::Result::Continue; } @@ -2797,9 +2819,8 @@ angle::Result ImageHelper::initLayerImageViewImpl( viewInfo.image = mImage.getHandle(); viewInfo.viewType = gl_vk::GetImageViewType(textureType); viewInfo.format = imageFormat; - ASSERT(viewInfo.format != VK_FORMAT_UNDEFINED); - if (swizzleMap.swizzleRequired()) + if (swizzleMap.swizzleRequired() && !mYuvConversionSampler.valid()) { viewInfo.components.r = gl_vk::GetSwizzle(swizzleMap.swizzleRed); viewInfo.components.g = gl_vk::GetSwizzle(swizzleMap.swizzleGreen); @@ -2821,6 +2842,15 @@ angle::Result ImageHelper::initLayerImageViewImpl( viewInfo.pNext = imageViewUsageCreateInfo; + VkSamplerYcbcrConversionInfo yuvConversionInfo = {}; + if (mYuvConversionSampler.valid()) + { + ASSERT((context->getRenderer()->getFeatures().supportsYUVSamplerConversion.enabled)); + yuvConversionInfo.sType = VK_STRUCTURE_TYPE_SAMPLER_YCBCR_CONVERSION_INFO; + yuvConversionInfo.pNext = nullptr; + yuvConversionInfo.conversion = mYuvConversionSampler.get().getHandle(); + vk::AddToPNextChain(&viewInfo, &yuvConversionInfo); + } ANGLE_VK_TRY(context, imageViewOut->init(context->getDevice(), viewInfo)); return angle::Result::Continue; } diff --git a/src/libANGLE/renderer/vulkan/vk_helpers.h b/src/libANGLE/renderer/vulkan/vk_helpers.h index 478ca09f5..9e5d13fbb 100644 --- a/src/libANGLE/renderer/vulkan/vk_helpers.h +++ b/src/libANGLE/renderer/vulkan/vk_helpers.h @@ -1060,12 +1060,14 @@ class ImageHelper final : public Resource, public angle::Subject angle::Result initMemory(Context *context, const MemoryProperties &memoryProperties, VkMemoryPropertyFlags flags); - angle::Result initExternalMemory(Context *context, - const MemoryProperties &memoryProperties, - const VkMemoryRequirements &memoryRequirements, - const void *extraAllocationInfo, - uint32_t currentQueueFamilyIndex, - VkMemoryPropertyFlags flags); + angle::Result initExternalMemory( + Context *context, + const MemoryProperties &memoryProperties, + const VkMemoryRequirements &memoryRequirements, + const VkSamplerYcbcrConversionCreateInfo *samplerYcbcrConversionCreateInfo, + const void *extraAllocationInfo, + uint32_t currentQueueFamilyIndex, + VkMemoryPropertyFlags flags); angle::Result initLayerImageView(Context *context, gl::TextureType textureType, VkImageAspectFlags aspectMask, @@ -1387,6 +1389,8 @@ class ImageHelper final : public Resource, public angle::Subject GLuint *inputSkipBytes); void onWrite() { mCurrentSingleClearValue.reset(); } + bool hasImmutableSampler() { return mExternalFormat != 0; } + uint64_t getExternalFormat() const { return mExternalFormat; } private: enum class UpdateSource @@ -1523,6 +1527,10 @@ class ImageHelper final : public Resource, public angle::Subject ImageLayout mLastNonShaderReadOnlyLayout; VkPipelineStageFlags mCurrentShaderReadStageMask; + // For imported images + vk::BindingPointer mYuvConversionSampler; + uint64_t mExternalFormat; + // Cached properties. uint32_t mBaseLevel; uint32_t mMaxLevel; diff --git a/src/libANGLE/renderer/vulkan/vk_utils.cpp b/src/libANGLE/renderer/vulkan/vk_utils.cpp index 72e17590c..ea300b575 100644 --- a/src/libANGLE/renderer/vulkan/vk_utils.cpp +++ b/src/libANGLE/renderer/vulkan/vk_utils.cpp @@ -775,6 +775,10 @@ PFN_vkImportFenceFdKHR vkImportFenceFdKHR = nullptr; PFN_vkGetPhysicalDeviceExternalSemaphorePropertiesKHR vkGetPhysicalDeviceExternalSemaphorePropertiesKHR = nullptr; +// VK_KHR_sampler_ycbcr_conversion +PFN_vkCreateSamplerYcbcrConversionKHR vkCreateSamplerYcbcrConversionKHR = nullptr; +PFN_vkDestroySamplerYcbcrConversionKHR vkDestroySamplerYcbcrConversionKHR = nullptr; + # if defined(ANGLE_PLATFORM_FUCHSIA) // VK_FUCHSIA_imagepipe_surface PFN_vkCreateImagePipeSurfaceFUCHSIA vkCreateImagePipeSurfaceFUCHSIA = nullptr; @@ -835,6 +839,13 @@ void InitTransformFeedbackEXTFunctions(VkDevice device) GET_DEVICE_FUNC(vkCmdDrawIndirectByteCountEXT); } +// VK_KHR_sampler_ycbcr_conversion +void InitSamplerYcbcrKHRFunctions(VkDevice device) +{ + GET_DEVICE_FUNC(vkCreateSamplerYcbcrConversionKHR); + GET_DEVICE_FUNC(vkDestroySamplerYcbcrConversionKHR); +} + # if defined(ANGLE_PLATFORM_FUCHSIA) void InitImagePipeSurfaceFUCHSIAFunctions(VkInstance instance) { diff --git a/src/libANGLE/renderer/vulkan/vk_utils.h b/src/libANGLE/renderer/vulkan/vk_utils.h index e62a8432c..e9681d50f 100644 --- a/src/libANGLE/renderer/vulkan/vk_utils.h +++ b/src/libANGLE/renderer/vulkan/vk_utils.h @@ -482,6 +482,12 @@ class BindingPointer final : angle::NonCopyable ~BindingPointer() { reset(); } + BindingPointer(BindingPointer &&other) + { + set(other.mRefCounted); + other.reset(); + } + void set(RefCounted *refCounted) { if (mRefCounted) @@ -685,6 +691,7 @@ void InitDebugUtilsEXTFunctions(VkInstance instance); void InitDebugReportEXTFunctions(VkInstance instance); void InitGetPhysicalDeviceProperties2KHRFunctions(VkInstance instance); void InitTransformFeedbackEXTFunctions(VkDevice device); +void InitSamplerYcbcrKHRFunctions(VkDevice device); # if defined(ANGLE_PLATFORM_FUCHSIA) // VK_FUCHSIA_imagepipe_surface diff --git a/src/libANGLE/renderer/vulkan/vk_wrapper.h b/src/libANGLE/renderer/vulkan/vk_wrapper.h index ec2c5c34e..b256267d7 100644 --- a/src/libANGLE/renderer/vulkan/vk_wrapper.h +++ b/src/libANGLE/renderer/vulkan/vk_wrapper.h @@ -48,6 +48,7 @@ namespace vk FUNC(QueryPool) \ FUNC(RenderPass) \ FUNC(Sampler) \ + FUNC(SamplerYcbcrConversion) \ FUNC(Semaphore) \ FUNC(ShaderModule) @@ -606,6 +607,15 @@ class Sampler final : public WrappedObject VkResult init(VkDevice device, const VkSamplerCreateInfo &createInfo); }; +class SamplerYcbcrConversion final + : public WrappedObject +{ + public: + SamplerYcbcrConversion() = default; + void destroy(VkDevice device); + VkResult init(VkDevice device, const VkSamplerYcbcrConversionCreateInfo &createInfo); +}; + class Event final : public WrappedObject { public: @@ -1698,6 +1708,23 @@ ANGLE_INLINE VkResult Sampler::init(VkDevice device, const VkSamplerCreateInfo & return vkCreateSampler(device, &createInfo, nullptr, &mHandle); } +// SamplerYuvConversion implementation. +ANGLE_INLINE void SamplerYcbcrConversion::destroy(VkDevice device) +{ + if (valid()) + { + vkDestroySamplerYcbcrConversionKHR(device, mHandle, nullptr); + mHandle = VK_NULL_HANDLE; + } +} + +ANGLE_INLINE VkResult +SamplerYcbcrConversion::init(VkDevice device, const VkSamplerYcbcrConversionCreateInfo &createInfo) +{ + ASSERT(!valid()); + return vkCreateSamplerYcbcrConversionKHR(device, &createInfo, nullptr, &mHandle); +} + // Event implementation. ANGLE_INLINE void Event::destroy(VkDevice device) {