1/* 2* Copyright 2016 Google Inc. 3* 4* Use of this source code is governed by a BSD-style license that can be 5* found in the LICENSE file. 6*/ 7 8#include "src/gpu/vk/GrVkSampler.h" 9 10#include "src/gpu/vk/GrVkGpu.h" 11#include "src/gpu/vk/GrVkSamplerYcbcrConversion.h" 12 13static VkSamplerAddressMode wrap_mode_to_vk_sampler_address(GrSamplerState::WrapMode wrapMode) { 14 switch (wrapMode) { 15 case GrSamplerState::WrapMode::kClamp: 16 return VK_SAMPLER_ADDRESS_MODE_CLAMP_TO_EDGE; 17 case GrSamplerState::WrapMode::kRepeat: 18 return VK_SAMPLER_ADDRESS_MODE_REPEAT; 19 case GrSamplerState::WrapMode::kMirrorRepeat: 20 return VK_SAMPLER_ADDRESS_MODE_MIRRORED_REPEAT; 21 case GrSamplerState::WrapMode::kClampToBorder: 22 return VK_SAMPLER_ADDRESS_MODE_CLAMP_TO_BORDER; 23 } 24 SkUNREACHABLE; 25} 26 27static VkSamplerMipmapMode mipmap_mode_to_vk_sampler_mipmap_mode(GrSamplerState::MipmapMode mm) { 28 switch (mm) { 29 // There is no disable mode. We use max level to disable mip mapping. 30 // It may make more sense to use NEAREST for kNone but Chrome pixel tests seam dependent 31 // on subtle rendering differences introduced by switching this. 32 case GrSamplerState::MipmapMode::kNone: return VK_SAMPLER_MIPMAP_MODE_LINEAR; 33 case GrSamplerState::MipmapMode::kNearest: return VK_SAMPLER_MIPMAP_MODE_NEAREST; 34 case GrSamplerState::MipmapMode::kLinear: return VK_SAMPLER_MIPMAP_MODE_LINEAR; 35 } 36 SkUNREACHABLE; 37} 38 39GrVkSampler* GrVkSampler::Create(GrVkGpu* gpu, GrSamplerState samplerState, 40 const GrVkYcbcrConversionInfo& ycbcrInfo) { 41 static VkFilter vkMinFilterModes[] = { 42 VK_FILTER_NEAREST, 43 VK_FILTER_LINEAR, 44 VK_FILTER_LINEAR 45 }; 46 static VkFilter vkMagFilterModes[] = { 47 VK_FILTER_NEAREST, 48 VK_FILTER_LINEAR, 49 VK_FILTER_LINEAR 50 }; 51 52 VkSamplerCreateInfo createInfo; 53 memset(&createInfo, 0, sizeof(VkSamplerCreateInfo)); 54 createInfo.sType = VK_STRUCTURE_TYPE_SAMPLER_CREATE_INFO; 55 createInfo.pNext = nullptr; 56 createInfo.flags = 0; 57 createInfo.magFilter = vkMagFilterModes[static_cast<int>(samplerState.filter())]; 58 createInfo.minFilter = vkMinFilterModes[static_cast<int>(samplerState.filter())]; 59 createInfo.mipmapMode = mipmap_mode_to_vk_sampler_mipmap_mode(samplerState.mipmapMode()); 60 createInfo.addressModeU = wrap_mode_to_vk_sampler_address(samplerState.wrapModeX()); 61 createInfo.addressModeV = wrap_mode_to_vk_sampler_address(samplerState.wrapModeY()); 62 createInfo.addressModeW = VK_SAMPLER_ADDRESS_MODE_CLAMP_TO_EDGE; // Shouldn't matter 63 createInfo.mipLodBias = 0.0f; 64 createInfo.anisotropyEnable = VK_FALSE; 65 createInfo.maxAnisotropy = 1.0f; 66 createInfo.compareEnable = VK_FALSE; 67 createInfo.compareOp = VK_COMPARE_OP_NEVER; 68 // Vulkan doesn't have a direct mapping of GL's nearest or linear filters for minFilter since 69 // there is always a mipmapMode. To get the same effect as GL we can set minLod = maxLod = 0.0. 70 // This works since our min and mag filters are the same (this forces us to use mag on the 0 71 // level mip). If the filters weren't the same we could set min = 0 and max = 0.25 to force 72 // the minFilter on mip level 0. 73 createInfo.minLod = 0.0f; 74 bool useMipMaps = samplerState.mipmapped() == GrMipmapped::kYes; 75 createInfo.maxLod = !useMipMaps ? 0.0f : 10000.0f; 76 createInfo.borderColor = VK_BORDER_COLOR_FLOAT_TRANSPARENT_BLACK; 77 createInfo.unnormalizedCoordinates = VK_FALSE; 78 79 VkSamplerYcbcrConversionInfo conversionInfo; 80 GrVkSamplerYcbcrConversion* ycbcrConversion = nullptr; 81 if (ycbcrInfo.isValid()) { 82 SkASSERT(gpu->vkCaps().supportsYcbcrConversion()); 83 84 ycbcrConversion = 85 gpu->resourceProvider().findOrCreateCompatibleSamplerYcbcrConversion(ycbcrInfo); 86 if (!ycbcrConversion) { 87 return nullptr; 88 } 89 90 conversionInfo.sType = VK_STRUCTURE_TYPE_SAMPLER_YCBCR_CONVERSION_INFO; 91 conversionInfo.pNext = nullptr; 92 conversionInfo.conversion = ycbcrConversion->ycbcrConversion(); 93 94 createInfo.pNext = &conversionInfo; 95 96 VkFormatFeatureFlags flags = ycbcrInfo.fFormatFeatures; 97 if (!SkToBool(flags & VK_FORMAT_FEATURE_SAMPLED_IMAGE_YCBCR_CONVERSION_LINEAR_FILTER_BIT)) { 98 createInfo.magFilter = VK_FILTER_NEAREST; 99 createInfo.minFilter = VK_FILTER_NEAREST; 100 } else if ( 101 !(flags & 102 VK_FORMAT_FEATURE_SAMPLED_IMAGE_YCBCR_CONVERSION_SEPARATE_RECONSTRUCTION_FILTER_BIT)) { 103 createInfo.magFilter = ycbcrInfo.fChromaFilter; 104 createInfo.minFilter = ycbcrInfo.fChromaFilter; 105 } 106 107 // Required values when using ycbcr conversion 108 createInfo.addressModeU = VK_SAMPLER_ADDRESS_MODE_CLAMP_TO_EDGE; 109 createInfo.addressModeV = VK_SAMPLER_ADDRESS_MODE_CLAMP_TO_EDGE; 110 createInfo.addressModeW = VK_SAMPLER_ADDRESS_MODE_CLAMP_TO_EDGE; 111 createInfo.anisotropyEnable = VK_FALSE; 112 createInfo.unnormalizedCoordinates = VK_FALSE; 113 } 114 115 VkSampler sampler; 116 VkResult result; 117 GR_VK_CALL_RESULT(gpu, result, CreateSampler(gpu->device(), &createInfo, nullptr, &sampler)); 118 if (result != VK_SUCCESS) { 119 ycbcrConversion->unref(); 120 return nullptr; 121 } 122 123 return new GrVkSampler(gpu, sampler, ycbcrConversion, GenerateKey(samplerState, ycbcrInfo)); 124} 125 126void GrVkSampler::freeGPUData() const { 127 SkASSERT(fSampler); 128 GR_VK_CALL(fGpu->vkInterface(), DestroySampler(fGpu->device(), fSampler, nullptr)); 129 if (fYcbcrConversion) { 130 fYcbcrConversion->unref(); 131 } 132} 133 134GrVkSampler::Key GrVkSampler::GenerateKey(GrSamplerState samplerState, 135 const GrVkYcbcrConversionInfo& ycbcrInfo) { 136 return {samplerState.asIndex(), GrVkSamplerYcbcrConversion::GenerateKey(ycbcrInfo)}; 137} 138