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 "GrVkSampler.h"
9
10#include "GrVkGpu.h"
11
12static inline VkSamplerAddressMode tile_to_vk_sampler_address(SkShader::TileMode tm) {
13    static const VkSamplerAddressMode gWrapModes[] = {
14        VK_SAMPLER_ADDRESS_MODE_CLAMP_TO_EDGE,
15        VK_SAMPLER_ADDRESS_MODE_REPEAT,
16        VK_SAMPLER_ADDRESS_MODE_MIRRORED_REPEAT
17    };
18    GR_STATIC_ASSERT(SkShader::kTileModeCount == SK_ARRAY_COUNT(gWrapModes));
19    GR_STATIC_ASSERT(0 == SkShader::kClamp_TileMode);
20    GR_STATIC_ASSERT(1 == SkShader::kRepeat_TileMode);
21    GR_STATIC_ASSERT(2 == SkShader::kMirror_TileMode);
22    return gWrapModes[tm];
23}
24
25GrVkSampler* GrVkSampler::Create(const GrVkGpu* gpu, const GrSamplerParams& params,
26                                 uint32_t mipLevels) {
27    static VkFilter vkMinFilterModes[] = {
28        VK_FILTER_NEAREST,
29        VK_FILTER_LINEAR,
30        VK_FILTER_LINEAR
31    };
32    static VkFilter vkMagFilterModes[] = {
33        VK_FILTER_NEAREST,
34        VK_FILTER_LINEAR,
35        VK_FILTER_LINEAR
36    };
37
38    VkSamplerCreateInfo createInfo;
39    memset(&createInfo, 0, sizeof(VkSamplerCreateInfo));
40    createInfo.sType = VK_STRUCTURE_TYPE_SAMPLER_CREATE_INFO;
41    createInfo.pNext = 0;
42    createInfo.flags = 0;
43    createInfo.magFilter = vkMagFilterModes[params.filterMode()];
44    createInfo.minFilter = vkMinFilterModes[params.filterMode()];
45    createInfo.mipmapMode = VK_SAMPLER_MIPMAP_MODE_LINEAR;
46    createInfo.addressModeU = tile_to_vk_sampler_address(params.getTileModeX());
47    createInfo.addressModeV = tile_to_vk_sampler_address(params.getTileModeY());
48    createInfo.addressModeW = VK_SAMPLER_ADDRESS_MODE_CLAMP_TO_EDGE; // Shouldn't matter
49    createInfo.mipLodBias = 0.0f;
50    createInfo.anisotropyEnable = VK_FALSE;
51    createInfo.maxAnisotropy = 1.0f;
52    createInfo.compareEnable = VK_FALSE;
53    createInfo.compareOp = VK_COMPARE_OP_NEVER;
54    // Vulkan doesn't have a direct mapping of GL's nearest or linear filters for minFilter since
55    // there is always a mipmapMode. To get the same effect as GL we can set minLod = maxLod = 0.0.
56    // This works since our min and mag filters are the same (this forces us to use mag on the 0
57    // level mip). If the filters weren't the same we could set min = 0 and max = 0.25 to force
58    // the minFilter on mip level 0.
59    createInfo.minLod = 0.0f;
60    bool useMipMaps = GrSamplerParams::kMipMap_FilterMode == params.filterMode() && mipLevels > 1;
61    createInfo.maxLod = !useMipMaps ? 0.0f : (float)(mipLevels);
62    createInfo.borderColor = VK_BORDER_COLOR_FLOAT_TRANSPARENT_BLACK;
63    createInfo.unnormalizedCoordinates = VK_FALSE;
64
65    VkSampler sampler;
66    GR_VK_CALL_ERRCHECK(gpu->vkInterface(), CreateSampler(gpu->device(),
67                                                          &createInfo,
68                                                          nullptr,
69                                                          &sampler));
70
71    return new GrVkSampler(sampler, GenerateKey(params, mipLevels));
72}
73
74void GrVkSampler::freeGPUData(const GrVkGpu* gpu) const {
75    SkASSERT(fSampler);
76    GR_VK_CALL(gpu->vkInterface(), DestroySampler(gpu->device(), fSampler, nullptr));
77}
78
79uint16_t GrVkSampler::GenerateKey(const GrSamplerParams& params, uint32_t mipLevels) {
80    const int kTileModeXShift = 2;
81    const int kTileModeYShift = 4;
82    const int kMipLevelShift = 6;
83
84    uint16_t key = params.filterMode();
85
86    SkASSERT(params.filterMode() <= 3);
87    key |= (params.getTileModeX() << kTileModeXShift);
88
89    GR_STATIC_ASSERT(SkShader::kTileModeCount <= 4);
90    key |= (params.getTileModeY() << kTileModeYShift);
91
92    SkASSERT(mipLevels < 1024);
93    key |= (mipLevels << kMipLevelShift);
94
95    return key;
96}
97