/*------------------------------------------------------------------------- * drawElements Quality Program OpenGL ES 3.1 Module * ------------------------------------------------- * * Copyright 2014 The Android Open Source Project * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. * *//*! * \file * \brief SSBO array length tests. *//*--------------------------------------------------------------------*/ #include "es31fSSBOArrayLengthTests.hpp" #include "gluShaderProgram.hpp" #include "gluRenderContext.hpp" #include "tcuTestLog.hpp" #include "glwFunctions.hpp" #include "glwEnums.hpp" #include "deStringUtil.hpp" #include <sstream> namespace deqp { namespace gles31 { namespace Functional { namespace { class SSBOArrayLengthCase : public TestCase { public: enum ArrayAccess { ACCESS_DEFAULT = 0, ACCESS_WRITEONLY, ACCESS_READONLY, ACCESS_LAST }; SSBOArrayLengthCase (Context& context, const char* name, const char* desc, ArrayAccess access, bool sized); ~SSBOArrayLengthCase (void); void init (void); void deinit (void); IterateResult iterate (void); private: std::string genComputeSource (void) const; const ArrayAccess m_access; const bool m_sized; glu::ShaderProgram* m_shader; deUint32 m_targetBufferID; deUint32 m_outputBufferID; static const int s_fixedBufferSize = 16; }; SSBOArrayLengthCase::SSBOArrayLengthCase (Context& context, const char* name, const char* desc, ArrayAccess access, bool sized) : TestCase (context, name, desc) , m_access (access) , m_sized (sized) , m_shader (DE_NULL) , m_targetBufferID (0) , m_outputBufferID (0) { } SSBOArrayLengthCase::~SSBOArrayLengthCase (void) { deinit(); } void SSBOArrayLengthCase::init (void) { const glw::Functions& gl = m_context.getRenderContext().getFunctions(); const deUint32 invalidValue = 0xFFFFFFFFUL; // program m_shader = new glu::ShaderProgram(m_context.getRenderContext(), glu::ProgramSources() << glu::ComputeSource(genComputeSource())); m_testCtx.getLog() << *m_shader; if (!m_shader->isOk()) throw tcu::TestError("Failed to build shader"); // gen and attach buffers gl.genBuffers(1, &m_outputBufferID); gl.bindBuffer(GL_SHADER_STORAGE_BUFFER, m_outputBufferID); gl.bufferData(GL_SHADER_STORAGE_BUFFER, 2 * (int)sizeof(deUint32), &invalidValue, GL_DYNAMIC_COPY); gl.genBuffers(1, &m_targetBufferID); gl.bindBuffer(GL_SHADER_STORAGE_BUFFER, m_targetBufferID); GLU_EXPECT_NO_ERROR(gl.getError(), "create buffers"); gl.bindBufferBase(GL_SHADER_STORAGE_BUFFER, 0, m_outputBufferID); gl.bindBufferBase(GL_SHADER_STORAGE_BUFFER, 1, m_targetBufferID); GLU_EXPECT_NO_ERROR(gl.getError(), "bind buffers"); // check the ssbo has expected layout { const deUint32 index = gl.getProgramResourceIndex(m_shader->getProgram(), GL_BUFFER_VARIABLE, "Out.outLength"); const glw::GLenum prop = GL_OFFSET; glw::GLint result = 0; if (index == GL_INVALID_INDEX) throw tcu::TestError("Failed to find outLength variable"); gl.getProgramResourceiv(m_shader->getProgram(), GL_BUFFER_VARIABLE, index, 1, &prop, 1, DE_NULL, &result); if (result != 0) throw tcu::TestError("Unexpected outLength location"); } { const deUint32 index = gl.getProgramResourceIndex(m_shader->getProgram(), GL_BUFFER_VARIABLE, "Out.unused"); const glw::GLenum prop = GL_OFFSET; glw::GLint result = 0; if (index == GL_INVALID_INDEX) throw tcu::TestError("Failed to find unused variable"); gl.getProgramResourceiv(m_shader->getProgram(), GL_BUFFER_VARIABLE, index, 1, &prop, 1, DE_NULL, &result); if (result != 4) throw tcu::TestError("Unexpected unused location"); } { const deUint32 index = gl.getProgramResourceIndex(m_shader->getProgram(), GL_BUFFER_VARIABLE, "Target.array"); const glw::GLenum prop = GL_ARRAY_STRIDE; glw::GLint result = 0; if (index == GL_INVALID_INDEX) throw tcu::TestError("Failed to find array variable"); gl.getProgramResourceiv(m_shader->getProgram(), GL_BUFFER_VARIABLE, index, 1, &prop, 1, DE_NULL, &result); if (result != 4) throw tcu::TestError("Unexpected array stride"); } } void SSBOArrayLengthCase::deinit (void) { if (m_shader) { delete m_shader; m_shader = DE_NULL; } if (m_targetBufferID) { m_context.getRenderContext().getFunctions().deleteBuffers(1, &m_targetBufferID); m_targetBufferID = 0; } if (m_outputBufferID) { m_context.getRenderContext().getFunctions().deleteBuffers(1, &m_outputBufferID); m_outputBufferID = 0; } } SSBOArrayLengthCase::IterateResult SSBOArrayLengthCase::iterate (void) { const glw::Functions& gl = m_context.getRenderContext().getFunctions(); bool error = false; // Update buffer size m_testCtx.getLog() << tcu::TestLog::Message << "Allocating float memory buffer with " << static_cast<int>(s_fixedBufferSize) << " elements." << tcu::TestLog::EndMessage; gl.bindBuffer(GL_SHADER_STORAGE_BUFFER, m_targetBufferID); gl.bufferData(GL_SHADER_STORAGE_BUFFER, s_fixedBufferSize * (int)sizeof(float), DE_NULL, GL_DYNAMIC_COPY); GLU_EXPECT_NO_ERROR(gl.getError(), "update buffer"); // Run compute m_testCtx.getLog() << tcu::TestLog::Message << "Running compute shader." << tcu::TestLog::EndMessage; gl.useProgram(m_shader->getProgram()); gl.dispatchCompute(1, 1, 1); GLU_EXPECT_NO_ERROR(gl.getError(), "dispatch"); // Verify { const void* ptr; gl.bindBuffer(GL_SHADER_STORAGE_BUFFER, m_outputBufferID); ptr = gl.mapBufferRange(GL_SHADER_STORAGE_BUFFER, 0, (int)sizeof(deUint32), GL_MAP_READ_BIT); GLU_EXPECT_NO_ERROR(gl.getError(), "map"); if (!ptr) throw tcu::TestError("mapBufferRange returned NULL"); if (*(const deUint32*)ptr != (deUint32)s_fixedBufferSize) { m_testCtx.getLog() << tcu::TestLog::Message << "ERROR: Length returned was " << *(const deUint32*)ptr << ", expected " << static_cast<int>(s_fixedBufferSize) << tcu::TestLog::EndMessage; error = true; } else m_testCtx.getLog() << tcu::TestLog::Message << "Length returned was correct." << tcu::TestLog::EndMessage; if (gl.unmapBuffer(GL_SHADER_STORAGE_BUFFER) == GL_FALSE) throw tcu::TestError("unmapBuffer returned false"); GLU_EXPECT_NO_ERROR(gl.getError(), "unmap"); } if (!error) m_testCtx.setTestResult(QP_TEST_RESULT_PASS, "Pass"); else m_testCtx.setTestResult(QP_TEST_RESULT_FAIL, "Fail"); return STOP; } std::string SSBOArrayLengthCase::genComputeSource (void) const { const std::string qualifierStr = (m_access == ACCESS_READONLY) ? ("readonly ") : (m_access == ACCESS_WRITEONLY) ? ("writeonly ") : (""); const std::string sizeStr = (m_sized) ? (de::toString(static_cast<int>(s_fixedBufferSize))) : (""); std::ostringstream buf; buf << "#version 310 es\n" << "layout(local_size_x = 1, local_size_y = 1) in;\n" << "layout(std430) buffer;\n" << "\n" << "layout(binding = 0) buffer Out\n" << "{\n" << " int outLength;\n" << " uint unused;\n" << "} sb_out;\n" << "layout(binding = 1) " << qualifierStr << "buffer Target\n" << "{\n" << " float array[" << sizeStr << "];\n" << "} sb_target;\n\n" << "void main (void)\n" << "{\n"; // read if (m_access == ACCESS_READONLY || m_access == ACCESS_DEFAULT) buf << " sb_out.unused = uint(sb_target.array[1]);\n"; // write if (m_access == ACCESS_WRITEONLY || m_access == ACCESS_DEFAULT) buf << " sb_target.array[2] = float(sb_out.unused);\n"; // actual test buf << "\n" << " sb_out.outLength = sb_target.array.length();\n" << "}\n"; return buf.str(); } } // anonymous SSBOArrayLengthTests::SSBOArrayLengthTests (Context& context) : TestCaseGroup(context, "array_length", "Test array.length()") { } SSBOArrayLengthTests::~SSBOArrayLengthTests (void) { } void SSBOArrayLengthTests::init (void) { static const struct Qualifier { SSBOArrayLengthCase::ArrayAccess access; const char* name; const char* desc; } qualifiers[] = { { SSBOArrayLengthCase::ACCESS_DEFAULT, "", "" }, { SSBOArrayLengthCase::ACCESS_WRITEONLY, "writeonly_", "writeonly" }, { SSBOArrayLengthCase::ACCESS_READONLY, "readonly_", "readonly" }, }; static const bool arraysSized[] = { true, false }; for (int sizeNdx = 0; sizeNdx < DE_LENGTH_OF_ARRAY(arraysSized); ++sizeNdx) for (int qualifierNdx = 0; qualifierNdx < DE_LENGTH_OF_ARRAY(qualifiers); ++qualifierNdx) { const std::string name = std::string() + ((arraysSized[sizeNdx]) ? ("sized_") : ("unsized_")) + qualifiers[qualifierNdx].name + "array"; const std::string desc = std::string("Test length() of ") + ((arraysSized[sizeNdx]) ? ("sized ") : ("unsized ")) + qualifiers[qualifierNdx].name + " array"; this->addChild(new SSBOArrayLengthCase(m_context, name.c_str(), desc.c_str(), qualifiers[qualifierNdx].access, arraysSized[sizeNdx])); } } } // Functional } // gles31 } // deqp