1 /*-------------------------------------------------------------------------
2  * Vulkan Conformance Tests
3  * ------------------------
4  *
5  * Copyright (c) 2016 The Khronos Group Inc.
6  *
7  * Licensed under the Apache License, Version 2.0 (the "License");
8  * you may not use this file except in compliance with the License.
9  * You may obtain a copy of the License at
10  *
11  *      http://www.apache.org/licenses/LICENSE-2.0
12  *
13  * Unless required by applicable law or agreed to in writing, software
14  * distributed under the License is distributed on an "AS IS" BASIS,
15  * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
16  * See the License for the specific language governing permissions and
17  * limitations under the License.
18  *
19  *//*!
20  * \file
21  * \brief Texture filtering anisotropy tests
22  *//*--------------------------------------------------------------------*/
23 
24 #include "vktTextureFilteringAnisotropyTests.hpp"
25 
26 #include "vktTextureTestUtil.hpp"
27 #include "vkImageUtil.hpp"
28 #include "vkQueryUtil.hpp"
29 #include "tcuImageCompare.hpp"
30 #include <vector>
31 
32 using namespace vk;
33 
34 namespace vkt
35 {
36 namespace texture
37 {
38 
39 using std::max;
40 using std::min;
41 using std::string;
42 using std::vector;
43 using tcu::Sampler;
44 using tcu::Surface;
45 using tcu::TextureFormat;
46 using tcu::Vec2;
47 using tcu::Vec4;
48 using namespace texture::util;
49 using namespace glu::TextureTestUtil;
50 
51 namespace
52 {
53 static const uint32_t ANISOTROPY_TEST_RESOLUTION = 128u;
54 
55 struct AnisotropyParams : public ReferenceParams
56 {
AnisotropyParamsvkt::texture::__anon9c404a4a0111::AnisotropyParams57     AnisotropyParams(const TextureType texType_, const float maxAnisotropy_, const Sampler::FilterMode minFilter_,
58                      const Sampler::FilterMode magFilter_, const bool singleLevelImage_ = false,
59                      const bool mipMap_ = false)
60         : ReferenceParams(texType_)
61         , maxAnisotropy(maxAnisotropy_)
62         , minFilter(minFilter_)
63         , magFilter(magFilter_)
64         , singleLevelImage(singleLevelImage_)
65         , mipMap(mipMap_)
66     {
67     }
68 
69     float maxAnisotropy;
70     Sampler::FilterMode minFilter;
71     Sampler::FilterMode magFilter;
72     bool singleLevelImage;
73     bool mipMap;
74 };
75 
76 class FilteringAnisotropyInstance : public vkt::TestInstance
77 {
78 public:
FilteringAnisotropyInstance(Context & context,const AnisotropyParams & refParams)79     FilteringAnisotropyInstance(Context &context, const AnisotropyParams &refParams)
80         : vkt::TestInstance(context)
81         , m_refParams(refParams)
82     {
83         // Sampling parameters.
84         m_refParams.sampler = util::createSampler(Sampler::CLAMP_TO_EDGE, Sampler::CLAMP_TO_EDGE, m_refParams.minFilter,
85                                                   m_refParams.magFilter);
86         m_refParams.samplerType = getSamplerType(vk::mapVkFormat(VK_FORMAT_R8G8B8A8_UNORM));
87         m_refParams.flags       = 0u;
88         m_refParams.lodMode     = LODMODE_EXACT;
89         m_refParams.maxAnisotropy =
90             min(getPhysicalDeviceProperties(m_context.getInstanceInterface(), m_context.getPhysicalDevice())
91                     .limits.maxSamplerAnisotropy,
92                 m_refParams.maxAnisotropy);
93 
94         if (m_refParams.mipMap)
95         {
96             m_refParams.maxLevel = deLog2Floor32(ANISOTROPY_TEST_RESOLUTION);
97             m_refParams.minLod   = 0.0f;
98             m_refParams.maxLod   = static_cast<float>(m_refParams.maxLevel);
99         }
100         else
101             m_refParams.maxLevel = 0;
102     }
103 
iterate(void)104     tcu::TestStatus iterate(void)
105     {
106         TextureRenderer renderer(m_context, VK_SAMPLE_COUNT_1_BIT, ANISOTROPY_TEST_RESOLUTION,
107                                  ANISOTROPY_TEST_RESOLUTION);
108         TestTexture2DSp texture;
109 
110         if (m_refParams.singleLevelImage)
111         {
112             // Add miplevel count (1u) as parameter if we want to test anisotropic filtering on image that has a single mip level.
113             texture            = TestTexture2DSp(new pipeline::TestTexture2D(
114                 vk::mapVkFormat(VK_FORMAT_R8G8B8A8_UNORM), ANISOTROPY_TEST_RESOLUTION, ANISOTROPY_TEST_RESOLUTION, 1u));
115             const int gridSize = max(texture->getLevel(0, 0).getHeight() / 8, 1);
116             tcu::fillWithGrid(texture->getLevel(0, 0), gridSize, Vec4(0.0f, 0.0f, 0.0f, 1.0f), Vec4(1.0f));
117         }
118         else
119         {
120             texture = TestTexture2DSp(new pipeline::TestTexture2D(
121                 vk::mapVkFormat(VK_FORMAT_R8G8B8A8_UNORM), ANISOTROPY_TEST_RESOLUTION, ANISOTROPY_TEST_RESOLUTION));
122             for (int levelNdx = 0; levelNdx < m_refParams.maxLevel + 1; levelNdx++)
123             {
124                 const int gridSize = max(texture->getLevel(levelNdx, 0).getHeight() / 8, 1);
125                 tcu::fillWithGrid(texture->getLevel(levelNdx, 0), gridSize, Vec4(0.0f, 0.0f, 0.0f, 1.0f), Vec4(1.0f));
126             }
127         }
128 
129         renderer.setViewport(0.0f, 0.0f, static_cast<float>(ANISOTROPY_TEST_RESOLUTION),
130                              static_cast<float>(ANISOTROPY_TEST_RESOLUTION));
131         renderer.add2DTexture(texture, VK_IMAGE_ASPECT_COLOR_BIT);
132 
133         {
134             Surface renderedFrame(ANISOTROPY_TEST_RESOLUTION, ANISOTROPY_TEST_RESOLUTION);
135             Surface renderedAnisotropyFrame(ANISOTROPY_TEST_RESOLUTION, ANISOTROPY_TEST_RESOLUTION);
136             const float position[] = {-3.5f, -1.0f, 0.0f, 3.5f, -3.5f, +1.0f, 0.0f, 1.0f,
137                                       +3.5f, -1.0f, 0.0f, 3.5f, +3.5f, +1.0f, 0.0f, 1.0f};
138             vector<float> texCoord;
139 
140             computeQuadTexCoord2D(texCoord, Vec2(0.0f), Vec2(1.0f));
141 
142             renderer.renderQuad(renderedFrame, position, 0, &texCoord[0], m_refParams, 1.0f);
143             renderer.renderQuad(renderedAnisotropyFrame, position, 0, &texCoord[0], m_refParams,
144                                 m_refParams.maxAnisotropy);
145 
146             if (!tcu::fuzzyCompare(m_context.getTestContext().getLog(), "Expecting comparison to pass",
147                                    "Expecting comparison to pass", renderedFrame.getAccess(),
148                                    renderedAnisotropyFrame.getAccess(), 0.05f, tcu::COMPARE_LOG_RESULT))
149                 return tcu::TestStatus::fail("Fail");
150 
151             // Anisotropic filtering is implementation dependent. Expecting differences with minification/magnification filter set to NEAREST is too strict.
152             // The specification does not require that your aniso & bi-linear filtering are different even in LINEAR, but this check is 'generally' going
153             // to detect *some* difference and possibly be useful in catching issues where an implementation hasn't setup their filtering modes correctly.
154             if (m_refParams.minFilter != tcu::Sampler::NEAREST && m_refParams.magFilter != tcu::Sampler::NEAREST)
155             {
156                 if (floatThresholdCompare(m_context.getTestContext().getLog(), "Expecting comparison to fail",
157                                           "Expecting comparison to fail", renderedFrame.getAccess(),
158                                           renderedAnisotropyFrame.getAccess(), Vec4(0.02f), tcu::COMPARE_LOG_RESULT))
159                     return tcu::TestStatus::fail("Fail");
160             }
161         }
162         return tcu::TestStatus::pass("Pass");
163     }
164 
165 private:
166     AnisotropyParams m_refParams;
167 };
168 
169 class FilteringAnisotropyTests : public vkt::TestCase
170 {
171 public:
FilteringAnisotropyTests(tcu::TestContext & testCtx,const string & name,const AnisotropyParams & refParams)172     FilteringAnisotropyTests(tcu::TestContext &testCtx, const string &name, const AnisotropyParams &refParams)
173         : vkt::TestCase(testCtx, name)
174         , m_refParams(refParams)
175     {
176     }
177 
initPrograms(SourceCollections & programCollection) const178     void initPrograms(SourceCollections &programCollection) const
179     {
180         std::vector<util::Program> programs;
181         programs.push_back(util::PROGRAM_2D_FLOAT);
182         initializePrograms(programCollection, glu::PRECISION_HIGHP, programs);
183     }
184 
createInstance(Context & context) const185     TestInstance *createInstance(Context &context) const
186     {
187         return new FilteringAnisotropyInstance(context, m_refParams);
188     }
189 
checkSupport(Context & context) const190     virtual void checkSupport(Context &context) const
191     {
192         // Check device for anisotropic filtering support.
193         if (!context.getDeviceFeatures().samplerAnisotropy)
194             TCU_THROW(NotSupportedError,
195                       "Skipping anisotropic tests since the device does not support anisotropic filtering.");
196     }
197 
198 private:
199     const AnisotropyParams m_refParams;
200 };
201 
202 } // namespace
203 
createFilteringAnisotropyTests(tcu::TestContext & testCtx)204 tcu::TestCaseGroup *createFilteringAnisotropyTests(tcu::TestContext &testCtx)
205 {
206     de::MovePtr<tcu::TestCaseGroup> filteringAnisotropyTests(new tcu::TestCaseGroup(testCtx, "filtering_anisotropy"));
207     de::MovePtr<tcu::TestCaseGroup> basicTests(new tcu::TestCaseGroup(testCtx, "basic"));
208     de::MovePtr<tcu::TestCaseGroup> mipmapTests(new tcu::TestCaseGroup(testCtx, "mipmap"));
209     de::MovePtr<tcu::TestCaseGroup> singleLevelImageTests(new tcu::TestCaseGroup(testCtx, "single_level"));
210     const char *valueName[]     = {"anisotropy_2", "anisotropy_4", "anisotropy_8", "anisotropy_max"};
211     const float maxAnisotropy[] = {
212         2.0f, 4.0f, 8.0f,
213         10000.0f //too huge will be flated to max value
214     };
215     const char *magFilterName[]                 = {"nearest", "linear"};
216     const tcu::Sampler::FilterMode magFilters[] = {Sampler::NEAREST, Sampler::LINEAR};
217 
218     // Basic anisotrophy filtering tests.
219     {
220         const tcu::Sampler::FilterMode *minFilters = magFilters;
221         const char **minFilterName                 = magFilterName;
222 
223         for (int anisotropyNdx = 0; anisotropyNdx < DE_LENGTH_OF_ARRAY(maxAnisotropy); anisotropyNdx++)
224         {
225             // Filtering anisotropy tests
226             de::MovePtr<tcu::TestCaseGroup> levelAnisotropyGroups(
227                 new tcu::TestCaseGroup(testCtx, valueName[anisotropyNdx]));
228 
229             for (int minFilterNdx = 0; minFilterNdx < DE_LENGTH_OF_ARRAY(magFilters); minFilterNdx++)
230                 for (int magFilterNdx = 0; magFilterNdx < DE_LENGTH_OF_ARRAY(magFilters); magFilterNdx++)
231                 {
232                     AnisotropyParams refParams(TEXTURETYPE_2D, maxAnisotropy[anisotropyNdx], minFilters[minFilterNdx],
233                                                magFilters[magFilterNdx]);
234                     levelAnisotropyGroups->addChild(new FilteringAnisotropyTests(
235                         testCtx,
236                         "mag_" + string(magFilterName[magFilterNdx]) + "_min_" + string(minFilterName[minFilterNdx]),
237                         refParams));
238                 }
239             basicTests->addChild(levelAnisotropyGroups.release());
240         }
241         filteringAnisotropyTests->addChild(basicTests.release());
242     }
243 
244     // Same as basic tests but with single imagelevel.
245     {
246         const tcu::Sampler::FilterMode *minFilters = magFilters;
247         const char **minFilterName                 = magFilterName;
248 
249         for (int anisotropyNdx = 0; anisotropyNdx < DE_LENGTH_OF_ARRAY(maxAnisotropy); anisotropyNdx++)
250         {
251             de::MovePtr<tcu::TestCaseGroup> levelAnisotropyGroups(
252                 new tcu::TestCaseGroup(testCtx, valueName[anisotropyNdx]));
253 
254             for (int minFilterNdx = 0; minFilterNdx < DE_LENGTH_OF_ARRAY(magFilters); minFilterNdx++)
255                 for (int magFilterNdx = 0; magFilterNdx < DE_LENGTH_OF_ARRAY(magFilters); magFilterNdx++)
256                 {
257                     AnisotropyParams refParams(TEXTURETYPE_2D, maxAnisotropy[anisotropyNdx], minFilters[minFilterNdx],
258                                                magFilters[magFilterNdx], true);
259                     levelAnisotropyGroups->addChild(new FilteringAnisotropyTests(
260                         testCtx,
261                         "mag_" + string(magFilterName[magFilterNdx]) + "_min_" + string(minFilterName[minFilterNdx]),
262                         refParams));
263                 }
264             singleLevelImageTests->addChild(levelAnisotropyGroups.release());
265         }
266         filteringAnisotropyTests->addChild(singleLevelImageTests.release());
267     }
268 
269     {
270         const tcu::Sampler::FilterMode minFilters[] = {Sampler::NEAREST_MIPMAP_NEAREST, Sampler::NEAREST_MIPMAP_LINEAR,
271                                                        Sampler::LINEAR_MIPMAP_NEAREST, Sampler::LINEAR_MIPMAP_LINEAR};
272         const char *minFilterName[] = {"nearest_mipmap_nearest", "nearest_mipmap_linear", "linear_mipmap_nearest",
273                                        "linear_mipmap_linear"};
274 
275         for (int anisotropyNdx = 0; anisotropyNdx < DE_LENGTH_OF_ARRAY(maxAnisotropy); anisotropyNdx++)
276         {
277             de::MovePtr<tcu::TestCaseGroup> levelAnisotropyGroups(
278                 new tcu::TestCaseGroup(testCtx, valueName[anisotropyNdx]));
279 
280             for (int minFilterNdx = 0; minFilterNdx < DE_LENGTH_OF_ARRAY(minFilters); minFilterNdx++)
281                 for (int magFilterNdx = 0; magFilterNdx < DE_LENGTH_OF_ARRAY(magFilters); magFilterNdx++)
282                 {
283                     AnisotropyParams refParams(TEXTURETYPE_2D, maxAnisotropy[anisotropyNdx], minFilters[minFilterNdx],
284                                                magFilters[magFilterNdx], false, true);
285                     levelAnisotropyGroups->addChild(new FilteringAnisotropyTests(
286                         testCtx,
287                         "mag_" + string(magFilterName[magFilterNdx]) + "_min_" + string(minFilterName[minFilterNdx]),
288                         refParams));
289                 }
290             mipmapTests->addChild(levelAnisotropyGroups.release());
291         }
292         filteringAnisotropyTests->addChild(mipmapTests.release());
293     }
294 
295     return filteringAnisotropyTests.release();
296 }
297 
298 } // namespace texture
299 } // namespace vkt
300