1 /*------------------------------------------------------------------------- 2 * drawElements Quality Program OpenGL ES 3.1 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 SSBO array length tests. 22 *//*--------------------------------------------------------------------*/ 23 24 #include "es31fSSBOArrayLengthTests.hpp" 25 #include "gluShaderProgram.hpp" 26 #include "gluRenderContext.hpp" 27 #include "tcuTestLog.hpp" 28 #include "glwFunctions.hpp" 29 #include "glwEnums.hpp" 30 #include "deStringUtil.hpp" 31 32 #include <sstream> 33 34 namespace deqp 35 { 36 namespace gles31 37 { 38 namespace Functional 39 { 40 namespace 41 { 42 43 class SSBOArrayLengthCase : public TestCase 44 { 45 public: 46 enum ArrayAccess 47 { 48 ACCESS_DEFAULT = 0, 49 ACCESS_WRITEONLY, 50 ACCESS_READONLY, 51 52 ACCESS_LAST 53 }; 54 55 SSBOArrayLengthCase (Context& context, const char* name, const char* desc, ArrayAccess access, bool sized); 56 ~SSBOArrayLengthCase (void); 57 58 void init (void); 59 void deinit (void); 60 IterateResult iterate (void); 61 62 private: 63 std::string genComputeSource (void) const; 64 65 const ArrayAccess m_access; 66 const bool m_sized; 67 68 glu::ShaderProgram* m_shader; 69 deUint32 m_targetBufferID; 70 deUint32 m_outputBufferID; 71 72 static const int s_fixedBufferSize = 16; 73 }; 74 75 SSBOArrayLengthCase::SSBOArrayLengthCase (Context& context, const char* name, const char* desc, ArrayAccess access, bool sized) 76 : TestCase (context, name, desc) 77 , m_access (access) 78 , m_sized (sized) 79 , m_shader (DE_NULL) 80 , m_targetBufferID (0) 81 , m_outputBufferID (0) 82 { 83 } 84 85 SSBOArrayLengthCase::~SSBOArrayLengthCase (void) 86 { 87 deinit(); 88 } 89 90 void SSBOArrayLengthCase::init (void) 91 { 92 const glw::Functions& gl = m_context.getRenderContext().getFunctions(); 93 const deUint32 invalidValue = 0xFFFFFFFFUL; 94 95 // program 96 m_shader = new glu::ShaderProgram(m_context.getRenderContext(), glu::ProgramSources() << glu::ComputeSource(genComputeSource())); 97 m_testCtx.getLog() << *m_shader; 98 99 if (!m_shader->isOk()) 100 throw tcu::TestError("Failed to build shader"); 101 102 // gen and attach buffers 103 gl.genBuffers(1, &m_outputBufferID); 104 gl.bindBuffer(GL_SHADER_STORAGE_BUFFER, m_outputBufferID); 105 gl.bufferData(GL_SHADER_STORAGE_BUFFER, 2 * (int)sizeof(deUint32), &invalidValue, GL_DYNAMIC_COPY); 106 107 gl.genBuffers(1, &m_targetBufferID); 108 gl.bindBuffer(GL_SHADER_STORAGE_BUFFER, m_targetBufferID); 109 110 GLU_EXPECT_NO_ERROR(gl.getError(), "create buffers"); 111 112 gl.bindBufferBase(GL_SHADER_STORAGE_BUFFER, 0, m_outputBufferID); 113 gl.bindBufferBase(GL_SHADER_STORAGE_BUFFER, 1, m_targetBufferID); 114 115 GLU_EXPECT_NO_ERROR(gl.getError(), "bind buffers"); 116 117 // check the ssbo has expected layout 118 { 119 const deUint32 index = gl.getProgramResourceIndex(m_shader->getProgram(), GL_BUFFER_VARIABLE, "Out.outLength"); 120 const glw::GLenum prop = GL_OFFSET; 121 glw::GLint result = 0; 122 123 if (index == GL_INVALID_INDEX) 124 throw tcu::TestError("Failed to find outLength variable"); 125 126 gl.getProgramResourceiv(m_shader->getProgram(), GL_BUFFER_VARIABLE, index, 1, &prop, 1, DE_NULL, &result); 127 128 if (result != 0) 129 throw tcu::TestError("Unexpected outLength location"); 130 } 131 { 132 const deUint32 index = gl.getProgramResourceIndex(m_shader->getProgram(), GL_BUFFER_VARIABLE, "Out.unused"); 133 const glw::GLenum prop = GL_OFFSET; 134 glw::GLint result = 0; 135 136 if (index == GL_INVALID_INDEX) 137 throw tcu::TestError("Failed to find unused variable"); 138 139 gl.getProgramResourceiv(m_shader->getProgram(), GL_BUFFER_VARIABLE, index, 1, &prop, 1, DE_NULL, &result); 140 141 if (result != 4) 142 throw tcu::TestError("Unexpected unused location"); 143 } 144 { 145 const deUint32 index = gl.getProgramResourceIndex(m_shader->getProgram(), GL_BUFFER_VARIABLE, "Target.array"); 146 const glw::GLenum prop = GL_ARRAY_STRIDE; 147 glw::GLint result = 0; 148 149 if (index == GL_INVALID_INDEX) 150 throw tcu::TestError("Failed to find array variable"); 151 152 gl.getProgramResourceiv(m_shader->getProgram(), GL_BUFFER_VARIABLE, index, 1, &prop, 1, DE_NULL, &result); 153 154 if (result != 4) 155 throw tcu::TestError("Unexpected array stride"); 156 } 157 } 158 159 void SSBOArrayLengthCase::deinit (void) 160 { 161 if (m_shader) 162 { 163 delete m_shader; 164 m_shader = DE_NULL; 165 } 166 167 if (m_targetBufferID) 168 { 169 m_context.getRenderContext().getFunctions().deleteBuffers(1, &m_targetBufferID); 170 m_targetBufferID = 0; 171 } 172 173 if (m_outputBufferID) 174 { 175 m_context.getRenderContext().getFunctions().deleteBuffers(1, &m_outputBufferID); 176 m_outputBufferID = 0; 177 } 178 } 179 180 SSBOArrayLengthCase::IterateResult SSBOArrayLengthCase::iterate (void) 181 { 182 const glw::Functions& gl = m_context.getRenderContext().getFunctions(); 183 bool error = false; 184 185 // Update buffer size 186 187 m_testCtx.getLog() << tcu::TestLog::Message << "Allocating float memory buffer with " << static_cast<int>(s_fixedBufferSize) << " elements." << tcu::TestLog::EndMessage; 188 189 gl.bindBuffer(GL_SHADER_STORAGE_BUFFER, m_targetBufferID); 190 gl.bufferData(GL_SHADER_STORAGE_BUFFER, s_fixedBufferSize * (int)sizeof(float), DE_NULL, GL_DYNAMIC_COPY); 191 192 GLU_EXPECT_NO_ERROR(gl.getError(), "update buffer"); 193 194 // Run compute 195 196 m_testCtx.getLog() << tcu::TestLog::Message << "Running compute shader." << tcu::TestLog::EndMessage; 197 198 gl.useProgram(m_shader->getProgram()); 199 gl.dispatchCompute(1, 1, 1); 200 201 GLU_EXPECT_NO_ERROR(gl.getError(), "dispatch"); 202 203 // Verify 204 { 205 const void* ptr; 206 207 gl.bindBuffer(GL_SHADER_STORAGE_BUFFER, m_outputBufferID); 208 ptr = gl.mapBufferRange(GL_SHADER_STORAGE_BUFFER, 0, (int)sizeof(deUint32), GL_MAP_READ_BIT); 209 GLU_EXPECT_NO_ERROR(gl.getError(), "map"); 210 211 if (!ptr) 212 throw tcu::TestError("mapBufferRange returned NULL"); 213 214 if (*(const deUint32*)ptr != (deUint32)s_fixedBufferSize) 215 { 216 m_testCtx.getLog() << tcu::TestLog::Message << "ERROR: Length returned was " << *(const deUint32*)ptr << ", expected " << static_cast<int>(s_fixedBufferSize) << tcu::TestLog::EndMessage; 217 error = true; 218 } 219 else 220 m_testCtx.getLog() << tcu::TestLog::Message << "Length returned was correct." << tcu::TestLog::EndMessage; 221 222 if (gl.unmapBuffer(GL_SHADER_STORAGE_BUFFER) == GL_FALSE) 223 throw tcu::TestError("unmapBuffer returned false"); 224 225 GLU_EXPECT_NO_ERROR(gl.getError(), "unmap"); 226 } 227 228 if (!error) 229 m_testCtx.setTestResult(QP_TEST_RESULT_PASS, "Pass"); 230 else 231 m_testCtx.setTestResult(QP_TEST_RESULT_FAIL, "Fail"); 232 return STOP; 233 } 234 235 std::string SSBOArrayLengthCase::genComputeSource (void) const 236 { 237 const std::string qualifierStr = (m_access == ACCESS_READONLY) ? ("readonly ") : (m_access == ACCESS_WRITEONLY) ? ("writeonly ") : (""); 238 const std::string sizeStr = (m_sized) ? (de::toString(static_cast<int>(s_fixedBufferSize))) : (""); 239 240 std::ostringstream buf; 241 buf << "#version 310 es\n" 242 << "layout(local_size_x = 1, local_size_y = 1) in;\n" 243 << "layout(std430) buffer;\n" 244 << "\n" 245 << "layout(binding = 0) buffer Out\n" 246 << "{\n" 247 << " int outLength;\n" 248 << " uint unused;\n" 249 << "} sb_out;\n" 250 << "layout(binding = 1) " << qualifierStr << "buffer Target\n" 251 << "{\n" 252 << " float array[" << sizeStr << "];\n" 253 << "} sb_target;\n\n" 254 << "void main (void)\n" 255 << "{\n"; 256 257 // read 258 if (m_access == ACCESS_READONLY || m_access == ACCESS_DEFAULT) 259 buf << " sb_out.unused = uint(sb_target.array[1]);\n"; 260 261 // write 262 if (m_access == ACCESS_WRITEONLY || m_access == ACCESS_DEFAULT) 263 buf << " sb_target.array[2] = float(sb_out.unused);\n"; 264 265 // actual test 266 buf << "\n" 267 << " sb_out.outLength = sb_target.array.length();\n" 268 << "}\n"; 269 270 return buf.str(); 271 } 272 273 } // anonymous 274 275 SSBOArrayLengthTests::SSBOArrayLengthTests (Context& context) 276 : TestCaseGroup(context, "array_length", "Test array.length()") 277 { 278 } 279 280 SSBOArrayLengthTests::~SSBOArrayLengthTests (void) 281 { 282 } 283 284 void SSBOArrayLengthTests::init (void) 285 { 286 static const struct Qualifier 287 { 288 SSBOArrayLengthCase::ArrayAccess access; 289 const char* name; 290 const char* desc; 291 } qualifiers[] = 292 { 293 { SSBOArrayLengthCase::ACCESS_DEFAULT, "", "" }, 294 { SSBOArrayLengthCase::ACCESS_WRITEONLY, "writeonly_", "writeonly" }, 295 { SSBOArrayLengthCase::ACCESS_READONLY, "readonly_", "readonly" }, 296 }; 297 298 static const bool arraysSized[] = { true, false }; 299 300 for (int sizeNdx = 0; sizeNdx < DE_LENGTH_OF_ARRAY(arraysSized); ++sizeNdx) 301 for (int qualifierNdx = 0; qualifierNdx < DE_LENGTH_OF_ARRAY(qualifiers); ++qualifierNdx) 302 { 303 const std::string name = std::string() + ((arraysSized[sizeNdx]) ? ("sized_") : ("unsized_")) + qualifiers[qualifierNdx].name + "array"; 304 const std::string desc = std::string("Test length() of ") + ((arraysSized[sizeNdx]) ? ("sized ") : ("unsized ")) + qualifiers[qualifierNdx].name + " array"; 305 306 this->addChild(new SSBOArrayLengthCase(m_context, name.c_str(), desc.c_str(), qualifiers[qualifierNdx].access, arraysSized[sizeNdx])); 307 } 308 } 309 310 } // Functional 311 } // gles31 312 } // deqp 313