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
34namespace deqp
35{
36namespace gles31
37{
38namespace Functional
39{
40namespace
41{
42
43class SSBOArrayLengthCase : public TestCase
44{
45public:
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
62private:
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
75SSBOArrayLengthCase::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
85SSBOArrayLengthCase::~SSBOArrayLengthCase (void)
86{
87	deinit();
88}
89
90void 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
159void 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
180SSBOArrayLengthCase::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
235std::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
275SSBOArrayLengthTests::SSBOArrayLengthTests (Context& context)
276	: TestCaseGroup(context, "array_length", "Test array.length()")
277{
278}
279
280SSBOArrayLengthTests::~SSBOArrayLengthTests (void)
281{
282}
283
284void 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