xref: /aosp_15_r20/external/deqp/framework/common/tcuBilinearImageCompare.cpp (revision 35238bce31c2a825756842865a792f8cf7f89930)
1*35238bceSAndroid Build Coastguard Worker /*-------------------------------------------------------------------------
2*35238bceSAndroid Build Coastguard Worker  * drawElements Quality Program Tester Core
3*35238bceSAndroid Build Coastguard Worker  * ----------------------------------------
4*35238bceSAndroid Build Coastguard Worker  *
5*35238bceSAndroid Build Coastguard Worker  * Copyright 2014 The Android Open Source Project
6*35238bceSAndroid Build Coastguard Worker  *
7*35238bceSAndroid Build Coastguard Worker  * Licensed under the Apache License, Version 2.0 (the "License");
8*35238bceSAndroid Build Coastguard Worker  * you may not use this file except in compliance with the License.
9*35238bceSAndroid Build Coastguard Worker  * You may obtain a copy of the License at
10*35238bceSAndroid Build Coastguard Worker  *
11*35238bceSAndroid Build Coastguard Worker  *      http://www.apache.org/licenses/LICENSE-2.0
12*35238bceSAndroid Build Coastguard Worker  *
13*35238bceSAndroid Build Coastguard Worker  * Unless required by applicable law or agreed to in writing, software
14*35238bceSAndroid Build Coastguard Worker  * distributed under the License is distributed on an "AS IS" BASIS,
15*35238bceSAndroid Build Coastguard Worker  * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
16*35238bceSAndroid Build Coastguard Worker  * See the License for the specific language governing permissions and
17*35238bceSAndroid Build Coastguard Worker  * limitations under the License.
18*35238bceSAndroid Build Coastguard Worker  *
19*35238bceSAndroid Build Coastguard Worker  *//*!
20*35238bceSAndroid Build Coastguard Worker  * \file
21*35238bceSAndroid Build Coastguard Worker  * \brief Bilinear image comparison.
22*35238bceSAndroid Build Coastguard Worker  *//*--------------------------------------------------------------------*/
23*35238bceSAndroid Build Coastguard Worker 
24*35238bceSAndroid Build Coastguard Worker #include "tcuBilinearImageCompare.hpp"
25*35238bceSAndroid Build Coastguard Worker #include "tcuTexture.hpp"
26*35238bceSAndroid Build Coastguard Worker #include "tcuTextureUtil.hpp"
27*35238bceSAndroid Build Coastguard Worker #include "tcuRGBA.hpp"
28*35238bceSAndroid Build Coastguard Worker 
29*35238bceSAndroid Build Coastguard Worker namespace tcu
30*35238bceSAndroid Build Coastguard Worker {
31*35238bceSAndroid Build Coastguard Worker 
32*35238bceSAndroid Build Coastguard Worker namespace
33*35238bceSAndroid Build Coastguard Worker {
34*35238bceSAndroid Build Coastguard Worker 
35*35238bceSAndroid Build Coastguard Worker enum
36*35238bceSAndroid Build Coastguard Worker {
37*35238bceSAndroid Build Coastguard Worker     NUM_SUBPIXEL_BITS = 8 //!< Number of subpixel bits used when doing bilinear interpolation.
38*35238bceSAndroid Build Coastguard Worker };
39*35238bceSAndroid Build Coastguard Worker 
40*35238bceSAndroid Build Coastguard Worker // \note Algorithm assumes that colors are packed to 32-bit values as dictated by
41*35238bceSAndroid Build Coastguard Worker //         tcu::RGBA::*_SHIFT values.
42*35238bceSAndroid Build Coastguard Worker 
43*35238bceSAndroid Build Coastguard Worker template <int Channel>
getChannel(uint32_t color)44*35238bceSAndroid Build Coastguard Worker static inline uint8_t getChannel(uint32_t color)
45*35238bceSAndroid Build Coastguard Worker {
46*35238bceSAndroid Build Coastguard Worker     return (uint8_t)((color >> (Channel * 8)) & 0xff);
47*35238bceSAndroid Build Coastguard Worker }
48*35238bceSAndroid Build Coastguard Worker 
49*35238bceSAndroid Build Coastguard Worker #if (DE_ENDIANNESS == DE_LITTLE_ENDIAN)
readRGBA8Raw(const ConstPixelBufferAccess & src,uint32_t x,uint32_t y)50*35238bceSAndroid Build Coastguard Worker inline uint32_t readRGBA8Raw(const ConstPixelBufferAccess &src, uint32_t x, uint32_t y)
51*35238bceSAndroid Build Coastguard Worker {
52*35238bceSAndroid Build Coastguard Worker     return *(const uint32_t *)((const uint8_t *)src.getDataPtr() + y * src.getRowPitch() + x * 4);
53*35238bceSAndroid Build Coastguard Worker }
54*35238bceSAndroid Build Coastguard Worker #else
readRGBA8Raw(const ConstPixelBufferAccess & src,uint32_t x,uint32_t y)55*35238bceSAndroid Build Coastguard Worker inline uint32_t readRGBA8Raw(const ConstPixelBufferAccess &src, uint32_t x, uint32_t y)
56*35238bceSAndroid Build Coastguard Worker {
57*35238bceSAndroid Build Coastguard Worker     return deReverseBytes32(*(const uint32_t *)((const uint8_t *)src.getDataPtr() + y * src.getRowPitch() + x * 4));
58*35238bceSAndroid Build Coastguard Worker }
59*35238bceSAndroid Build Coastguard Worker #endif
60*35238bceSAndroid Build Coastguard Worker 
readRGBA8(const ConstPixelBufferAccess & src,uint32_t x,uint32_t y)61*35238bceSAndroid Build Coastguard Worker inline RGBA readRGBA8(const ConstPixelBufferAccess &src, uint32_t x, uint32_t y)
62*35238bceSAndroid Build Coastguard Worker {
63*35238bceSAndroid Build Coastguard Worker     uint32_t raw = readRGBA8Raw(src, x, y);
64*35238bceSAndroid Build Coastguard Worker     uint32_t res = 0;
65*35238bceSAndroid Build Coastguard Worker 
66*35238bceSAndroid Build Coastguard Worker     res |= getChannel<0>(raw) << RGBA::RED_SHIFT;
67*35238bceSAndroid Build Coastguard Worker     res |= getChannel<1>(raw) << RGBA::GREEN_SHIFT;
68*35238bceSAndroid Build Coastguard Worker     res |= getChannel<2>(raw) << RGBA::BLUE_SHIFT;
69*35238bceSAndroid Build Coastguard Worker     res |= getChannel<3>(raw) << RGBA::ALPHA_SHIFT;
70*35238bceSAndroid Build Coastguard Worker 
71*35238bceSAndroid Build Coastguard Worker     return RGBA(res);
72*35238bceSAndroid Build Coastguard Worker }
73*35238bceSAndroid Build Coastguard Worker 
interpolateChannel(uint32_t fx1,uint32_t fy1,uint8_t p00,uint8_t p01,uint8_t p10,uint8_t p11)74*35238bceSAndroid Build Coastguard Worker inline uint8_t interpolateChannel(uint32_t fx1, uint32_t fy1, uint8_t p00, uint8_t p01, uint8_t p10, uint8_t p11)
75*35238bceSAndroid Build Coastguard Worker {
76*35238bceSAndroid Build Coastguard Worker     const uint32_t fx0     = (1u << NUM_SUBPIXEL_BITS) - fx1;
77*35238bceSAndroid Build Coastguard Worker     const uint32_t fy0     = (1u << NUM_SUBPIXEL_BITS) - fy1;
78*35238bceSAndroid Build Coastguard Worker     const uint32_t half    = 1u << (NUM_SUBPIXEL_BITS * 2 - 1);
79*35238bceSAndroid Build Coastguard Worker     const uint32_t sum     = fx0 * fy0 * p00 + fx1 * fy0 * p10 + fx0 * fy1 * p01 + fx1 * fy1 * p11;
80*35238bceSAndroid Build Coastguard Worker     const uint32_t rounded = (sum + half) >> (NUM_SUBPIXEL_BITS * 2);
81*35238bceSAndroid Build Coastguard Worker 
82*35238bceSAndroid Build Coastguard Worker     DE_ASSERT(de::inRange<uint32_t>(rounded, 0, 0xff));
83*35238bceSAndroid Build Coastguard Worker     return (uint8_t)rounded;
84*35238bceSAndroid Build Coastguard Worker }
85*35238bceSAndroid Build Coastguard Worker 
bilinearSampleRGBA8(const ConstPixelBufferAccess & access,uint32_t u,uint32_t v)86*35238bceSAndroid Build Coastguard Worker RGBA bilinearSampleRGBA8(const ConstPixelBufferAccess &access, uint32_t u, uint32_t v)
87*35238bceSAndroid Build Coastguard Worker {
88*35238bceSAndroid Build Coastguard Worker     uint32_t x0 = u >> NUM_SUBPIXEL_BITS;
89*35238bceSAndroid Build Coastguard Worker     uint32_t y0 = v >> NUM_SUBPIXEL_BITS;
90*35238bceSAndroid Build Coastguard Worker     uint32_t x1 = x0 + 1; //de::min(x0+1, (uint32_t)(access.getWidth()-1));
91*35238bceSAndroid Build Coastguard Worker     uint32_t y1 = y0 + 1; //de::min(y0+1, (uint32_t)(access.getHeight()-1));
92*35238bceSAndroid Build Coastguard Worker 
93*35238bceSAndroid Build Coastguard Worker     DE_ASSERT(x1 < (uint32_t)access.getWidth());
94*35238bceSAndroid Build Coastguard Worker     DE_ASSERT(y1 < (uint32_t)access.getHeight());
95*35238bceSAndroid Build Coastguard Worker 
96*35238bceSAndroid Build Coastguard Worker     uint32_t fx1 = u - (x0 << NUM_SUBPIXEL_BITS);
97*35238bceSAndroid Build Coastguard Worker     uint32_t fy1 = v - (y0 << NUM_SUBPIXEL_BITS);
98*35238bceSAndroid Build Coastguard Worker 
99*35238bceSAndroid Build Coastguard Worker     uint32_t p00 = readRGBA8Raw(access, x0, y0);
100*35238bceSAndroid Build Coastguard Worker     uint32_t p10 = readRGBA8Raw(access, x1, y0);
101*35238bceSAndroid Build Coastguard Worker     uint32_t p01 = readRGBA8Raw(access, x0, y1);
102*35238bceSAndroid Build Coastguard Worker     uint32_t p11 = readRGBA8Raw(access, x1, y1);
103*35238bceSAndroid Build Coastguard Worker 
104*35238bceSAndroid Build Coastguard Worker     uint32_t res = 0;
105*35238bceSAndroid Build Coastguard Worker 
106*35238bceSAndroid Build Coastguard Worker     res |= interpolateChannel(fx1, fy1, getChannel<0>(p00), getChannel<0>(p01), getChannel<0>(p10), getChannel<0>(p11))
107*35238bceSAndroid Build Coastguard Worker            << RGBA::RED_SHIFT;
108*35238bceSAndroid Build Coastguard Worker     res |= interpolateChannel(fx1, fy1, getChannel<1>(p00), getChannel<1>(p01), getChannel<1>(p10), getChannel<1>(p11))
109*35238bceSAndroid Build Coastguard Worker            << RGBA::GREEN_SHIFT;
110*35238bceSAndroid Build Coastguard Worker     res |= interpolateChannel(fx1, fy1, getChannel<2>(p00), getChannel<2>(p01), getChannel<2>(p10), getChannel<2>(p11))
111*35238bceSAndroid Build Coastguard Worker            << RGBA::BLUE_SHIFT;
112*35238bceSAndroid Build Coastguard Worker     res |= interpolateChannel(fx1, fy1, getChannel<3>(p00), getChannel<3>(p01), getChannel<3>(p10), getChannel<3>(p11))
113*35238bceSAndroid Build Coastguard Worker            << RGBA::ALPHA_SHIFT;
114*35238bceSAndroid Build Coastguard Worker 
115*35238bceSAndroid Build Coastguard Worker     return RGBA(res);
116*35238bceSAndroid Build Coastguard Worker }
117*35238bceSAndroid Build Coastguard Worker 
comparePixelRGBA8(const ConstPixelBufferAccess & reference,const ConstPixelBufferAccess & result,const RGBA threshold,int x,int y)118*35238bceSAndroid Build Coastguard Worker bool comparePixelRGBA8(const ConstPixelBufferAccess &reference, const ConstPixelBufferAccess &result,
119*35238bceSAndroid Build Coastguard Worker                        const RGBA threshold, int x, int y)
120*35238bceSAndroid Build Coastguard Worker {
121*35238bceSAndroid Build Coastguard Worker     const RGBA resPix = readRGBA8(result, (uint32_t)x, (uint32_t)y);
122*35238bceSAndroid Build Coastguard Worker 
123*35238bceSAndroid Build Coastguard Worker     // Step 1: Compare result pixel to 3x3 neighborhood pixels in reference.
124*35238bceSAndroid Build Coastguard Worker     {
125*35238bceSAndroid Build Coastguard Worker         const uint32_t x0 = (uint32_t)de::max(x - 1, 0);
126*35238bceSAndroid Build Coastguard Worker         const uint32_t x1 = (uint32_t)x;
127*35238bceSAndroid Build Coastguard Worker         const uint32_t x2 = (uint32_t)de::min(x + 1, reference.getWidth() - 1);
128*35238bceSAndroid Build Coastguard Worker         const uint32_t y0 = (uint32_t)de::max(y - 1, 0);
129*35238bceSAndroid Build Coastguard Worker         const uint32_t y1 = (uint32_t)y;
130*35238bceSAndroid Build Coastguard Worker         const uint32_t y2 = (uint32_t)de::min(y + 1, reference.getHeight() - 1);
131*35238bceSAndroid Build Coastguard Worker 
132*35238bceSAndroid Build Coastguard Worker         if (compareThreshold(resPix, readRGBA8(reference, x1, y1), threshold) ||
133*35238bceSAndroid Build Coastguard Worker             compareThreshold(resPix, readRGBA8(reference, x0, y1), threshold) ||
134*35238bceSAndroid Build Coastguard Worker             compareThreshold(resPix, readRGBA8(reference, x2, y1), threshold) ||
135*35238bceSAndroid Build Coastguard Worker             compareThreshold(resPix, readRGBA8(reference, x0, y0), threshold) ||
136*35238bceSAndroid Build Coastguard Worker             compareThreshold(resPix, readRGBA8(reference, x1, y0), threshold) ||
137*35238bceSAndroid Build Coastguard Worker             compareThreshold(resPix, readRGBA8(reference, x2, y0), threshold) ||
138*35238bceSAndroid Build Coastguard Worker             compareThreshold(resPix, readRGBA8(reference, x0, y2), threshold) ||
139*35238bceSAndroid Build Coastguard Worker             compareThreshold(resPix, readRGBA8(reference, x1, y2), threshold) ||
140*35238bceSAndroid Build Coastguard Worker             compareThreshold(resPix, readRGBA8(reference, x2, y2), threshold))
141*35238bceSAndroid Build Coastguard Worker             return true;
142*35238bceSAndroid Build Coastguard Worker     }
143*35238bceSAndroid Build Coastguard Worker 
144*35238bceSAndroid Build Coastguard Worker     // Step 2: Compare using bilinear sampling.
145*35238bceSAndroid Build Coastguard Worker     {
146*35238bceSAndroid Build Coastguard Worker         // \todo [pyry] Optimize sample positions!
147*35238bceSAndroid Build Coastguard Worker         static const uint32_t s_offsets[][2] = {{226, 186}, {335, 235}, {279, 334}, {178, 272}, {112, 202}, {306, 117},
148*35238bceSAndroid Build Coastguard Worker                                                 {396, 299}, {206, 382}, {146, 96},  {423, 155}, {361, 412}, {84, 339},
149*35238bceSAndroid Build Coastguard Worker                                                 {48, 130},  {367, 43},  {455, 367}, {105, 439}, {83, 46},   {217, 24},
150*35238bceSAndroid Build Coastguard Worker                                                 {461, 71},  {450, 459}, {239, 469}, {67, 267},  {459, 255}, {13, 416},
151*35238bceSAndroid Build Coastguard Worker                                                 {10, 192},  {141, 502}, {503, 304}, {380, 506}};
152*35238bceSAndroid Build Coastguard Worker 
153*35238bceSAndroid Build Coastguard Worker         for (int sampleNdx = 0; sampleNdx < DE_LENGTH_OF_ARRAY(s_offsets); sampleNdx++)
154*35238bceSAndroid Build Coastguard Worker         {
155*35238bceSAndroid Build Coastguard Worker             const int u = (x << NUM_SUBPIXEL_BITS) + (int)s_offsets[sampleNdx][0] - (1 << NUM_SUBPIXEL_BITS);
156*35238bceSAndroid Build Coastguard Worker             const int v = (y << NUM_SUBPIXEL_BITS) + (int)s_offsets[sampleNdx][1] - (1 << NUM_SUBPIXEL_BITS);
157*35238bceSAndroid Build Coastguard Worker 
158*35238bceSAndroid Build Coastguard Worker             if (!de::inBounds(u, 0, (reference.getWidth() - 1) << NUM_SUBPIXEL_BITS) ||
159*35238bceSAndroid Build Coastguard Worker                 !de::inBounds(v, 0, (reference.getHeight() - 1) << NUM_SUBPIXEL_BITS))
160*35238bceSAndroid Build Coastguard Worker                 continue;
161*35238bceSAndroid Build Coastguard Worker 
162*35238bceSAndroid Build Coastguard Worker             if (compareThreshold(resPix, bilinearSampleRGBA8(reference, (uint32_t)u, (uint32_t)v), threshold))
163*35238bceSAndroid Build Coastguard Worker                 return true;
164*35238bceSAndroid Build Coastguard Worker         }
165*35238bceSAndroid Build Coastguard Worker     }
166*35238bceSAndroid Build Coastguard Worker 
167*35238bceSAndroid Build Coastguard Worker     return false;
168*35238bceSAndroid Build Coastguard Worker }
169*35238bceSAndroid Build Coastguard Worker 
bilinearCompareRGBA8(const ConstPixelBufferAccess & reference,const ConstPixelBufferAccess & result,const PixelBufferAccess & errorMask,const RGBA threshold)170*35238bceSAndroid Build Coastguard Worker bool bilinearCompareRGBA8(const ConstPixelBufferAccess &reference, const ConstPixelBufferAccess &result,
171*35238bceSAndroid Build Coastguard Worker                           const PixelBufferAccess &errorMask, const RGBA threshold)
172*35238bceSAndroid Build Coastguard Worker {
173*35238bceSAndroid Build Coastguard Worker     DE_ASSERT(reference.getFormat() == TextureFormat(TextureFormat::RGBA, TextureFormat::UNORM_INT8) &&
174*35238bceSAndroid Build Coastguard Worker               result.getFormat() == TextureFormat(TextureFormat::RGBA, TextureFormat::UNORM_INT8));
175*35238bceSAndroid Build Coastguard Worker 
176*35238bceSAndroid Build Coastguard Worker     // Clear error mask first to green (faster this way).
177*35238bceSAndroid Build Coastguard Worker     clear(errorMask, Vec4(0.0f, 1.0f, 0.0f, 1.0f));
178*35238bceSAndroid Build Coastguard Worker 
179*35238bceSAndroid Build Coastguard Worker     bool allOk = true;
180*35238bceSAndroid Build Coastguard Worker 
181*35238bceSAndroid Build Coastguard Worker     for (int y = 0; y < reference.getHeight(); y++)
182*35238bceSAndroid Build Coastguard Worker     {
183*35238bceSAndroid Build Coastguard Worker         for (int x = 0; x < reference.getWidth(); x++)
184*35238bceSAndroid Build Coastguard Worker         {
185*35238bceSAndroid Build Coastguard Worker             if (!comparePixelRGBA8(reference, result, threshold, x, y) &&
186*35238bceSAndroid Build Coastguard Worker                 !comparePixelRGBA8(result, reference, threshold, x, y))
187*35238bceSAndroid Build Coastguard Worker             {
188*35238bceSAndroid Build Coastguard Worker                 allOk = false;
189*35238bceSAndroid Build Coastguard Worker                 errorMask.setPixel(Vec4(1.0f, 0.0f, 0.0f, 1.0f), x, y);
190*35238bceSAndroid Build Coastguard Worker             }
191*35238bceSAndroid Build Coastguard Worker         }
192*35238bceSAndroid Build Coastguard Worker     }
193*35238bceSAndroid Build Coastguard Worker 
194*35238bceSAndroid Build Coastguard Worker     return allOk;
195*35238bceSAndroid Build Coastguard Worker }
196*35238bceSAndroid Build Coastguard Worker 
197*35238bceSAndroid Build Coastguard Worker } // namespace
198*35238bceSAndroid Build Coastguard Worker 
bilinearCompare(const ConstPixelBufferAccess & reference,const ConstPixelBufferAccess & result,const PixelBufferAccess & errorMask,const RGBA threshold)199*35238bceSAndroid Build Coastguard Worker bool bilinearCompare(const ConstPixelBufferAccess &reference, const ConstPixelBufferAccess &result,
200*35238bceSAndroid Build Coastguard Worker                      const PixelBufferAccess &errorMask, const RGBA threshold)
201*35238bceSAndroid Build Coastguard Worker {
202*35238bceSAndroid Build Coastguard Worker     DE_ASSERT(reference.getWidth() == result.getWidth() && reference.getHeight() == result.getHeight() &&
203*35238bceSAndroid Build Coastguard Worker               reference.getDepth() == result.getDepth() && reference.getFormat() == result.getFormat());
204*35238bceSAndroid Build Coastguard Worker     DE_ASSERT(reference.getWidth() == errorMask.getWidth() && reference.getHeight() == errorMask.getHeight() &&
205*35238bceSAndroid Build Coastguard Worker               reference.getDepth() == errorMask.getDepth());
206*35238bceSAndroid Build Coastguard Worker 
207*35238bceSAndroid Build Coastguard Worker     if (reference.getFormat() == TextureFormat(TextureFormat::RGBA, TextureFormat::UNORM_INT8))
208*35238bceSAndroid Build Coastguard Worker         return bilinearCompareRGBA8(reference, result, errorMask, threshold);
209*35238bceSAndroid Build Coastguard Worker     else
210*35238bceSAndroid Build Coastguard Worker         throw InternalError("Unsupported format for bilinear comparison");
211*35238bceSAndroid Build Coastguard Worker }
212*35238bceSAndroid Build Coastguard Worker 
213*35238bceSAndroid Build Coastguard Worker } // namespace tcu
214