Commit f61272fb by Courtney Goeltzenleuchter Committed by Commit Bot

Add support for VK_KHR_sampler_ycbcr_conversion

This adds ability for applications to import Android Hardware Buffers (AHBs) as OpenGL images which in turn can be sampled from and/or written. This was specifically tested with the common use case of importing a buffer created by an media decoder and using that as a texture source to include that video content on the screen. Tested with: - Angry Birds 2 video player (for ads) requires YUV conversion. - Basic Media Decoder example: https://github.com/android/media-samples/tree/master/BasicMediaDecoder Bug: b/155487768 Change-Id: I9255450f81aa4daa2aace7205d4f6c3f225abcca Reviewed-on: https://chromium-review.googlesource.com/c/angle/angle/+/2175103 Commit-Queue: Courtney Goeltzenleuchter <courtneygo@google.com> Reviewed-by: 's avatarCourtney Goeltzenleuchter <courtneygo@google.com> Reviewed-by: 's avatarTim Van Patten <timvp@google.com> Reviewed-by: 's avatarJamie Madill <jmadill@chromium.org>
parent b549be96
...@@ -122,11 +122,11 @@ struct FeaturesVk : FeatureSetBase ...@@ -122,11 +122,11 @@ struct FeaturesVk : FeatureSetBase
// Whether the VkDevice supports the VK_FUCHSIA_external_memory // Whether the VkDevice supports the VK_FUCHSIA_external_memory
// extension, on which the GL_ANGLE_memory_object_fuchsia extension can be layered. // extension, on which the GL_ANGLE_memory_object_fuchsia extension can be layered.
angle::Feature supportsExternalMemoryFuchsia = { Feature supportsExternalMemoryFuchsia = {
"supports_external_memory_fuchsia", FeatureCategory::VulkanFeatures, "supports_external_memory_fuchsia", FeatureCategory::VulkanFeatures,
"VkDevice supports the VK_FUCHSIA_external_memory extension", &members}; "VkDevice supports the VK_FUCHSIA_external_memory extension", &members};
angle::Feature supportsFilteringPrecision = { Feature supportsFilteringPrecision = {
"supports_filtering_precision_google", FeatureCategory::VulkanFeatures, "supports_filtering_precision_google", FeatureCategory::VulkanFeatures,
"VkDevice supports the VK_GOOGLE_sampler_filtering_precision extension", &members}; "VkDevice supports the VK_GOOGLE_sampler_filtering_precision extension", &members};
...@@ -172,6 +172,12 @@ struct FeaturesVk : FeatureSetBase ...@@ -172,6 +172,12 @@ struct FeaturesVk : FeatureSetBase
"supports_shader_stencil_export", FeatureCategory::VulkanFeatures, "supports_shader_stencil_export", FeatureCategory::VulkanFeatures,
"VkDevice supports the VK_EXT_shader_stencil_export extension", &members}; "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. // Where VK_EXT_transform_feedback is not support, an emulation path is used.
// http://anglebug.com/3205 // http://anglebug.com/3205
Feature emulateTransformFeedback = { Feature emulateTransformFeedback = {
......
...@@ -4,9 +4,9 @@ ...@@ -4,9 +4,9 @@
"src/libANGLE/renderer/angle_format_map.json": "src/libANGLE/renderer/angle_format_map.json":
"c79d833aea7007c7d0d51cdaa9b265a6", "c79d833aea7007c7d0d51cdaa9b265a6",
"src/libANGLE/renderer/vulkan/gen_vk_format_table.py": "src/libANGLE/renderer/vulkan/gen_vk_format_table.py":
"d8a0f2278c09a49049a73930b9da3719", "54a7374f93f17da1386600027acca7a3",
"src/libANGLE/renderer/vulkan/vk_format_map.json": "src/libANGLE/renderer/vulkan/vk_format_map.json":
"738c8dc36fbe212669944e88ae918f9c", "738c8dc36fbe212669944e88ae918f9c",
"src/libANGLE/renderer/vulkan/vk_format_table_autogen.cpp": "src/libANGLE/renderer/vulkan/vk_format_table_autogen.cpp":
"383749c786e957bdbbbce9d8b5c2441a" "c7e01cbf1eded87f4ed9f3bc8b7a567c"
} }
\ No newline at end of file
...@@ -64,6 +64,10 @@ extern PFN_vkImportFenceFdKHR vkImportFenceFdKHR; ...@@ -64,6 +64,10 @@ extern PFN_vkImportFenceFdKHR vkImportFenceFdKHR;
extern PFN_vkGetPhysicalDeviceExternalSemaphorePropertiesKHR extern PFN_vkGetPhysicalDeviceExternalSemaphorePropertiesKHR
vkGetPhysicalDeviceExternalSemaphorePropertiesKHR; vkGetPhysicalDeviceExternalSemaphorePropertiesKHR;
// VK_KHR_sampler_ycbcr_conversion
extern PFN_vkCreateSamplerYcbcrConversionKHR vkCreateSamplerYcbcrConversionKHR;
extern PFN_vkDestroySamplerYcbcrConversionKHR vkDestroySamplerYcbcrConversionKHR;
# if defined(ANGLE_PLATFORM_FUCHSIA) # if defined(ANGLE_PLATFORM_FUCHSIA)
// VK_FUCHSIA_imagepipe_surface // VK_FUCHSIA_imagepipe_surface
extern PFN_vkCreateImagePipeSurfaceFUCHSIA vkCreateImagePipeSurfaceFUCHSIA; extern PFN_vkCreateImagePipeSurfaceFUCHSIA vkCreateImagePipeSurfaceFUCHSIA;
......
...@@ -3748,6 +3748,7 @@ angle::Result ContextVk::updateActiveTextures(const gl::Context *context) ...@@ -3748,6 +3748,7 @@ angle::Result ContextVk::updateActiveTextures(const gl::Context *context)
const gl::ActiveTextureMask &activeTextures = executable->getActiveSamplersMask(); const gl::ActiveTextureMask &activeTextures = executable->getActiveSamplersMask();
const gl::ActiveTextureTypeArray &textureTypes = executable->getActiveSamplerTypes(); const gl::ActiveTextureTypeArray &textureTypes = executable->getActiveSamplerTypes();
bool haveImmutableSampler = false;
for (size_t textureUnit : activeTextures) for (size_t textureUnit : activeTextures)
{ {
gl::Texture *texture = textures[textureUnit]; gl::Texture *texture = textures[textureUnit];
...@@ -3776,6 +3777,11 @@ angle::Result ContextVk::updateActiveTextures(const gl::Context *context) ...@@ -3776,6 +3777,11 @@ angle::Result ContextVk::updateActiveTextures(const gl::Context *context)
samplerSerial = samplerVk->getSerial(); samplerSerial = samplerVk->getSerial();
} }
if (textureVk->getImage().hasImmutableSampler())
{
haveImmutableSampler = true;
}
mActiveTextures[textureUnit].texture = textureVk; mActiveTextures[textureUnit].texture = textureVk;
mActiveTextures[textureUnit].sampler = samplerVk; mActiveTextures[textureUnit].sampler = samplerVk;
// Cache serials from sampler and texture, but re-use texture if no sampler bound // 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) ...@@ -3783,6 +3789,12 @@ angle::Result ContextVk::updateActiveTextures(const gl::Context *context)
mActiveTexturesDesc.update(textureUnit, textureVk->getSerial(), samplerSerial); mActiveTexturesDesc.update(textureUnit, textureVk->getSerial(), samplerSerial);
} }
if (haveImmutableSampler)
{
ANGLE_TRY(mExecutable->updatePipelineLayout(context, &mActiveTextures));
invalidateCurrentGraphicsPipeline();
}
return angle::Result::Continue; return angle::Result::Continue;
} }
......
...@@ -97,9 +97,13 @@ egl::Error ImageVk::initialize(const egl::Display *display) ...@@ -97,9 +97,13 @@ egl::Error ImageVk::initialize(const egl::Display *display)
return egl::EglBadAccess(); 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 // Make sure a staging buffer is ready to use to upload data
mImage->initStagingBuffer(renderer, mImage->getFormat().getImageCopyBufferAlignment(), mImage->initStagingBuffer(renderer, alignment, vk::kStagingBufferFlags,
vk::kStagingBufferFlags, vk::kStagingBufferSize); vk::kStagingBufferSize);
mOwnsImage = false; mOwnsImage = false;
......
...@@ -245,7 +245,7 @@ angle::Result MemoryObjectVk::createImage(ContextVk *contextVk, ...@@ -245,7 +245,7 @@ angle::Result MemoryObjectVk::createImage(ContextVk *contextVk,
VkMemoryPropertyFlags flags = 0; VkMemoryPropertyFlags flags = 0;
ANGLE_TRY(image->initExternalMemory(contextVk, renderer->getMemoryProperties(), ANGLE_TRY(image->initExternalMemory(contextVk, renderer->getMemoryProperties(),
externalMemoryRequirements, importMemoryInfo, externalMemoryRequirements, nullptr, importMemoryInfo,
renderer->getQueueFamilyIndex(), flags)); renderer->getQueueFamilyIndex(), flags));
return angle::Result::Continue; return angle::Result::Continue;
......
...@@ -446,9 +446,11 @@ void ProgramExecutableVk::addImageDescriptorSetDesc(const gl::ProgramExecutable ...@@ -446,9 +446,11 @@ void ProgramExecutableVk::addImageDescriptorSetDesc(const gl::ProgramExecutable
} }
} }
void ProgramExecutableVk::addTextureDescriptorSetDesc(const gl::ProgramState &programState, void ProgramExecutableVk::addTextureDescriptorSetDesc(
bool useOldRewriteStructSamplers, const gl::ProgramState &programState,
vk::DescriptorSetLayoutDesc *descOut) bool useOldRewriteStructSamplers,
const gl::ActiveTextureArray<vk::TextureUnit> *activeTextures,
vk::DescriptorSetLayoutDesc *descOut)
{ {
const std::vector<gl::SamplerBinding> &samplerBindings = programState.getSamplerBindings(); const std::vector<gl::SamplerBinding> &samplerBindings = programState.getSamplerBindings();
const std::vector<gl::LinkedUniform> &uniforms = programState.getUniforms(); const std::vector<gl::LinkedUniform> &uniforms = programState.getUniforms();
...@@ -493,8 +495,25 @@ void ProgramExecutableVk::addTextureDescriptorSetDesc(const gl::ProgramState &pr ...@@ -493,8 +495,25 @@ void ProgramExecutableVk::addTextureDescriptorSetDesc(const gl::ProgramState &pr
ShaderInterfaceVariableInfo &info = mVariableInfoMap[shaderType][samplerName]; ShaderInterfaceVariableInfo &info = mVariableInfoMap[shaderType][samplerName];
VkShaderStageFlags activeStages = gl_vk::kShaderStageMap[shaderType]; VkShaderStageFlags activeStages = gl_vk::kShaderStageMap[shaderType];
descOut->update(info.binding, VK_DESCRIPTOR_TYPE_COMBINED_IMAGE_SAMPLER, arraySize, // TODO: https://issuetracker.google.com/issues/158215272: how do we handle array of
activeStages, nullptr); // 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, ...@@ -610,7 +629,11 @@ angle::Result ProgramExecutableVk::getComputePipeline(ContextVk *contextVk,
return shaderProgram->getComputePipeline(contextVk, getPipelineLayout(), pipelineOut); 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<vk::TextureUnit> *activeTextures)
{ {
const gl::State &glState = glContext->getState(); const gl::State &glState = glContext->getState();
ContextVk *contextVk = vk::GetImpl(glContext); ContextVk *contextVk = vk::GetImpl(glContext);
...@@ -621,13 +644,12 @@ angle::Result ProgramExecutableVk::createPipelineLayout(const gl::Context *glCon ...@@ -621,13 +644,12 @@ angle::Result ProgramExecutableVk::createPipelineLayout(const gl::Context *glCon
gl::ShaderMap<const gl::ProgramState *> programStates; gl::ShaderMap<const gl::ProgramState *> programStates;
fillProgramStateMap(contextVk, &programStates); fillProgramStateMap(contextVk, &programStates);
reset(contextVk);
// Store a reference to the pipeline and descriptor set layouts. This will create them if they // Store a reference to the pipeline and descriptor set layouts. This will create them if they
// don't already exist in the cache. // don't already exist in the cache.
// Default uniforms and transform feedback: // Default uniforms and transform feedback:
vk::DescriptorSetLayoutDesc uniformsAndXfbSetDesc; vk::DescriptorSetLayoutDesc uniformsAndXfbSetDesc;
mNumDefaultUniformDescriptors = 0;
for (const gl::ShaderType shaderType : linkedShaderStages) for (const gl::ShaderType shaderType : linkedShaderStages)
{ {
const std::string uniformBlockName = kDefaultUniformNames[shaderType]; const std::string uniformBlockName = kDefaultUniformNames[shaderType];
...@@ -693,7 +715,7 @@ angle::Result ProgramExecutableVk::createPipelineLayout(const gl::Context *glCon ...@@ -693,7 +715,7 @@ angle::Result ProgramExecutableVk::createPipelineLayout(const gl::Context *glCon
const gl::ProgramState *programState = programStates[shaderType]; const gl::ProgramState *programState = programStates[shaderType];
ASSERT(programState); ASSERT(programState);
addTextureDescriptorSetDesc(*programState, contextVk->useOldRewriteStructSamplers(), addTextureDescriptorSetDesc(*programState, contextVk->useOldRewriteStructSamplers(),
&texturesSetDesc); activeTextures, &texturesSetDesc);
} }
ANGLE_TRY(renderer->getDescriptorSetLayout(contextVk, texturesSetDesc, ANGLE_TRY(renderer->getDescriptorSetLayout(contextVk, texturesSetDesc,
...@@ -721,6 +743,22 @@ angle::Result ProgramExecutableVk::createPipelineLayout(const gl::Context *glCon ...@@ -721,6 +743,22 @@ angle::Result ProgramExecutableVk::createPipelineLayout(const gl::Context *glCon
ANGLE_TRY(renderer->getPipelineLayout(contextVk, pipelineLayoutDesc, mDescriptorSetLayouts, ANGLE_TRY(renderer->getPipelineLayout(contextVk, pipelineLayoutDesc, mDescriptorSetLayouts,
&mPipelineLayout)); &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<const gl::ProgramState *> programStates;
fillProgramStateMap(contextVk, &programStates);
reset(contextVk);
ANGLE_TRY(updatePipelineLayout(glContext, nullptr));
// Initialize descriptor pools. // Initialize descriptor pools.
std::array<VkDescriptorPoolSize, 2> uniformAndXfbSetSize = { std::array<VkDescriptorPoolSize, 2> uniformAndXfbSetSize = {
{{VK_DESCRIPTOR_TYPE_UNIFORM_BUFFER_DYNAMIC, {{VK_DESCRIPTOR_TYPE_UNIFORM_BUFFER_DYNAMIC,
...@@ -1302,6 +1340,10 @@ angle::Result ProgramExecutableVk::updateTexturesDescriptorSet(ContextVk *contex ...@@ -1302,6 +1340,10 @@ angle::Result ProgramExecutableVk::updateTexturesDescriptorSet(ContextVk *contex
textureVk->getReadImageViewAndRecordUse(contextVk).getHandle(); textureVk->getReadImageViewAndRecordUse(contextVk).getHandle();
} }
if (textureVk->getImage().hasImmutableSampler())
{
imageInfos[arrayElement].sampler = textureVk->getSampler().getHandle();
}
ShaderInterfaceVariableInfoMap &variableInfoMap = mVariableInfoMap[shaderType]; ShaderInterfaceVariableInfoMap &variableInfoMap = mVariableInfoMap[shaderType];
const std::string samplerName = const std::string samplerName =
contextVk->getRenderer()->getFeatures().forceOldRewriteStructSamplers.enabled contextVk->getRenderer()->getFeatures().forceOldRewriteStructSamplers.enabled
......
...@@ -137,6 +137,8 @@ class ProgramExecutableVk ...@@ -137,6 +137,8 @@ class ProgramExecutableVk
const vk::PipelineLayout &getPipelineLayout() const { return mPipelineLayout.get(); } const vk::PipelineLayout &getPipelineLayout() const { return mPipelineLayout.get(); }
angle::Result createPipelineLayout(const gl::Context *glContext); angle::Result createPipelineLayout(const gl::Context *glContext);
angle::Result updatePipelineLayout(const gl::Context *glContext,
gl::ActiveTextureArray<vk::TextureUnit> *activeTextures);
angle::Result updateTexturesDescriptorSet(ContextVk *contextVk); angle::Result updateTexturesDescriptorSet(ContextVk *contextVk);
angle::Result updateShaderResourcesDescriptorSet(ContextVk *contextVk, angle::Result updateShaderResourcesDescriptorSet(ContextVk *contextVk,
...@@ -182,6 +184,7 @@ class ProgramExecutableVk ...@@ -182,6 +184,7 @@ class ProgramExecutableVk
vk::DescriptorSetLayoutDesc *descOut); vk::DescriptorSetLayoutDesc *descOut);
void addTextureDescriptorSetDesc(const gl::ProgramState &programState, void addTextureDescriptorSetDesc(const gl::ProgramState &programState,
bool useOldRewriteStructSamplers, bool useOldRewriteStructSamplers,
const gl::ActiveTextureArray<vk::TextureUnit> *activeTextures,
vk::DescriptorSetLayoutDesc *descOut); vk::DescriptorSetLayoutDesc *descOut);
void updateDefaultUniformsDescriptorSet( void updateDefaultUniformsDescriptorSet(
......
...@@ -492,6 +492,7 @@ void RendererVk::onDestroy() ...@@ -492,6 +492,7 @@ void RendererVk::onDestroy()
mPipelineCache.destroy(mDevice); mPipelineCache.destroy(mDevice);
mSamplerCache.destroy(this); mSamplerCache.destroy(this);
mYuvConversionCache.destroy(this);
mTheNullBuffer.destroy(this); mTheNullBuffer.destroy(this);
mAllocator.destroy(); mAllocator.destroy();
...@@ -886,6 +887,10 @@ void RendererVk::queryDeviceExtensionFeatures(const ExtensionNameList &deviceExt ...@@ -886,6 +887,10 @@ void RendererVk::queryDeviceExtensionFeatures(const ExtensionNameList &deviceExt
mExternalSemaphoreProperties = {}; mExternalSemaphoreProperties = {};
mExternalSemaphoreProperties.sType = VK_STRUCTURE_TYPE_EXTERNAL_SEMAPHORE_PROPERTIES; mExternalSemaphoreProperties.sType = VK_STRUCTURE_TYPE_EXTERNAL_SEMAPHORE_PROPERTIES;
mSamplerYcbcrConversionFeatures = {};
mSamplerYcbcrConversionFeatures.sType =
VK_STRUCTURE_TYPE_PHYSICAL_DEVICE_SAMPLER_YCBCR_CONVERSION_FEATURES;
if (!vkGetPhysicalDeviceProperties2KHR || !vkGetPhysicalDeviceFeatures2KHR) if (!vkGetPhysicalDeviceProperties2KHR || !vkGetPhysicalDeviceFeatures2KHR)
{ {
return; return;
...@@ -935,6 +940,12 @@ void RendererVk::queryDeviceExtensionFeatures(const ExtensionNameList &deviceExt ...@@ -935,6 +940,12 @@ void RendererVk::queryDeviceExtensionFeatures(const ExtensionNameList &deviceExt
vk::AddToPNextChain(&deviceProperties, &mExternalMemoryHostProperties); 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 // Query subgroup properties
vk::AddToPNextChain(&deviceProperties, &mSubgroupProperties); vk::AddToPNextChain(&deviceProperties, &mSubgroupProperties);
...@@ -972,6 +983,13 @@ void RendererVk::queryDeviceExtensionFeatures(const ExtensionNameList &deviceExt ...@@ -972,6 +983,13 @@ void RendererVk::queryDeviceExtensionFeatures(const ExtensionNameList &deviceExt
mIndexTypeUint8Features.pNext = nullptr; mIndexTypeUint8Features.pNext = nullptr;
mSubgroupProperties.pNext = nullptr; mSubgroupProperties.pNext = nullptr;
mExternalMemoryHostProperties.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) angle::Result RendererVk::initializeDevice(DisplayVk *displayVk, uint32_t queueFamilyIndex)
...@@ -1274,6 +1292,12 @@ angle::Result RendererVk::initializeDevice(DisplayVk *displayVk, uint32_t queueF ...@@ -1274,6 +1292,12 @@ angle::Result RendererVk::initializeDevice(DisplayVk *displayVk, uint32_t queueF
#endif // !defined(ANGLE_SHARED_LIBVULKAN) #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.sType = VK_STRUCTURE_TYPE_DEVICE_CREATE_INFO;
createInfo.flags = 0; createInfo.flags = 0;
createInfo.queueCreateInfoCount = 1; createInfo.queueCreateInfoCount = 1;
...@@ -1325,6 +1349,10 @@ angle::Result RendererVk::initializeDevice(DisplayVk *displayVk, uint32_t queueF ...@@ -1325,6 +1349,10 @@ angle::Result RendererVk::initializeDevice(DisplayVk *displayVk, uint32_t queueF
{ {
InitTransformFeedbackEXTFunctions(mDevice); InitTransformFeedbackEXTFunctions(mDevice);
} }
if (getFeatures().supportsYUVSamplerConversion.enabled)
{
InitSamplerYcbcrKHRFunctions(mDevice);
}
#endif // !defined(ANGLE_SHARED_LIBVULKAN) #endif // !defined(ANGLE_SHARED_LIBVULKAN)
// Initialize the vulkan pipeline cache. // Initialize the vulkan pipeline cache.
...@@ -1730,6 +1758,9 @@ void RendererVk::initFeatures(DisplayVk *displayVk, const ExtensionNameList &dev ...@@ -1730,6 +1758,9 @@ void RendererVk::initFeatures(DisplayVk *displayVk, const ExtensionNameList &dev
// Currently disabled by default: http://anglebug.com/4324 // Currently disabled by default: http://anglebug.com/4324
ANGLE_FEATURE_CONDITION(&mFeatures, enableCommandProcessingThread, false); ANGLE_FEATURE_CONDITION(&mFeatures, enableCommandProcessingThread, false);
ANGLE_FEATURE_CONDITION(&mFeatures, supportsYUVSamplerConversion,
mSamplerYcbcrConversionFeatures.samplerYcbcrConversion != VK_FALSE);
angle::PlatformMethods *platform = ANGLEPlatformCurrent(); angle::PlatformMethods *platform = ANGLEPlatformCurrent();
platform->overrideFeaturesVk(platform, &mFeatures); platform->overrideFeaturesVk(platform, &mFeatures);
......
...@@ -247,6 +247,7 @@ class RendererVk : angle::NonCopyable ...@@ -247,6 +247,7 @@ class RendererVk : angle::NonCopyable
bool enableDebugUtils() const { return mEnableDebugUtils; } bool enableDebugUtils() const { return mEnableDebugUtils; }
SamplerCache &getSamplerCache() { return mSamplerCache; } SamplerCache &getSamplerCache() { return mSamplerCache; }
SamplerYcbcrConversionCache &getYuvConversionCache() { return mYuvConversionCache; }
vk::ActiveHandleCounter &getActiveHandleCounts() { return mActiveHandleCounts; } vk::ActiveHandleCounter &getActiveHandleCounts() { return mActiveHandleCounts; }
// Queue commands to worker thread for processing // Queue commands to worker thread for processing
...@@ -309,6 +310,7 @@ class RendererVk : angle::NonCopyable ...@@ -309,6 +310,7 @@ class RendererVk : angle::NonCopyable
VkPhysicalDeviceExternalMemoryHostPropertiesEXT mExternalMemoryHostProperties; VkPhysicalDeviceExternalMemoryHostPropertiesEXT mExternalMemoryHostProperties;
VkExternalFenceProperties mExternalFenceProperties; VkExternalFenceProperties mExternalFenceProperties;
VkExternalSemaphoreProperties mExternalSemaphoreProperties; VkExternalSemaphoreProperties mExternalSemaphoreProperties;
VkPhysicalDeviceSamplerYcbcrConversionFeatures mSamplerYcbcrConversionFeatures;
std::vector<VkQueueFamilyProperties> mQueueFamilyProperties; std::vector<VkQueueFamilyProperties> mQueueFamilyProperties;
std::mutex mQueueMutex; std::mutex mQueueMutex;
angle::PackedEnumMap<egl::ContextPriority, VkQueue> mQueues; angle::PackedEnumMap<egl::ContextPriority, VkQueue> mQueues;
...@@ -382,6 +384,7 @@ class RendererVk : angle::NonCopyable ...@@ -382,6 +384,7 @@ class RendererVk : angle::NonCopyable
vk::Allocator mAllocator; vk::Allocator mAllocator;
SamplerCache mSamplerCache; SamplerCache mSamplerCache;
SamplerYcbcrConversionCache mYuvConversionCache;
vk::ActiveHandleCounter mActiveHandleCounts; vk::ActiveHandleCounter mActiveHandleCounts;
// Vulkan does not allow binding a null vertex buffer. We use a dummy as a placeholder. // Vulkan does not allow binding a null vertex buffer. We use a dummy as a placeholder.
......
...@@ -39,7 +39,7 @@ angle::Result SamplerVk::syncState(const gl::Context *context, const bool dirty) ...@@ -39,7 +39,7 @@ angle::Result SamplerVk::syncState(const gl::Context *context, const bool dirty)
mSampler.reset(); mSampler.reset();
} }
vk::SamplerDesc desc(mState, false); vk::SamplerDesc desc(mState, false, 0);
ANGLE_TRY(renderer->getSamplerCache().getSampler(contextVk, desc, &mSampler)); ANGLE_TRY(renderer->getSamplerCache().getSampler(contextVk, desc, &mSampler));
// Regenerate the serial on a sampler change. // Regenerate the serial on a sampler change.
......
...@@ -210,9 +210,9 @@ angle::Result OffscreenSurfaceVk::AttachmentImage::initializeWithExternalMemory( ...@@ -210,9 +210,9 @@ angle::Result OffscreenSurfaceVk::AttachmentImage::initializeWithExternalMemory(
image.getImage().getMemoryRequirements(renderer->getDevice(), &externalMemoryRequirements); image.getImage().getMemoryRequirements(renderer->getDevice(), &externalMemoryRequirements);
VkMemoryPropertyFlags flags = 0; VkMemoryPropertyFlags flags = 0;
ANGLE_TRY(image.initExternalMemory(displayVk, renderer->getMemoryProperties(), ANGLE_TRY(image.initExternalMemory(
externalMemoryRequirements, &importMemoryHostPointerInfo, displayVk, renderer->getMemoryProperties(), externalMemoryRequirements, nullptr,
VK_QUEUE_FAMILY_EXTERNAL, flags)); &importMemoryHostPointerInfo, VK_QUEUE_FAMILY_EXTERNAL, flags));
return angle::Result::Continue; return angle::Result::Continue;
} }
......
...@@ -1884,7 +1884,8 @@ angle::Result TextureVk::syncState(const gl::Context *context, ...@@ -1884,7 +1884,8 @@ angle::Result TextureVk::syncState(const gl::Context *context,
mImage->getLevelCount(), layerCount)); 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)); ANGLE_TRY(renderer->getSamplerCache().getSampler(contextVk, samplerDesc, &mSampler));
// Regenerate the serial on a sampler change. // Regenerate the serial on a sampler change.
......
...@@ -413,7 +413,7 @@ class TextureVk : public TextureImpl, public angle::ObserverInterface ...@@ -413,7 +413,7 @@ class TextureVk : public TextureImpl, public angle::ObserverInterface
// reallocated independently of |mImage| on state changes. // reallocated independently of |mImage| on state changes.
vk::ImageViewHelper mImageViews; 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. // sampling states for the Texture.
vk::BindingPointer<vk::Sampler> mSampler; vk::BindingPointer<vk::Sampler> mSampler;
......
...@@ -70,7 +70,18 @@ egl::Error HardwareBufferImageSiblingVkAndroid::ValidateHardwareBuffer(RendererV ...@@ -70,7 +70,18 @@ egl::Error HardwareBufferImageSiblingVkAndroid::ValidateHardwareBuffer(RendererV
return egl::EglBadParameter() << "Failed to query AHardwareBuffer properties"; 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() return egl::EglBadParameter()
<< "AHardwareBuffer format does not support enough features to use as a texture."; << "AHardwareBuffer format does not support enough features to use as a texture.";
...@@ -151,18 +162,35 @@ angle::Result HardwareBufferImageSiblingVkAndroid::initImpl(DisplayVk *displayVk ...@@ -151,18 +162,35 @@ angle::Result HardwareBufferImageSiblingVkAndroid::initImpl(DisplayVk *displayVk
ANGLE_VK_TRY(displayVk, vkGetAndroidHardwareBufferPropertiesANDROID(device, hardwareBuffer, ANGLE_VK_TRY(displayVk, vkGetAndroidHardwareBufferPropertiesANDROID(device, hardwareBuffer,
&bufferProperties)); &bufferProperties));
const vk::Format &vkFormat = renderer->getFormat(internalFormat);
const angle::Format &imageFormat = vkFormat.actualImageFormat();
VkExternalFormatANDROID externalFormat = {}; VkExternalFormatANDROID externalFormat = {};
externalFormat.sType = VK_STRUCTURE_TYPE_EXTERNAL_FORMAT_ANDROID; externalFormat.sType = VK_STRUCTURE_TYPE_EXTERNAL_FORMAT_ANDROID;
externalFormat.externalFormat = 0; 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) if (bufferFormatProperties.format == VK_FORMAT_UNDEFINED)
{ {
externalFormat.externalFormat = bufferFormatProperties.externalFormat; 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 = {}; VkExternalMemoryImageCreateInfo externalMemoryImageCreateInfo = {};
externalMemoryImageCreateInfo.sType = VK_STRUCTURE_TYPE_EXTERNAL_MEMORY_IMAGE_CREATE_INFO; externalMemoryImageCreateInfo.sType = VK_STRUCTURE_TYPE_EXTERNAL_MEMORY_IMAGE_CREATE_INFO;
externalMemoryImageCreateInfo.pNext = &externalFormat; externalMemoryImageCreateInfo.pNext = &externalFormat;
externalMemoryImageCreateInfo.handleTypes = externalMemoryImageCreateInfo.handleTypes =
...@@ -173,19 +201,12 @@ angle::Result HardwareBufferImageSiblingVkAndroid::initImpl(DisplayVk *displayVk ...@@ -173,19 +201,12 @@ angle::Result HardwareBufferImageSiblingVkAndroid::initImpl(DisplayVk *displayVk
mImage = new vk::ImageHelper(); 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); mImage->setTilingMode(imageTilingMode);
ANGLE_TRY(mImage->initExternal( ANGLE_TRY(mImage->initExternal(
displayVk, gl::TextureType::_2D, vkExtents, vkFormat, 1, usage, vk::kVkImageCreateFlagsNone, displayVk, gl::TextureType::_2D, vkExtents,
vk::ImageLayout::ExternalPreInitialized, &externalMemoryImageCreateInfo, 0, 0, 1, 1)); bufferFormatProperties.format == VK_FORMAT_UNDEFINED ? externalVkFormat : vkFormat, 1,
usage, vk::kVkImageCreateFlagsNone, vk::ImageLayout::ExternalPreInitialized,
&externalMemoryImageCreateInfo, 0, 0, 1, 1));
VkImportAndroidHardwareBufferInfoANDROID importHardwareBufferInfo = {}; VkImportAndroidHardwareBufferInfoANDROID importHardwareBufferInfo = {};
importHardwareBufferInfo.sType = VK_STRUCTURE_TYPE_IMPORT_ANDROID_HARDWARE_BUFFER_INFO_ANDROID; importHardwareBufferInfo.sType = VK_STRUCTURE_TYPE_IMPORT_ANDROID_HARDWARE_BUFFER_INFO_ANDROID;
...@@ -203,9 +224,35 @@ angle::Result HardwareBufferImageSiblingVkAndroid::initImpl(DisplayVk *displayVk ...@@ -203,9 +224,35 @@ angle::Result HardwareBufferImageSiblingVkAndroid::initImpl(DisplayVk *displayVk
externalMemoryRequirements.memoryTypeBits = bufferProperties.memoryTypeBits; externalMemoryRequirements.memoryTypeBits = bufferProperties.memoryTypeBits;
VkMemoryPropertyFlags flags = VK_MEMORY_PROPERTY_DEVICE_LOCAL_BIT; VkMemoryPropertyFlags flags = VK_MEMORY_PROPERTY_DEVICE_LOCAL_BIT;
ANGLE_TRY(mImage->initExternalMemory(displayVk, renderer->getMemoryProperties(), if (bufferFormatProperties.format == VK_FORMAT_UNDEFINED)
externalMemoryRequirements, &dedicatedAllocInfo, {
VK_QUEUE_FAMILY_FOREIGN_EXT, flags)); // 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 kColorRenderableRequiredBits = VK_FORMAT_FEATURE_COLOR_ATTACHMENT_BIT;
constexpr uint32_t kDepthStencilRenderableRequiredBits = VK_FORMAT_FEATURE_COLOR_ATTACHMENT_BIT; constexpr uint32_t kDepthStencilRenderableRequiredBits = VK_FORMAT_FEATURE_COLOR_ATTACHMENT_BIT;
......
...@@ -144,7 +144,7 @@ def gen_format_case(angle, internal_format, vk_json_data): ...@@ -144,7 +144,7 @@ def gen_format_case(angle, internal_format, vk_json_data):
args = dict( args = dict(
format_id=angle, internal_format=internal_format, image_template="", buffer_template="") 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) return empty_format_entry_template.format(**args)
# get_formats returns override format (if any) + fallbacks # get_formats returns override format (if any) + fallbacks
......
...@@ -14,6 +14,7 @@ ...@@ -14,6 +14,7 @@
#include "common/vulkan/vk_google_filtering_precision.h" #include "common/vulkan/vk_google_filtering_precision.h"
#include "libANGLE/BlobCache.h" #include "libANGLE/BlobCache.h"
#include "libANGLE/VertexAttribute.h" #include "libANGLE/VertexAttribute.h"
#include "libANGLE/renderer/vulkan/DisplayVk.h"
#include "libANGLE/renderer/vulkan/FramebufferVk.h" #include "libANGLE/renderer/vulkan/FramebufferVk.h"
#include "libANGLE/renderer/vulkan/ProgramVk.h" #include "libANGLE/renderer/vulkan/ProgramVk.h"
#include "libANGLE/renderer/vulkan/RendererVk.h" #include "libANGLE/renderer/vulkan/RendererVk.h"
...@@ -1741,9 +1742,11 @@ SamplerDesc::SamplerDesc(const SamplerDesc &other) = default; ...@@ -1741,9 +1742,11 @@ SamplerDesc::SamplerDesc(const SamplerDesc &other) = default;
SamplerDesc &SamplerDesc::operator=(const SamplerDesc &rhs) = 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() void SamplerDesc::reset()
...@@ -1752,6 +1755,7 @@ void SamplerDesc::reset() ...@@ -1752,6 +1755,7 @@ void SamplerDesc::reset()
mMaxAnisotropy = 0.0f; mMaxAnisotropy = 0.0f;
mMinLod = 0.0f; mMinLod = 0.0f;
mMaxLod = 0.0f; mMaxLod = 0.0f;
mExternalFormat = 0;
mMagFilter = 0; mMagFilter = 0;
mMinFilter = 0; mMinFilter = 0;
mMipmapMode = 0; mMipmapMode = 0;
...@@ -1760,16 +1764,23 @@ void SamplerDesc::reset() ...@@ -1760,16 +1764,23 @@ void SamplerDesc::reset()
mAddressModeW = 0; mAddressModeW = 0;
mCompareEnabled = 0; mCompareEnabled = 0;
mCompareOp = 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; mMipLodBias = 0.0f;
mMaxAnisotropy = samplerState.getMaxAnisotropy(); mMaxAnisotropy = samplerState.getMaxAnisotropy();
mMinLod = samplerState.getMinLod(); mMinLod = samplerState.getMinLod();
mMaxLod = samplerState.getMaxLod(); 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; bool compareEnable = samplerState.getCompareMode() == GL_COMPARE_REF_TO_TEXTURE;
VkCompareOp compareOp = gl_vk::GetCompareOp(samplerState.getCompareFunc()); VkCompareOp compareOp = gl_vk::GetCompareOp(samplerState.getCompareFunc());
// When sampling from stencil, deqp tests expect texture compare to have no effect // 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) ...@@ -1799,7 +1810,9 @@ void SamplerDesc::update(const gl::SamplerState &samplerState, bool stencilMode)
mMaxLod = 0.25f; mMaxLod = 0.25f;
} }
mReserved = 0; mReserved[0] = 0;
mReserved[1] = 0;
mReserved[2] = 0;
} }
angle::Result SamplerDesc::init(ContextVk *contextVk, vk::Sampler *sampler) const angle::Result SamplerDesc::init(ContextVk *contextVk, vk::Sampler *sampler) const
...@@ -1841,6 +1854,25 @@ angle::Result SamplerDesc::init(ContextVk *contextVk, vk::Sampler *sampler) cons ...@@ -1841,6 +1854,25 @@ angle::Result SamplerDesc::init(ContextVk *contextVk, vk::Sampler *sampler) cons
vk::AddToPNextChain(&createInfo, &filteringInfo); 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)); ANGLE_VK_TRY(contextVk, sampler->init(contextVk->getDevice(), createInfo));
return angle::Result::Continue; return angle::Result::Continue;
...@@ -2167,6 +2199,73 @@ angle::Result PipelineLayoutCache::getPipelineLayout( ...@@ -2167,6 +2199,73 @@ angle::Result PipelineLayoutCache::getPipelineLayout(
return angle::Result::Continue; 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<vk::SamplerYcbcrConversion> *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 implementation.
SamplerCache::SamplerCache() = default; SamplerCache::SamplerCache() = default;
......
...@@ -26,9 +26,10 @@ enum class ImageLayout; ...@@ -26,9 +26,10 @@ enum class ImageLayout;
using RenderPassAndSerial = ObjectAndSerial<RenderPass>; using RenderPassAndSerial = ObjectAndSerial<RenderPass>;
using PipelineAndSerial = ObjectAndSerial<Pipeline>; using PipelineAndSerial = ObjectAndSerial<Pipeline>;
using RefCountedDescriptorSetLayout = RefCounted<DescriptorSetLayout>; using RefCountedDescriptorSetLayout = RefCounted<DescriptorSetLayout>;
using RefCountedPipelineLayout = RefCounted<PipelineLayout>; using RefCountedPipelineLayout = RefCounted<PipelineLayout>;
using RefCountedSampler = RefCounted<Sampler>; using RefCountedSampler = RefCounted<Sampler>;
using RefCountedSamplerYcbcrConversion = RefCounted<SamplerYcbcrConversion>;
// Helper macro that casts to a bitfield type then verifies no bits were dropped. // Helper macro that casts to a bitfield type then verifies no bits were dropped.
#define SetBitField(lhs, rhs) \ #define SetBitField(lhs, rhs) \
...@@ -609,13 +610,13 @@ class SamplerDesc final ...@@ -609,13 +610,13 @@ class SamplerDesc final
{ {
public: public:
SamplerDesc(); SamplerDesc();
explicit SamplerDesc(const gl::SamplerState &samplerState, bool stencilMode); SamplerDesc(const gl::SamplerState &samplerState, bool stencilMode, uint64_t externalFormat);
~SamplerDesc(); ~SamplerDesc();
SamplerDesc(const SamplerDesc &other); SamplerDesc(const SamplerDesc &other);
SamplerDesc &operator=(const SamplerDesc &rhs); 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(); void reset();
angle::Result init(ContextVk *contextVk, vk::Sampler *sampler) const; angle::Result init(ContextVk *contextVk, vk::Sampler *sampler) const;
...@@ -630,6 +631,14 @@ class SamplerDesc final ...@@ -630,6 +631,14 @@ class SamplerDesc final
float mMinLod; float mMinLod;
float mMaxLod; 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. // 16 bits for modes + states.
// 1 bit per filter (only 2 possible values in GL: linear/nearest) // 1 bit per filter (only 2 possible values in GL: linear/nearest)
uint16_t mMagFilter : 1; uint16_t mMagFilter : 1;
...@@ -649,12 +658,11 @@ class SamplerDesc final ...@@ -649,12 +658,11 @@ class SamplerDesc final
// Border color and unnormalized coordinates implicitly set to contants. // Border color and unnormalized coordinates implicitly set to contants.
// 16 extra bits reserved for future use. // 48 extra bits reserved for future use.
uint16_t mReserved; uint16_t mReserved[3];
}; };
// Total size: 160 bits == 20 bytes. static_assert(sizeof(SamplerDesc) == 32, "Unexpected SamplerDesc size");
static_assert(sizeof(SamplerDesc) == 20, "Unexpected SamplerDesc size");
// Disable warnings about struct padding. // Disable warnings about struct padding.
ANGLE_DISABLE_STRUCT_PADDING_WARNINGS ANGLE_DISABLE_STRUCT_PADDING_WARNINGS
...@@ -1051,6 +1059,26 @@ class SamplerCache final : angle::NonCopyable ...@@ -1051,6 +1059,26 @@ class SamplerCache final : angle::NonCopyable
std::unordered_map<vk::SamplerDesc, vk::RefCountedSampler> mPayload; std::unordered_map<vk::SamplerDesc, vk::RefCountedSampler> 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<vk::SamplerYcbcrConversion> *yuvConversionOut);
VkSamplerYcbcrConversion getYuvConversionFromExternalFormat(uint64_t externalFormat) const;
private:
std::unordered_map<uint64_t, vk::RefCountedSamplerYcbcrConversion> mPayload;
};
// Some descriptor set and pipeline layout constants. // Some descriptor set and pipeline layout constants.
// //
// The set/binding assignment is done as following: // The set/binding assignment is done as following:
......
...@@ -1145,7 +1145,15 @@ void Format::initialize(RendererVk *renderer, const angle::Format &angleFormat) ...@@ -1145,7 +1145,15 @@ void Format::initialize(RendererVk *renderer, const angle::Format &angleFormat)
break; break;
case angle::FormatID::NONE: 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; break;
case angle::FormatID::PVRTC1_RGBA_2BPP_UNORM_BLOCK: case angle::FormatID::PVRTC1_RGBA_2BPP_UNORM_BLOCK:
......
...@@ -189,6 +189,13 @@ size_t Format::getImageCopyBufferAlignment() const ...@@ -189,6 +189,13 @@ size_t Format::getImageCopyBufferAlignment() const
return alignment; return alignment;
} }
size_t Format::getValidImageCopyBufferAlignment() const
{
constexpr size_t kMinimumAlignment = 16;
return (intendedFormatID == angle::FormatID::NONE) ? kMinimumAlignment
: getImageCopyBufferAlignment();
}
bool Format::hasEmulatedImageChannels() const bool Format::hasEmulatedImageChannels() const
{ {
const angle::Format &angleFmt = intendedFormat(); const angle::Format &angleFmt = intendedFormat();
......
...@@ -84,6 +84,7 @@ struct Format final : private angle::NonCopyable ...@@ -84,6 +84,7 @@ struct Format final : private angle::NonCopyable
// Returns buffer alignment for image-copy operations (to or from a buffer). // Returns buffer alignment for image-copy operations (to or from a buffer).
size_t getImageCopyBufferAlignment() const; size_t getImageCopyBufferAlignment() const;
size_t getValidImageCopyBufferAlignment() const;
// Returns true if the Image format has more channels than the ANGLE format. // Returns true if the Image format has more channels than the ANGLE format.
bool hasEmulatedImageChannels() const; bool hasEmulatedImageChannels() const;
......
...@@ -2469,6 +2469,8 @@ ImageHelper::ImageHelper(ImageHelper &&other) ...@@ -2469,6 +2469,8 @@ ImageHelper::ImageHelper(ImageHelper &&other)
mCurrentQueueFamilyIndex(other.mCurrentQueueFamilyIndex), mCurrentQueueFamilyIndex(other.mCurrentQueueFamilyIndex),
mLastNonShaderReadOnlyLayout(other.mLastNonShaderReadOnlyLayout), mLastNonShaderReadOnlyLayout(other.mLastNonShaderReadOnlyLayout),
mCurrentShaderReadStageMask(other.mCurrentShaderReadStageMask), mCurrentShaderReadStageMask(other.mCurrentShaderReadStageMask),
mYuvConversionSampler(std::move(other.mYuvConversionSampler)),
mExternalFormat(other.mExternalFormat),
mBaseLevel(other.mBaseLevel), mBaseLevel(other.mBaseLevel),
mMaxLevel(other.mMaxLevel), mMaxLevel(other.mMaxLevel),
mLayerCount(other.mLayerCount), mLayerCount(other.mLayerCount),
...@@ -2503,6 +2505,7 @@ void ImageHelper::resetCachedProperties() ...@@ -2503,6 +2505,7 @@ void ImageHelper::resetCachedProperties()
mLayerCount = 0; mLayerCount = 0;
mLevelCount = 0; mLevelCount = 0;
mCurrentSingleClearValue.reset(); mCurrentSingleClearValue.reset();
mExternalFormat = 0;
} }
void ImageHelper::initStagingBuffer(RendererVk *renderer, void ImageHelper::initStagingBuffer(RendererVk *renderer,
...@@ -2582,6 +2585,9 @@ angle::Result ImageHelper::initExternal(Context *context, ...@@ -2582,6 +2585,9 @@ angle::Result ImageHelper::initExternal(Context *context,
mCurrentLayout = initialLayout; mCurrentLayout = initialLayout;
mYuvConversionSampler.reset();
mExternalFormat = 0;
ANGLE_VK_TRY(context, mImage.init(context->getDevice(), imageInfo)); ANGLE_VK_TRY(context, mImage.init(context->getDevice(), imageInfo));
stageClearIfEmulatedFormat(context); stageClearIfEmulatedFormat(context);
...@@ -2736,18 +2742,34 @@ angle::Result ImageHelper::initMemory(Context *context, ...@@ -2736,18 +2742,34 @@ angle::Result ImageHelper::initMemory(Context *context,
return angle::Result::Continue; return angle::Result::Continue;
} }
angle::Result ImageHelper::initExternalMemory(Context *context, angle::Result ImageHelper::initExternalMemory(
const MemoryProperties &memoryProperties, Context *context,
const VkMemoryRequirements &memoryRequirements, const MemoryProperties &memoryProperties,
const void *extraAllocationInfo, const VkMemoryRequirements &memoryRequirements,
uint32_t currentQueueFamilyIndex, const VkSamplerYcbcrConversionCreateInfo *samplerYcbcrConversionCreateInfo,
const void *extraAllocationInfo,
uint32_t currentQueueFamilyIndex,
VkMemoryPropertyFlags flags) VkMemoryPropertyFlags flags)
{ {
// TODO(jmadill): Memory sub-allocation. http://anglebug.com/2162 // TODO(jmadill): Memory sub-allocation. http://anglebug.com/2162
ANGLE_TRY(AllocateImageMemoryWithRequirements(context, flags, memoryRequirements, ANGLE_TRY(AllocateImageMemoryWithRequirements(context, flags, memoryRequirements,
extraAllocationInfo, &mImage, &mDeviceMemory)); extraAllocationInfo, &mImage, &mDeviceMemory));
mCurrentQueueFamilyIndex = currentQueueFamilyIndex; mCurrentQueueFamilyIndex = currentQueueFamilyIndex;
#ifdef VK_USE_PLATFORM_ANDROID_KHR
if (samplerYcbcrConversionCreateInfo)
{
const VkExternalFormatANDROID *vkExternalFormat =
reinterpret_cast<const VkExternalFormatANDROID *>(
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; return angle::Result::Continue;
} }
...@@ -2797,9 +2819,8 @@ angle::Result ImageHelper::initLayerImageViewImpl( ...@@ -2797,9 +2819,8 @@ angle::Result ImageHelper::initLayerImageViewImpl(
viewInfo.image = mImage.getHandle(); viewInfo.image = mImage.getHandle();
viewInfo.viewType = gl_vk::GetImageViewType(textureType); viewInfo.viewType = gl_vk::GetImageViewType(textureType);
viewInfo.format = imageFormat; 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.r = gl_vk::GetSwizzle(swizzleMap.swizzleRed);
viewInfo.components.g = gl_vk::GetSwizzle(swizzleMap.swizzleGreen); viewInfo.components.g = gl_vk::GetSwizzle(swizzleMap.swizzleGreen);
...@@ -2821,6 +2842,15 @@ angle::Result ImageHelper::initLayerImageViewImpl( ...@@ -2821,6 +2842,15 @@ angle::Result ImageHelper::initLayerImageViewImpl(
viewInfo.pNext = imageViewUsageCreateInfo; 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)); ANGLE_VK_TRY(context, imageViewOut->init(context->getDevice(), viewInfo));
return angle::Result::Continue; return angle::Result::Continue;
} }
......
...@@ -1060,12 +1060,14 @@ class ImageHelper final : public Resource, public angle::Subject ...@@ -1060,12 +1060,14 @@ class ImageHelper final : public Resource, public angle::Subject
angle::Result initMemory(Context *context, angle::Result initMemory(Context *context,
const MemoryProperties &memoryProperties, const MemoryProperties &memoryProperties,
VkMemoryPropertyFlags flags); VkMemoryPropertyFlags flags);
angle::Result initExternalMemory(Context *context, angle::Result initExternalMemory(
const MemoryProperties &memoryProperties, Context *context,
const VkMemoryRequirements &memoryRequirements, const MemoryProperties &memoryProperties,
const void *extraAllocationInfo, const VkMemoryRequirements &memoryRequirements,
uint32_t currentQueueFamilyIndex, const VkSamplerYcbcrConversionCreateInfo *samplerYcbcrConversionCreateInfo,
VkMemoryPropertyFlags flags); const void *extraAllocationInfo,
uint32_t currentQueueFamilyIndex,
VkMemoryPropertyFlags flags);
angle::Result initLayerImageView(Context *context, angle::Result initLayerImageView(Context *context,
gl::TextureType textureType, gl::TextureType textureType,
VkImageAspectFlags aspectMask, VkImageAspectFlags aspectMask,
...@@ -1387,6 +1389,8 @@ class ImageHelper final : public Resource, public angle::Subject ...@@ -1387,6 +1389,8 @@ class ImageHelper final : public Resource, public angle::Subject
GLuint *inputSkipBytes); GLuint *inputSkipBytes);
void onWrite() { mCurrentSingleClearValue.reset(); } void onWrite() { mCurrentSingleClearValue.reset(); }
bool hasImmutableSampler() { return mExternalFormat != 0; }
uint64_t getExternalFormat() const { return mExternalFormat; }
private: private:
enum class UpdateSource enum class UpdateSource
...@@ -1523,6 +1527,10 @@ class ImageHelper final : public Resource, public angle::Subject ...@@ -1523,6 +1527,10 @@ class ImageHelper final : public Resource, public angle::Subject
ImageLayout mLastNonShaderReadOnlyLayout; ImageLayout mLastNonShaderReadOnlyLayout;
VkPipelineStageFlags mCurrentShaderReadStageMask; VkPipelineStageFlags mCurrentShaderReadStageMask;
// For imported images
vk::BindingPointer<vk::SamplerYcbcrConversion> mYuvConversionSampler;
uint64_t mExternalFormat;
// Cached properties. // Cached properties.
uint32_t mBaseLevel; uint32_t mBaseLevel;
uint32_t mMaxLevel; uint32_t mMaxLevel;
......
...@@ -775,6 +775,10 @@ PFN_vkImportFenceFdKHR vkImportFenceFdKHR = nullptr; ...@@ -775,6 +775,10 @@ PFN_vkImportFenceFdKHR vkImportFenceFdKHR = nullptr;
PFN_vkGetPhysicalDeviceExternalSemaphorePropertiesKHR PFN_vkGetPhysicalDeviceExternalSemaphorePropertiesKHR
vkGetPhysicalDeviceExternalSemaphorePropertiesKHR = nullptr; vkGetPhysicalDeviceExternalSemaphorePropertiesKHR = nullptr;
// VK_KHR_sampler_ycbcr_conversion
PFN_vkCreateSamplerYcbcrConversionKHR vkCreateSamplerYcbcrConversionKHR = nullptr;
PFN_vkDestroySamplerYcbcrConversionKHR vkDestroySamplerYcbcrConversionKHR = nullptr;
# if defined(ANGLE_PLATFORM_FUCHSIA) # if defined(ANGLE_PLATFORM_FUCHSIA)
// VK_FUCHSIA_imagepipe_surface // VK_FUCHSIA_imagepipe_surface
PFN_vkCreateImagePipeSurfaceFUCHSIA vkCreateImagePipeSurfaceFUCHSIA = nullptr; PFN_vkCreateImagePipeSurfaceFUCHSIA vkCreateImagePipeSurfaceFUCHSIA = nullptr;
...@@ -835,6 +839,13 @@ void InitTransformFeedbackEXTFunctions(VkDevice device) ...@@ -835,6 +839,13 @@ void InitTransformFeedbackEXTFunctions(VkDevice device)
GET_DEVICE_FUNC(vkCmdDrawIndirectByteCountEXT); 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) # if defined(ANGLE_PLATFORM_FUCHSIA)
void InitImagePipeSurfaceFUCHSIAFunctions(VkInstance instance) void InitImagePipeSurfaceFUCHSIAFunctions(VkInstance instance)
{ {
......
...@@ -482,6 +482,12 @@ class BindingPointer final : angle::NonCopyable ...@@ -482,6 +482,12 @@ class BindingPointer final : angle::NonCopyable
~BindingPointer() { reset(); } ~BindingPointer() { reset(); }
BindingPointer(BindingPointer &&other)
{
set(other.mRefCounted);
other.reset();
}
void set(RefCounted<T> *refCounted) void set(RefCounted<T> *refCounted)
{ {
if (mRefCounted) if (mRefCounted)
...@@ -685,6 +691,7 @@ void InitDebugUtilsEXTFunctions(VkInstance instance); ...@@ -685,6 +691,7 @@ void InitDebugUtilsEXTFunctions(VkInstance instance);
void InitDebugReportEXTFunctions(VkInstance instance); void InitDebugReportEXTFunctions(VkInstance instance);
void InitGetPhysicalDeviceProperties2KHRFunctions(VkInstance instance); void InitGetPhysicalDeviceProperties2KHRFunctions(VkInstance instance);
void InitTransformFeedbackEXTFunctions(VkDevice device); void InitTransformFeedbackEXTFunctions(VkDevice device);
void InitSamplerYcbcrKHRFunctions(VkDevice device);
# if defined(ANGLE_PLATFORM_FUCHSIA) # if defined(ANGLE_PLATFORM_FUCHSIA)
// VK_FUCHSIA_imagepipe_surface // VK_FUCHSIA_imagepipe_surface
......
...@@ -48,6 +48,7 @@ namespace vk ...@@ -48,6 +48,7 @@ namespace vk
FUNC(QueryPool) \ FUNC(QueryPool) \
FUNC(RenderPass) \ FUNC(RenderPass) \
FUNC(Sampler) \ FUNC(Sampler) \
FUNC(SamplerYcbcrConversion) \
FUNC(Semaphore) \ FUNC(Semaphore) \
FUNC(ShaderModule) FUNC(ShaderModule)
...@@ -606,6 +607,15 @@ class Sampler final : public WrappedObject<Sampler, VkSampler> ...@@ -606,6 +607,15 @@ class Sampler final : public WrappedObject<Sampler, VkSampler>
VkResult init(VkDevice device, const VkSamplerCreateInfo &createInfo); VkResult init(VkDevice device, const VkSamplerCreateInfo &createInfo);
}; };
class SamplerYcbcrConversion final
: public WrappedObject<SamplerYcbcrConversion, VkSamplerYcbcrConversion>
{
public:
SamplerYcbcrConversion() = default;
void destroy(VkDevice device);
VkResult init(VkDevice device, const VkSamplerYcbcrConversionCreateInfo &createInfo);
};
class Event final : public WrappedObject<Event, VkEvent> class Event final : public WrappedObject<Event, VkEvent>
{ {
public: public:
...@@ -1698,6 +1708,23 @@ ANGLE_INLINE VkResult Sampler::init(VkDevice device, const VkSamplerCreateInfo & ...@@ -1698,6 +1708,23 @@ ANGLE_INLINE VkResult Sampler::init(VkDevice device, const VkSamplerCreateInfo &
return vkCreateSampler(device, &createInfo, nullptr, &mHandle); 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. // Event implementation.
ANGLE_INLINE void Event::destroy(VkDevice device) ANGLE_INLINE void Event::destroy(VkDevice device)
{ {
......
Markdown is supported
0% or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment