xref: /aosp_15_r20/external/deqp/modules/gles2/functional/es2fRandomFragmentOpTests.cpp (revision 35238bce31c2a825756842865a792f8cf7f89930)
1 /*-------------------------------------------------------------------------
2  * drawElements Quality Program OpenGL ES 3.0 Module
3  * -------------------------------------------------
4  *
5  * Copyright 2014 The Android Open Source Project
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 Randomized per-fragment operation tests.
22  *//*--------------------------------------------------------------------*/
23 
24 #include "es2fRandomFragmentOpTests.hpp"
25 #include "glsFragmentOpUtil.hpp"
26 #include "glsInteractionTestUtil.hpp"
27 #include "tcuRenderTarget.hpp"
28 #include "tcuTestLog.hpp"
29 #include "tcuSurface.hpp"
30 #include "tcuCommandLine.hpp"
31 #include "tcuImageCompare.hpp"
32 #include "tcuVectorUtil.hpp"
33 #include "tcuTextureUtil.hpp"
34 #include "gluPixelTransfer.hpp"
35 #include "gluCallLogWrapper.hpp"
36 #include "gluRenderContext.hpp"
37 #include "deStringUtil.hpp"
38 #include "deRandom.hpp"
39 #include "deMath.h"
40 #include "glwFunctions.hpp"
41 #include "glwEnums.hpp"
42 #include "rrFragmentOperations.hpp"
43 #include "sglrReferenceUtils.hpp"
44 
45 #include <algorithm>
46 
47 namespace deqp
48 {
49 namespace gles2
50 {
51 namespace Functional
52 {
53 
54 using std::vector;
55 using tcu::BVec4;
56 using tcu::IVec2;
57 using tcu::TestLog;
58 using tcu::Vec2;
59 using tcu::Vec4;
60 
61 enum
62 {
63     VIEWPORT_WIDTH          = 64,
64     VIEWPORT_HEIGHT         = 64,
65     NUM_CALLS_PER_ITERATION = 3,
66     NUM_ITERATIONS_PER_CASE = 10
67 };
68 
69 static const tcu::Vec4 CLEAR_COLOR(0.25f, 0.5f, 0.75f, 1.0f);
70 static const float CLEAR_DEPTH    = 1.0f;
71 static const int CLEAR_STENCIL    = 0;
72 static const bool ENABLE_CALL_LOG = true;
73 
74 using namespace gls::FragmentOpUtil;
75 using namespace gls::InteractionTestUtil;
76 
translateStencilState(const StencilState & src,rr::StencilState & dst)77 void translateStencilState(const StencilState &src, rr::StencilState &dst)
78 {
79     dst.func      = sglr::rr_util::mapGLTestFunc(src.function);
80     dst.ref       = src.reference;
81     dst.compMask  = src.compareMask;
82     dst.sFail     = sglr::rr_util::mapGLStencilOp(src.stencilFailOp);
83     dst.dpFail    = sglr::rr_util::mapGLStencilOp(src.depthFailOp);
84     dst.dpPass    = sglr::rr_util::mapGLStencilOp(src.depthPassOp);
85     dst.writeMask = src.writeMask;
86 }
87 
translateBlendState(const BlendState & src,rr::BlendState & dst)88 void translateBlendState(const BlendState &src, rr::BlendState &dst)
89 {
90     dst.equation = sglr::rr_util::mapGLBlendEquation(src.equation);
91     dst.srcFunc  = sglr::rr_util::mapGLBlendFunc(src.srcFunc);
92     dst.dstFunc  = sglr::rr_util::mapGLBlendFunc(src.dstFunc);
93 }
94 
translateState(const RenderState & src,rr::FragmentOperationState & dst,const tcu::RenderTarget & renderTarget)95 void translateState(const RenderState &src, rr::FragmentOperationState &dst, const tcu::RenderTarget &renderTarget)
96 {
97     bool hasDepth   = renderTarget.getDepthBits() > 0;
98     bool hasStencil = renderTarget.getStencilBits() > 0;
99 
100     dst.scissorTestEnabled = src.scissorTestEnabled;
101     dst.scissorRectangle   = src.scissorRectangle;
102     dst.stencilTestEnabled = hasStencil && src.stencilTestEnabled;
103     dst.depthTestEnabled   = hasDepth && src.depthTestEnabled;
104     dst.blendMode          = src.blendEnabled ? rr::BLENDMODE_STANDARD : rr::BLENDMODE_NONE;
105     dst.numStencilBits     = renderTarget.getStencilBits();
106 
107     dst.colorMask = src.colorMask;
108 
109     if (dst.depthTestEnabled)
110     {
111         dst.depthFunc = sglr::rr_util::mapGLTestFunc(src.depthFunc);
112         dst.depthMask = src.depthWriteMask;
113     }
114 
115     if (dst.stencilTestEnabled)
116     {
117         translateStencilState(src.stencil[rr::FACETYPE_BACK], dst.stencilStates[rr::FACETYPE_BACK]);
118         translateStencilState(src.stencil[rr::FACETYPE_FRONT], dst.stencilStates[rr::FACETYPE_FRONT]);
119     }
120 
121     if (src.blendEnabled)
122     {
123         translateBlendState(src.blendRGBState, dst.blendRGBState);
124         translateBlendState(src.blendAState, dst.blendAState);
125         dst.blendColor = tcu::clamp(src.blendColor, Vec4(0.0f), Vec4(1.0f));
126     }
127 }
128 
renderQuad(const glw::Functions & gl,gls::FragmentOpUtil::QuadRenderer & renderer,const gls::FragmentOpUtil::IntegerQuad & quad,int baseX,int baseY)129 static void renderQuad(const glw::Functions &gl, gls::FragmentOpUtil::QuadRenderer &renderer,
130                        const gls::FragmentOpUtil::IntegerQuad &quad, int baseX, int baseY)
131 {
132     gls::FragmentOpUtil::Quad translated;
133 
134     std::copy(DE_ARRAY_BEGIN(quad.color), DE_ARRAY_END(quad.color), DE_ARRAY_BEGIN(translated.color));
135 
136     bool flipX    = quad.posB.x() < quad.posA.x();
137     bool flipY    = quad.posB.y() < quad.posA.y();
138     int viewportX = de::min(quad.posA.x(), quad.posB.x());
139     int viewportY = de::min(quad.posA.y(), quad.posB.y());
140     int viewportW = de::abs(quad.posA.x() - quad.posB.x()) + 1;
141     int viewportH = de::abs(quad.posA.y() - quad.posB.y()) + 1;
142 
143     translated.posA = Vec2(flipX ? 1.0f : -1.0f, flipY ? 1.0f : -1.0f);
144     translated.posB = Vec2(flipX ? -1.0f : 1.0f, flipY ? -1.0f : 1.0f);
145 
146     // \todo [2012-12-18 pyry] Pass in DepthRange parameters.
147     for (int ndx = 0; ndx < DE_LENGTH_OF_ARRAY(quad.depth); ndx++)
148         translated.depth[ndx] = quad.depth[ndx] * 2.0f - 1.0f;
149 
150     gl.viewport(baseX + viewportX, baseY + viewportY, viewportW, viewportH);
151     renderer.render(translated);
152 }
153 
setGLState(glu::CallLogWrapper & wrapper,const RenderState & state,int viewportX,int viewportY)154 static void setGLState(glu::CallLogWrapper &wrapper, const RenderState &state, int viewportX, int viewportY)
155 {
156     if (state.scissorTestEnabled)
157     {
158         wrapper.glEnable(GL_SCISSOR_TEST);
159         wrapper.glScissor(viewportX + state.scissorRectangle.left, viewportY + state.scissorRectangle.bottom,
160                           state.scissorRectangle.width, state.scissorRectangle.height);
161     }
162     else
163         wrapper.glDisable(GL_SCISSOR_TEST);
164 
165     if (state.stencilTestEnabled)
166     {
167         wrapper.glEnable(GL_STENCIL_TEST);
168 
169         for (int face = 0; face < rr::FACETYPE_LAST; face++)
170         {
171             uint32_t glFace             = face == rr::FACETYPE_BACK ? GL_BACK : GL_FRONT;
172             const StencilState &sParams = state.stencil[face];
173 
174             wrapper.glStencilFuncSeparate(glFace, sParams.function, sParams.reference, sParams.compareMask);
175             wrapper.glStencilOpSeparate(glFace, sParams.stencilFailOp, sParams.depthFailOp, sParams.depthPassOp);
176             wrapper.glStencilMaskSeparate(glFace, sParams.writeMask);
177         }
178     }
179     else
180         wrapper.glDisable(GL_STENCIL_TEST);
181 
182     if (state.depthTestEnabled)
183     {
184         wrapper.glEnable(GL_DEPTH_TEST);
185         wrapper.glDepthFunc(state.depthFunc);
186         wrapper.glDepthMask(state.depthWriteMask ? GL_TRUE : GL_FALSE);
187     }
188     else
189         wrapper.glDisable(GL_DEPTH_TEST);
190 
191     if (state.blendEnabled)
192     {
193         wrapper.glEnable(GL_BLEND);
194         wrapper.glBlendEquationSeparate(state.blendRGBState.equation, state.blendAState.equation);
195         wrapper.glBlendFuncSeparate(state.blendRGBState.srcFunc, state.blendRGBState.dstFunc, state.blendAState.srcFunc,
196                                     state.blendAState.dstFunc);
197         wrapper.glBlendColor(state.blendColor.x(), state.blendColor.y(), state.blendColor.z(), state.blendColor.w());
198     }
199     else
200         wrapper.glDisable(GL_BLEND);
201 
202     if (state.ditherEnabled)
203         wrapper.glEnable(GL_DITHER);
204     else
205         wrapper.glDisable(GL_DITHER);
206 
207     wrapper.glColorMask(state.colorMask[0] ? GL_TRUE : GL_FALSE, state.colorMask[1] ? GL_TRUE : GL_FALSE,
208                         state.colorMask[2] ? GL_TRUE : GL_FALSE, state.colorMask[3] ? GL_TRUE : GL_FALSE);
209 }
210 
211 class RandomFragmentOpCase : public TestCase
212 {
213 public:
214     RandomFragmentOpCase(Context &context, const char *name, const char *desc, uint32_t seed);
215     ~RandomFragmentOpCase(void);
216 
217     void init(void);
218     void deinit(void);
219     IterateResult iterate(void);
220 
221 private:
222     tcu::UVec4 getCompareThreshold(void) const;
223 
224     uint32_t m_seed;
225 
226     glu::CallLogWrapper m_callLogWrapper;
227 
228     gls::FragmentOpUtil::QuadRenderer *m_renderer;
229     tcu::TextureLevel *m_refColorBuffer;
230     tcu::TextureLevel *m_refDepthBuffer;
231     tcu::TextureLevel *m_refStencilBuffer;
232     gls::FragmentOpUtil::ReferenceQuadRenderer *m_refRenderer;
233 
234     int m_iterNdx;
235 };
236 
RandomFragmentOpCase(Context & context,const char * name,const char * desc,uint32_t seed)237 RandomFragmentOpCase::RandomFragmentOpCase(Context &context, const char *name, const char *desc, uint32_t seed)
238     : TestCase(context, name, desc)
239     , m_seed(seed)
240     , m_callLogWrapper(context.getRenderContext().getFunctions(), context.getTestContext().getLog())
241     , m_renderer(DE_NULL)
242     , m_refColorBuffer(DE_NULL)
243     , m_refDepthBuffer(DE_NULL)
244     , m_refStencilBuffer(DE_NULL)
245     , m_refRenderer(DE_NULL)
246     , m_iterNdx(0)
247 {
248     m_callLogWrapper.enableLogging(ENABLE_CALL_LOG);
249 }
250 
~RandomFragmentOpCase(void)251 RandomFragmentOpCase::~RandomFragmentOpCase(void)
252 {
253     delete m_renderer;
254     delete m_refColorBuffer;
255     delete m_refDepthBuffer;
256     delete m_refStencilBuffer;
257     delete m_refRenderer;
258 }
259 
init(void)260 void RandomFragmentOpCase::init(void)
261 {
262     DE_ASSERT(!m_renderer && !m_refColorBuffer && !m_refDepthBuffer && !m_refStencilBuffer && !m_refRenderer);
263 
264     int width   = de::min<int>(m_context.getRenderTarget().getWidth(), VIEWPORT_WIDTH);
265     int height  = de::min<int>(m_context.getRenderTarget().getHeight(), VIEWPORT_HEIGHT);
266     bool useRGB = m_context.getRenderTarget().getPixelFormat().alphaBits == 0;
267 
268     m_renderer       = new gls::FragmentOpUtil::QuadRenderer(m_context.getRenderContext(), glu::GLSL_VERSION_100_ES);
269     m_refColorBuffer = new tcu::TextureLevel(
270         tcu::TextureFormat(useRGB ? tcu::TextureFormat::RGB : tcu::TextureFormat::RGBA, tcu::TextureFormat::UNORM_INT8),
271         width, height);
272     m_refDepthBuffer =
273         new tcu::TextureLevel(tcu::TextureFormat(tcu::TextureFormat::D, tcu::TextureFormat::FLOAT), width, height);
274     m_refStencilBuffer = new tcu::TextureLevel(
275         tcu::TextureFormat(tcu::TextureFormat::S, tcu::TextureFormat::UNSIGNED_INT32), width, height);
276     m_refRenderer = new gls::FragmentOpUtil::ReferenceQuadRenderer();
277     m_iterNdx     = 0;
278 
279     m_testCtx.setTestResult(QP_TEST_RESULT_PASS, "Pass");
280 }
281 
deinit(void)282 void RandomFragmentOpCase::deinit(void)
283 {
284     delete m_renderer;
285     delete m_refColorBuffer;
286     delete m_refDepthBuffer;
287     delete m_refStencilBuffer;
288     delete m_refRenderer;
289 
290     m_renderer         = DE_NULL;
291     m_refColorBuffer   = DE_NULL;
292     m_refDepthBuffer   = DE_NULL;
293     m_refStencilBuffer = DE_NULL;
294     m_refRenderer      = DE_NULL;
295 }
296 
iterate(void)297 RandomFragmentOpCase::IterateResult RandomFragmentOpCase::iterate(void)
298 {
299     const glw::Functions &gl = m_context.getRenderContext().getFunctions();
300     const bool isMSAA        = m_context.getRenderTarget().getNumSamples() > 1;
301     const uint32_t iterSeed =
302         deUint32Hash(m_seed) ^ deInt32Hash(m_iterNdx) ^ deInt32Hash(m_testCtx.getCommandLine().getBaseSeed());
303     de::Random rnd(iterSeed);
304 
305     const int width     = m_refColorBuffer->getWidth();
306     const int height    = m_refColorBuffer->getHeight();
307     const int viewportX = rnd.getInt(0, m_context.getRenderTarget().getWidth() - width);
308     const int viewportY = rnd.getInt(0, m_context.getRenderTarget().getHeight() - height);
309 
310     tcu::Surface renderedImg(width, height);
311     tcu::Surface referenceImg(width, height);
312 
313     const Vec4 clearColor  = CLEAR_COLOR;
314     const float clearDepth = CLEAR_DEPTH;
315     const int clearStencil = CLEAR_STENCIL;
316 
317     bool gotError = false;
318 
319     const tcu::ScopedLogSection iterSection(m_testCtx.getLog(), std::string("Iteration") + de::toString(m_iterNdx),
320                                             std::string("Iteration ") + de::toString(m_iterNdx));
321 
322     // Compute randomized rendering commands.
323     vector<RenderCommand> commands;
324     computeRandomRenderCommands(rnd, glu::ApiType::es(2, 0), NUM_CALLS_PER_ITERATION, width, height, commands);
325 
326     // Reset default fragment state.
327     gl.disable(GL_SCISSOR_TEST);
328     gl.colorMask(GL_TRUE, GL_TRUE, GL_TRUE, GL_TRUE);
329     gl.depthMask(GL_TRUE);
330     gl.stencilMask(~0u);
331 
332     // Render using GL.
333     m_callLogWrapper.glClearColor(clearColor.x(), clearColor.y(), clearColor.z(), clearColor.w());
334     m_callLogWrapper.glClearDepthf(clearDepth);
335     m_callLogWrapper.glClearStencil(clearStencil);
336     m_callLogWrapper.glClear(GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT | GL_STENCIL_BUFFER_BIT);
337     m_callLogWrapper.glViewport(viewportX, viewportY, width, height);
338 
339     for (vector<RenderCommand>::const_iterator cmd = commands.begin(); cmd != commands.end(); cmd++)
340     {
341         setGLState(m_callLogWrapper, cmd->state, viewportX, viewportY);
342 
343         if (ENABLE_CALL_LOG)
344             m_testCtx.getLog() << TestLog::Message << "// Quad: " << cmd->quad.posA << " -> " << cmd->quad.posB
345                                << ", color: [" << cmd->quad.color[0] << ", " << cmd->quad.color[1] << ", "
346                                << cmd->quad.color[2] << ", " << cmd->quad.color[3] << "]"
347                                << ", depth: [" << cmd->quad.depth[0] << ", " << cmd->quad.depth[1] << ", "
348                                << cmd->quad.depth[2] << ", " << cmd->quad.depth[3] << "]" << TestLog::EndMessage;
349 
350         renderQuad(gl, *m_renderer, cmd->quad, viewportX, viewportY);
351     }
352 
353     // Check error.
354     if (m_callLogWrapper.glGetError() != GL_NO_ERROR)
355         gotError = true;
356 
357     gl.flush();
358 
359     // Render reference while GPU is doing work.
360     tcu::clear(m_refColorBuffer->getAccess(), clearColor);
361     tcu::clearDepth(m_refDepthBuffer->getAccess(), clearDepth);
362     tcu::clearStencil(m_refStencilBuffer->getAccess(), clearStencil);
363 
364     for (vector<RenderCommand>::const_iterator cmd = commands.begin(); cmd != commands.end(); cmd++)
365     {
366         rr::FragmentOperationState refState;
367         translateState(cmd->state, refState, m_context.getRenderTarget());
368         m_refRenderer->render(gls::FragmentOpUtil::getMultisampleAccess(m_refColorBuffer->getAccess()),
369                               gls::FragmentOpUtil::getMultisampleAccess(m_refDepthBuffer->getAccess()),
370                               gls::FragmentOpUtil::getMultisampleAccess(m_refStencilBuffer->getAccess()), cmd->quad,
371                               refState);
372     }
373 
374     // Expand reference color buffer to RGBA8
375     copy(referenceImg.getAccess(), m_refColorBuffer->getAccess());
376 
377     // Read rendered image.
378     glu::readPixels(m_context.getRenderContext(), viewportX, viewportY, renderedImg.getAccess());
379 
380     m_iterNdx += 1;
381 
382     // Compare to reference.
383     bool isLastIter            = m_iterNdx >= NUM_ITERATIONS_PER_CASE;
384     const tcu::UVec4 threshold = getCompareThreshold();
385     bool compareOk;
386 
387     if (isMSAA)
388     {
389         // in MSAA cases, the sampling points could be anywhere in the pixel and we could
390         // even have multiple samples that are combined in resolve. Allow arbitrary sample
391         // positions by using bilinearCompare.
392         compareOk = tcu::bilinearCompare(m_testCtx.getLog(), "CompareResult", "Image Comparison Result",
393                                          referenceImg.getAccess(), renderedImg.getAccess(),
394                                          tcu::RGBA(threshold.x(), threshold.y(), threshold.z(), threshold.w()),
395                                          tcu::COMPARE_LOG_RESULT);
396     }
397     else
398         compareOk = tcu::intThresholdCompare(m_testCtx.getLog(), "CompareResult", "Image Comparison Result",
399                                              referenceImg.getAccess(), renderedImg.getAccess(), threshold,
400                                              tcu::COMPARE_LOG_RESULT);
401 
402     m_testCtx.getLog() << TestLog::Message << (compareOk ? "  Passed." : "  FAILED!") << TestLog::EndMessage;
403 
404     if (!compareOk)
405         m_testCtx.setTestResult(QP_TEST_RESULT_FAIL, "Image comparison failed");
406     else if (gotError)
407         m_testCtx.setTestResult(QP_TEST_RESULT_FAIL, "GL error");
408 
409     if (compareOk && !gotError && !isLastIter)
410         return CONTINUE;
411     else
412         return STOP;
413 }
414 
getCompareThreshold(void) const415 tcu::UVec4 RandomFragmentOpCase::getCompareThreshold(void) const
416 {
417     tcu::PixelFormat format = m_context.getRenderTarget().getPixelFormat();
418 
419     if (format == tcu::PixelFormat(8, 8, 8, 8) || format == tcu::PixelFormat(8, 8, 8, 0))
420         return format.getColorThreshold().toIVec().asUint() + tcu::UVec4(6); // Default threshold.
421     else
422         return format.getColorThreshold().toIVec().asUint() * tcu::UVec4(5) +
423                tcu::UVec4(
424                    2); // \note Non-scientific ad hoc formula. Need big threshold when few color bits; especially multiple blendings bring extra inaccuracy.
425 }
426 
RandomFragmentOpTests(Context & context)427 RandomFragmentOpTests::RandomFragmentOpTests(Context &context)
428     : TestCaseGroup(context, "random", "Randomized Per-Fragment Operation Tests")
429 {
430 }
431 
~RandomFragmentOpTests(void)432 RandomFragmentOpTests::~RandomFragmentOpTests(void)
433 {
434 }
435 
init(void)436 void RandomFragmentOpTests::init(void)
437 {
438     for (int ndx = 0; ndx < 100; ndx++)
439         addChild(new RandomFragmentOpCase(m_context, de::toString(ndx).c_str(), "",
440                                           (uint32_t)(ndx * NUM_ITERATIONS_PER_CASE)));
441 }
442 
443 } // namespace Functional
444 } // namespace gles2
445 } // namespace deqp
446