| // Copyright 2018 The Dawn & Tint Authors | |
| // | |
| // Redistribution and use in source and binary forms, with or without | |
| // modification, are permitted provided that the following conditions are met: | |
| // | |
| // 1. Redistributions of source code must retain the above copyright notice, this | |
| // list of conditions and the following disclaimer. | |
| // | |
| // 2. Redistributions in binary form must reproduce the above copyright notice, | |
| // this list of conditions and the following disclaimer in the documentation | |
| // and/or other materials provided with the distribution. | |
| // | |
| // 3. Neither the name of the copyright holder nor the names of its | |
| // contributors may be used to endorse or promote products derived from | |
| // this software without specific prior written permission. | |
| // | |
| // THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" | |
| // AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE | |
| // IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE | |
| // DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE | |
| // FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL | |
| // DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR | |
| // SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER | |
| // CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, | |
| // OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE | |
| // OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. | |
| #include "src/dawn/native/vulkan/SamplerVk.h" | |
| #include <algorithm> | |
| #include "src/dawn/native/ChainUtils.h" | |
| #include "src/dawn/native/vulkan/DeviceVk.h" | |
| #include "src/dawn/native/vulkan/FencedDeleter.h" | |
| #include "src/dawn/native/vulkan/TextureVk.h" | |
| #include "src/dawn/native/vulkan/UtilsVulkan.h" | |
| #include "src/dawn/native/vulkan/VulkanError.h" | |
| #include "vulkan/vulkan_core.h" | |
| namespace dawn::native::vulkan { | |
| namespace { | |
| VkSamplerAddressMode VulkanSamplerAddressMode(wgpu::AddressMode mode) { | |
| switch (mode) { | |
| case wgpu::AddressMode::Repeat: | |
| return VK_SAMPLER_ADDRESS_MODE_REPEAT; | |
| case wgpu::AddressMode::MirrorRepeat: | |
| return VK_SAMPLER_ADDRESS_MODE_MIRRORED_REPEAT; | |
| case wgpu::AddressMode::ClampToEdge: | |
| return VK_SAMPLER_ADDRESS_MODE_CLAMP_TO_EDGE; | |
| case wgpu::AddressMode::Undefined: | |
| break; | |
| } | |
| DAWN_UNREACHABLE(); | |
| } | |
| VkSamplerMipmapMode VulkanMipMapMode(wgpu::MipmapFilterMode filter) { | |
| switch (filter) { | |
| case wgpu::MipmapFilterMode::Linear: | |
| return VK_SAMPLER_MIPMAP_MODE_LINEAR; | |
| case wgpu::MipmapFilterMode::Nearest: | |
| return VK_SAMPLER_MIPMAP_MODE_NEAREST; | |
| case wgpu::MipmapFilterMode::Undefined: | |
| break; | |
| } | |
| DAWN_UNREACHABLE(); | |
| } | |
| } // anonymous namespace | |
| // static | |
| ResultOrError<Ref<Sampler>> Sampler::Create(Device* device, const SamplerDescriptor* descriptor) { | |
| Ref<Sampler> sampler = AcquireRef(new Sampler(device, descriptor)); | |
| DAWN_TRY(sampler->Initialize(descriptor)); | |
| return sampler; | |
| } | |
| // static | |
| ResultOrError<Ref<Sampler>> Sampler::Create(Device* device, const StaticSamplerSpecialization& s) { | |
| SamplerDescriptor samplerDesc; | |
| samplerDesc.magFilter = s.magFilter; | |
| samplerDesc.minFilter = s.minFilter; | |
| YCbCrVkDescriptor yCbCrDesc; | |
| if (s.isYCbCr) { | |
| yCbCrDesc = StaticSamplerSpecialization::GetYCbCrForTextureView(s.vkFormat, | |
| s.androidExternalFormat); | |
| samplerDesc.nextInChain = &yCbCrDesc; | |
| } | |
| // Create the Sampler, skipping validation: the frontend requires the YCbCrVulkanSamplers | |
| // feature enabled to be able to use YCbCrVkDescriptor. However the | |
| // OpaqueYCbCrAndroidForExternalTexture can be used without YCbCrVulkanSamplers, in which case | |
| // a validation error is emitted. | |
| Ref<SamplerBase> sampler; | |
| DAWN_TRY_ASSIGN(sampler, device->CreateSampler(&samplerDesc, ValidationMode::Skip)); | |
| device->CacheStaticSampler(ToBackend(sampler)); | |
| return ToBackend(sampler); | |
| } | |
| MaybeError Sampler::Initialize(const SamplerDescriptor* descriptor) { | |
| VkSamplerCreateInfo createInfo = {}; | |
| createInfo.sType = VK_STRUCTURE_TYPE_SAMPLER_CREATE_INFO; | |
| createInfo.pNext = nullptr; | |
| createInfo.flags = 0; | |
| createInfo.magFilter = ToVulkanSamplerFilter(descriptor->magFilter); | |
| createInfo.minFilter = ToVulkanSamplerFilter(descriptor->minFilter); | |
| createInfo.mipmapMode = VulkanMipMapMode(descriptor->mipmapFilter); | |
| createInfo.addressModeU = VulkanSamplerAddressMode(descriptor->addressModeU); | |
| createInfo.addressModeV = VulkanSamplerAddressMode(descriptor->addressModeV); | |
| createInfo.addressModeW = VulkanSamplerAddressMode(descriptor->addressModeW); | |
| createInfo.mipLodBias = 0.0f; | |
| if (descriptor->compare != wgpu::CompareFunction::Undefined) { | |
| createInfo.compareOp = ToVulkanCompareOp(descriptor->compare); | |
| createInfo.compareEnable = VK_TRUE; | |
| } else { | |
| // Still set the compareOp so it's not garbage. | |
| createInfo.compareOp = VK_COMPARE_OP_NEVER; | |
| createInfo.compareEnable = VK_FALSE; | |
| } | |
| createInfo.minLod = descriptor->lodMinClamp; | |
| createInfo.maxLod = descriptor->lodMaxClamp; | |
| createInfo.unnormalizedCoordinates = VK_FALSE; | |
| Device* device = ToBackend(GetDevice()); | |
| uint16_t maxAnisotropy = GetMaxAnisotropy(); | |
| if (device->GetDeviceInfo().features.samplerAnisotropy == VK_TRUE && maxAnisotropy > 1) { | |
| createInfo.anisotropyEnable = VK_TRUE; | |
| // https://www.khronos.org/registry/vulkan/specs/1.2-extensions/man/html/VkSamplerCreateInfo.html | |
| createInfo.maxAnisotropy = | |
| std::min(static_cast<float>(maxAnisotropy), | |
| device->GetDeviceInfo().properties.limits.maxSamplerAnisotropy); | |
| } else { | |
| createInfo.anisotropyEnable = VK_FALSE; | |
| createInfo.maxAnisotropy = 1; | |
| } | |
| VkSamplerYcbcrConversionInfo samplerYCbCrInfo = {}; | |
| if (IsYCbCr()) { | |
| DAWN_TRY_ASSIGN(mSamplerYCbCrConversion, | |
| CreateSamplerYCbCrConversion(device, GetYCbCrVkDescriptor())); | |
| samplerYCbCrInfo.sType = VK_STRUCTURE_TYPE_SAMPLER_YCBCR_CONVERSION_INFO; | |
| samplerYCbCrInfo.pNext = nullptr; | |
| samplerYCbCrInfo.conversion = mSamplerYCbCrConversion; | |
| createInfo.pNext = &samplerYCbCrInfo; | |
| // VUID-VkSamplerCreateInfo-addressModeU-01646 requires CLAMP_TO_EDGE on every axis and | |
| // anisotropy disabled when VkSamplerYcbcrConversionInfo is provided. | |
| 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; | |
| } | |
| DAWN_TRY(CheckVkSuccess( | |
| device->fn.CreateSampler(device->GetVkDevice(), &createInfo, nullptr, &*mHandle), | |
| "CreateSampler")); | |
| SetLabelImpl(); | |
| return {}; | |
| } | |
| Sampler::~Sampler() = default; | |
| void Sampler::DestroyImpl(DestroyReason reason) { | |
| SamplerBase::DestroyImpl(reason); | |
| if (mSamplerYCbCrConversion != VK_NULL_HANDLE) { | |
| ToBackend(GetDevice())->GetFencedDeleter()->DeleteWhenUnused(mSamplerYCbCrConversion); | |
| mSamplerYCbCrConversion = VK_NULL_HANDLE; | |
| } | |
| if (mHandle != VK_NULL_HANDLE) { | |
| ToBackend(GetDevice())->GetFencedDeleter()->DeleteWhenUnused(mHandle); | |
| mHandle = VK_NULL_HANDLE; | |
| } | |
| } | |
| const VkSampler& Sampler::GetHandle() const { | |
| return mHandle; | |
| } | |
| void Sampler::SetLabelImpl() { | |
| SetDebugName(ToBackend(GetDevice()), mHandle, "Dawn_Sampler", GetLabel()); | |
| } | |
| // static | |
| StaticSamplerSpecialization StaticSamplerSpecialization::From(const TextureView* view, | |
| const Sampler* sampler) { | |
| bool isYCbCr = | |
| view->GetTexture()->GetFormat().format == wgpu::TextureFormat::OpaqueYCbCrAndroid; | |
| bool filterable = !isYCbCr || view->IsYCbCrFilterable(); | |
| StaticSamplerSpecialization spec = { | |
| .minFilter = (sampler != nullptr && filterable) ? sampler->GetMinFilter() | |
| : wgpu::FilterMode::Nearest, | |
| .magFilter = (sampler != nullptr && filterable) ? sampler->GetMagFilter() | |
| : wgpu::FilterMode::Nearest, | |
| .isYCbCr = isYCbCr, | |
| // Put default values for the YCbCr part. | |
| .vkFormat = VK_FORMAT_UNDEFINED, | |
| .androidExternalFormat = 0, | |
| }; | |
| if (isYCbCr) { | |
| auto stm = static_cast<SharedTextureMemoryContentsVk*>( | |
| view->GetTexture()->GetSharedResourceMemoryContents()); | |
| DAWN_ASSERT(stm != nullptr); | |
| spec.vkFormat = static_cast<VkFormat>(stm->GetYCbCrVkDesc().vkFormat); | |
| spec.androidExternalFormat = stm->GetYCbCrVkDesc().externalFormat; | |
| } | |
| return spec; | |
| } | |
| // static | |
| YCbCrVkDescriptor StaticSamplerSpecialization::GetYCbCrForTextureView( | |
| VkFormat vkFormat, | |
| uint32_t androidExternalFormat) { | |
| YCbCrVkDescriptor yCbCrDesc; | |
| yCbCrDesc.vkFormat = vkFormat; | |
| yCbCrDesc.externalFormat = androidExternalFormat; | |
| yCbCrDesc.vkYCbCrModel = VK_SAMPLER_YCBCR_MODEL_CONVERSION_RGB_IDENTITY; | |
| yCbCrDesc.vkYCbCrRange = VK_SAMPLER_YCBCR_RANGE_ITU_FULL; | |
| yCbCrDesc.vkComponentSwizzleRed = VK_COMPONENT_SWIZZLE_R; | |
| yCbCrDesc.vkComponentSwizzleGreen = VK_COMPONENT_SWIZZLE_G; | |
| yCbCrDesc.vkComponentSwizzleBlue = VK_COMPONENT_SWIZZLE_B; | |
| yCbCrDesc.vkComponentSwizzleAlpha = VK_COMPONENT_SWIZZLE_A; | |
| yCbCrDesc.vkXChromaOffset = VK_CHROMA_LOCATION_MIDPOINT; | |
| yCbCrDesc.vkYChromaOffset = VK_CHROMA_LOCATION_MIDPOINT; | |
| yCbCrDesc.vkChromaFilter = wgpu::FilterMode::Nearest; | |
| yCbCrDesc.forceExplicitReconstruction = false; | |
| return yCbCrDesc; | |
| } | |
| } // namespace dawn::native::vulkan |