#ifndef _GL4CSPARSEBUFFERTESTS_HPP #define _GL4CSPARSEBUFFERTESTS_HPP /*------------------------------------------------------------------------- * OpenGL Conformance Test Suite * ----------------------------- * * Copyright (c) 2015-2016 The Khronos Group Inc. * * 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 */ /*-------------------------------------------------------------------*/ /** */ /*! * \file gl4cSparseBufferTests.hpp * \brief Conformance tests for the GL_ARB_sparse_buffer functionality. */ /*-------------------------------------------------------------------*/ #include "glcTestCase.hpp" #include "glwDefs.hpp" #include "glwEnums.hpp" #include "tcuDefs.hpp" #include namespace gl4cts { /** Utility functions, used across many sparse buffer conformance test classes. */ class SparseBufferTestUtilities { public: /* Public methods */ static unsigned int alignOffset(const unsigned int& offset, const unsigned int& value); static glw::GLuint createComputeProgram(const glw::Functions& gl, const char** cs_body_parts, unsigned int n_cs_body_parts); static glw::GLuint createProgram(const glw::Functions& gl, const char** fs_body_parts, unsigned int n_fs_body_parts, const char** vs_body_parts, unsigned int n_vs_body_parts, const char** attribute_names, const unsigned int* attribute_locations, unsigned int n_attribute_properties, const glw::GLchar* const* tf_varyings = DE_NULL, unsigned int n_tf_varyings = 0, glw::GLenum tf_varying_mode = GL_NONE); static std::string getSparseBOFlagsString(glw::GLenum flags); }; /** * Verify glBufferPageCommitmentARB() returns GL_INVALID_ENUM if is * set to GL_INTERLEAVED_ATTRIBS. * * * Verify glBufferStorage() throws a GL_INVALID_VALUE error if is * set to (GL_SPARSE_STORAGE_BIT_ARB | GL_MAP_READ_BIT) or * (GL_SPARSE_STORAGE_BIT_ARB | GL_MAP_WRITE_BIT). * * * Verify glBufferPageCommitmentARB() generates a GL_INVALID_OPERATION error if * it is called for an immutable BO, which has not been initialized with the * GL_SPARSE_STORAGE_BIT_ARB flag. * * * Verify glBufferPageCommitmentARB() issues a GL_INVALID_VALUE error if * is set to (0.5 * GL_SPARSE_BUFFER_PAGE_SIZE_ARB). Skip if the constant's value * is equal to 1. * * * Verify glBufferPageCommitmentARB() emits a GL_INVALID_VALUE error if * is set to (0.5 * GL_SPARSE_BUFFER_PAGE_SIZE_ARB). Skip if the constant's value * is equal to 1. * * * Verify glBufferPageCommitmentARB() returns GL_INVALID_VALUE if is * set to -1, but all other arguments are valid. * * * Verify glBufferPageCommitmentARB() returns GL_INVALID_VALUE if is * set to -1, but all other arguments are valid. * * * Verify glBufferPageCommitmentARB() returns GL_INVALID_VALUE if BO's size is * GL_SPARSE_BUFFER_PAGE_SIZE_ARB * 3, but the is set to 0 and * argument used for the call is set to GL_SPARSE_BUFFER_PAGE_SIZE_ARB * 4. * * * Verify glBufferPageCommitmentARB() returns GL_INVALID_VALUE if BO's size is * GL_SPARSE_BUFFER_PAGE_SIZE_ARB * 3, but the is set to * GL_SPARSE_BUFFER_PAGE_SIZE_ARB * 1 and argument used for the call * is set to GL_SPARSE_BUFFER_PAGE_SIZE_ARB * 3. * * * Verify that calling glMapBuffer() or glMapBufferRange() against a sparse * buffer generates a GL_INVALID_OPERATION error. **/ class NegativeTests : public deqp::TestCase { public: /* Public methods */ NegativeTests(deqp::Context& context); void deinit(); void init(); tcu::TestNode::IterateResult iterate(); private: /* Private methods */ /* Private members */ glw::GLuint m_helper_bo_id; /* never allocated actual storage; bound to GL_ELEMENT_ARRAY_BUFFER */ glw::GLuint m_immutable_bo_id; /* bound to GL_COPY_READ_BUFFER */ const unsigned int m_immutable_bo_size; glw::GLuint m_sparse_bo_id; /* bound to GL_ARRAY_BUFFER */ }; /** 1. Make sure glGetBooleanv(), glGetDoublev(), glGetFloatv(), glGetIntegerv() * and glGetInteger64v() recognize the new GL_SPARSE_BUFFER_PAGE_SIZE_ARB * pname and return a value equal to or larger than 1, but no bigger than 65536 */ class PageSizeGetterTest : public deqp::TestCase { public: /* Public methods */ PageSizeGetterTest(deqp::Context& context); void deinit(); void init(); tcu::TestNode::IterateResult iterate(); }; /** Interface class for test case implementation for the functional test 2. */ class BufferStorageTestCase { public: virtual ~BufferStorageTestCase() { } /* Public methods */ virtual void deinitTestCaseGlobal() = 0; virtual bool execute(glw::GLuint sparse_bo_storage_flags) = 0; virtual const char* getName() = 0; virtual bool initTestCaseGlobal() = 0; virtual bool initTestCaseIteration(glw::GLuint sparse_bo) = 0; virtual void deinitTestCaseIteration() { /* Stub by default */ } }; /** Implements the test case e for the test 2: * * e. Use the committed sparse buffer storage to store atomic counter values. * The vertex shader used for the test case should define as many ACs as * supported by the platform (GL_MAX_VERTEX_ATOMIC_COUNTERS). The condition, * under which each of the ACs should be incremented, can be based on * gl_VertexID's value (eg. increment AC0 if gl_VertexID % 2 == 0, increment * AC1 if gl_VertexID % 3 == 0, and so on). * * Use regular draw calls, issued consecutively for three times, for the * test. * Verify that both atomic counter buffer binding commands (glBindBufferBase() * and glBindBufferRange() ) work correctly. * * The test passes if the result values are correct. * * The test should run in two iterations: * a) All required pages are committed. * b) Only half of the pages are committed. If only a single page is needed, * de-commit that page before issuing the draw call. */ class AtomicCounterBufferStorageTestCase : public BufferStorageTestCase { public: /* Public methods */ AtomicCounterBufferStorageTestCase(const glw::Functions& gl, tcu::TestContext& testContext, glw::GLint page_size, bool all_pages_committed); /* BufferStorageTestCase implementation */ void deinitTestCaseGlobal(); void deinitTestCaseIteration(); bool execute(glw::GLuint sparse_bo_storage_flags); bool initTestCaseGlobal(); bool initTestCaseIteration(glw::GLuint sparse_bo); const char* getName() { return "case e"; } private: /* Private fields */ bool m_all_pages_committed; const glw::Functions& m_gl; glw::GLint m_gl_atomic_counter_uniform_array_stride; glw::GLint m_gl_max_vertex_atomic_counters_value; glw::GLuint m_helper_bo; unsigned int m_helper_bo_size; unsigned int m_helper_bo_size_rounded; const unsigned int m_n_draw_calls; glw::GLint m_page_size; glw::GLuint m_po; glw::GLuint m_sparse_bo; unsigned int m_sparse_bo_data_size; unsigned int m_sparse_bo_data_size_rounded; /* aligned to page size */ unsigned int m_sparse_bo_data_start_offset; unsigned int m_sparse_bo_data_start_offset_rounded; /* <= m_sparse_bo_data_start_offset, aligned to page size */ tcu::TestContext& m_testCtx; glw::GLuint m_vao; }; /** Implements the test case f for the test 2: * * f. Use the committed sparse buffer storage as a backing for a buffer texture * object. A compute shader should inspect the contents of the texture and, * for invocation-specific texels, write out 1 to a SSBO if the fetched texel * was correct. Otherwise, it should write out 0. * * The shader storage block needs not be backed by a sparse buffer. * * As with previous cases, make sure both of the following scenarios are * tested: * * a) All required pages are committed. * b) Only half of the pages are committed. If only a single page is needed, * de-commit that page before issuing the dispatch call. * * Both glTexBuffer() and glTexBufferRange() should be tested. * */ class BufferTextureStorageTestCase : public BufferStorageTestCase { public: /* Public methods */ BufferTextureStorageTestCase(const glw::Functions& gl, deqp::Context& context, tcu::TestContext& testContext, glw::GLint page_size); /* BufferStorageTestCase implementation */ void deinitTestCaseGlobal(); void deinitTestCaseIteration(); bool execute(glw::GLuint sparse_bo_storage_flags); bool initTestCaseGlobal(); bool initTestCaseIteration(glw::GLuint sparse_bo); const char* getName() { return "case f"; } private: /* Private fields */ const glw::Functions& m_gl; glw::GLuint m_helper_bo; unsigned char* m_helper_bo_data; unsigned int m_helper_bo_data_size; bool m_is_texture_buffer_range_supported; glw::GLint m_page_size; glw::GLuint m_po; const unsigned int m_po_local_wg_size; glw::GLuint m_sparse_bo; unsigned int m_sparse_bo_size; unsigned int m_sparse_bo_size_rounded; glw::GLuint m_ssbo; unsigned char* m_ssbo_zero_data; unsigned int m_ssbo_zero_data_size; tcu::TestContext& m_testCtx; glw::GLuint m_to; const unsigned int m_to_width; }; /** Implements the test case c for the test 2: * * c. Issue glClearBufferData() and glClearBufferSubData() calls * over a sparse buffer. Make sure that all committed pages, which should * have been affected by the calls, have been reset to the requested * values. * Try issuing glClearNamedBufferSubData() over a region, for which one * of the halves is committed, and the other is not. Make sure the former * has been touched, and that no crash has occurred. * */ class ClearOpsBufferStorageTestCase : public BufferStorageTestCase { public: /* Public methods */ ClearOpsBufferStorageTestCase(const glw::Functions& gl, tcu::TestContext& testContext, glw::GLint page_size); /* BufferStorageTestCase implementation */ void deinitTestCaseGlobal(); void deinitTestCaseIteration(); bool execute(glw::GLuint sparse_bo_storage_flags); bool initTestCaseGlobal(); bool initTestCaseIteration(glw::GLuint sparse_bo); const char* getName() { return "case c"; } private: /* Private fields */ const glw::Functions& m_gl; glw::GLuint m_helper_bo; /* holds m_sparse_bo_size_rounded bytes */ unsigned char* m_initial_data; /* holds m_sparse_bo_size_rounded bytes */ unsigned int m_n_pages_to_use; glw::GLint m_page_size; glw::GLuint m_sparse_bo; unsigned int m_sparse_bo_size_rounded; tcu::TestContext& m_testCtx; }; /** Implements the test case g for the test 2: * * g. Verify copy operations work correctly for cases where: * * I) Destination and source are different sparse BOs. * II) Destination is a sparse buffer object, source is an immutable BO. * III) Destination is an immutable BO, source is a sparse BO. * IV) Destination and source are the same sparse BO, but refer to * different, non-overlapping memory regions. * * and * * *) All pages of the source region are not committed * **) Half of the pages of the source region is not committed * ***) None of the pages of the source region are committed. * * and * * +) All pages of the destination region are not committed * ++) Half of the pages of the destination region is not committed * +++) None of the pages of the destination region are committed. * * Test all combinations of I-IV, *-***, and +-+++ bearing in mind that: * * a) reads executed on non-committed memory regions return meaningless * values but MUST NOT crash GL * b) writes performed on non-committed memory regions are silently * ignored. */ class CopyOpsBufferStorageTestCase : public BufferStorageTestCase { public: /* Public methods */ CopyOpsBufferStorageTestCase(const glw::Functions& gl, tcu::TestContext& testContext, glw::GLint page_size); /* BufferStorageTestCase implementation */ void deinitTestCaseGlobal(); void deinitTestCaseIteration(); bool execute(glw::GLuint sparse_bo_storage_flags); bool initTestCaseGlobal(); bool initTestCaseIteration(glw::GLuint sparse_bo); const char* getName() { return "case g"; } private: /* Private type definitions */ typedef struct _test_case { glw::GLint dst_bo_commit_size; glw::GLint dst_bo_commit_start_offset; glw::GLuint dst_bo_sparse_id; bool dst_bo_is_sparse; unsigned short* dst_bo_ref_data; glw::GLint dst_bo_start_offset; glw::GLint n_bytes_to_copy; glw::GLint src_bo_commit_size; glw::GLint src_bo_commit_start_offset; glw::GLuint src_bo_sparse_id; bool src_bo_is_sparse; unsigned short* src_bo_ref_data; glw::GLint src_bo_start_offset; } _test_case; typedef std::vector<_test_case> _test_cases; typedef _test_cases::const_iterator _test_cases_const_iterator; typedef _test_cases::iterator _test_cases_iterator; /* Private methods */ void initReferenceData(); void initTestCases(); /* Private fields */ const glw::Functions& m_gl; glw::GLuint m_helper_bo; glw::GLuint m_immutable_bo; glw::GLint m_page_size; unsigned short* m_ref_data[3]; /* [0] - immutable bo data, [1] - sparse bo[0] data, [2] - sparse bo[1] data. * * Each data buffer holds m_sparse_bo_size_rounded bytes. */ glw::GLuint m_sparse_bos[2]; /* [0] - provided by BufferStorageTest[0], [1] - managed by the test case */ unsigned int m_sparse_bo_size; unsigned int m_sparse_bo_size_rounded; _test_cases m_test_cases; tcu::TestContext& m_testCtx; }; /** Implements the test case h for the test 2: * * h. Verify indirect dispatch calls work correctly for the following cases: * * a) The arguments are taken from a committed memory page. * b) The arguments are taken from a de-committed memory page. We expect * the dispatch request to be silently ignored in this case. * c) Half of the arguments are taken from a committed memory page, * and the other half come from a de-committed memory page. Anticipated * result is as per b). * * Each spawned compute shader invocation should increment an atomic * counter. * */ class IndirectDispatchBufferStorageTestCase : public BufferStorageTestCase { public: /* Public methods */ IndirectDispatchBufferStorageTestCase(const glw::Functions& gl, tcu::TestContext& testContext, glw::GLint page_size); /* BufferStorageTestCase implementation */ void deinitTestCaseGlobal(); void deinitTestCaseIteration(); bool execute(glw::GLuint sparse_bo_storage_flags); bool initTestCaseGlobal(); bool initTestCaseIteration(glw::GLuint sparse_bo); const char* getName() { return "case h"; } private: /* Private fields */ unsigned int m_dispatch_draw_call_args_start_offset; unsigned int m_expected_ac_value; const glw::Functions& m_gl; const unsigned int m_global_wg_size_x; glw::GLuint m_helper_bo; /* stores AC value + indirect dispatch call args */ const unsigned int m_local_wg_size_x; glw::GLint m_page_size; glw::GLuint m_po; glw::GLuint m_sparse_bo; unsigned int m_sparse_bo_size; unsigned int m_sparse_bo_size_rounded; tcu::TestContext& m_testCtx; }; /** Implements the test case d for the test 2: * * d. Issue glInvalidateBufferData() and glInvalidateBufferSubData() calls for * sparse buffers. For the *SubData() case, make sure you test both of * cases: * * * the whole touched region has been committed * * only half of the pages have physical backing. */ class InvalidateBufferStorageTestCase : public BufferStorageTestCase { public: /* Public methods */ InvalidateBufferStorageTestCase(const glw::Functions& gl, tcu::TestContext& testContext, glw::GLint page_size); /* BufferStorageTestCase implementation */ void deinitTestCaseGlobal(); void deinitTestCaseIteration(); bool execute(glw::GLuint sparse_bo_storage_flags); bool initTestCaseGlobal(); bool initTestCaseIteration(glw::GLuint sparse_bo); const char* getName() { return "case d"; } private: /* Private fields */ const glw::Functions& m_gl; unsigned int m_n_pages_to_use; const glw::GLint m_page_size; glw::GLuint m_sparse_bo; unsigned int m_sparse_bo_size; unsigned int m_sparse_bo_size_rounded; }; /** Implement the test case k from CTS_ARB_sparse_buffer: * * k. Verify pixel pack functionality works correctly, when a sparse buffer * is bound to the pixel pack buffer binding point. Render a black-to-white * RGBA8 gradient and use glReadPixels() to read & verify the rendered * data. The color attachment should be of 1024x1024 resolution. * * Consider three scenarios: * * a) All pages, to which the data is to be written to, have been committed. * b) Use the same memory page commitment layout as proposed in b2. The * committed pages should contain correct data. Contents the pages * without the physical backing should not be verified. * c) No pages have been committed. The draw & read call should not crash * the driver, but the actual contents is of no relevance. * **/ class PixelPackBufferStorageTestCase : public BufferStorageTestCase { public: /* Public methods */ PixelPackBufferStorageTestCase(const glw::Functions& gl, tcu::TestContext& testContext, glw::GLint page_size); /* BufferStorageTestCase implementation */ void deinitTestCaseGlobal(); void deinitTestCaseIteration(); bool execute(glw::GLuint sparse_bo_storage_flags); bool initTestCaseGlobal(); bool initTestCaseIteration(glw::GLuint sparse_bo); const char* getName() { return "case k"; } private: /* Private fields */ glw::GLuint m_color_rb; const unsigned int m_color_rb_height; const unsigned int m_color_rb_width; glw::GLuint m_fbo; const glw::Functions& m_gl; glw::GLuint m_helper_bo; glw::GLint m_page_size; glw::GLuint m_po; unsigned char* m_ref_data_ptr; unsigned int m_ref_data_size; glw::GLuint m_sparse_bo; unsigned int m_sparse_bo_size; unsigned int m_sparse_bo_size_rounded; tcu::TestContext& m_testCtx; glw::GLuint m_vao; }; /** Implements the test case l for the test 2: * * l. Verify pixel unpack functionality works correctly, when a sparse buffer * is bound to the pixel unpack buffer binding point. Use a black-to-white * gradient texture data for a glTexSubImage2D() call applied against an * immutable texture object's base mip-map. Read back the data with * a glGetTexImage() call and verify the contents is valid. * * Consider three scenarios: * * a) All pages, from which the texture data were read from, have been * committed at the glTexSubImage2D() call time. * b) Use the same memory page commitment layout as proposed in b2. The * test should only check contents of the committed memory pages. * c) No pages have been committed at the glTexSubImage2D() call time. * The upload & getter calls should not crash, but the returned * contents are irrelevant in this case. */ class PixelUnpackBufferStorageTestCase : public BufferStorageTestCase { public: /* Public methods */ PixelUnpackBufferStorageTestCase(const glw::Functions& gl, tcu::TestContext& testContext, glw::GLint page_size); /* BufferStorageTestCase implementation */ void deinitTestCaseGlobal(); void deinitTestCaseIteration(); bool execute(glw::GLuint sparse_bo_storage_flags); bool initTestCaseGlobal(); bool initTestCaseIteration(glw::GLuint sparse_bo); const char* getName() { return "case l"; } private: /* Private fields */ const glw::Functions& m_gl; glw::GLuint m_helper_bo; glw::GLint m_page_size; unsigned char* m_read_data_ptr; glw::GLuint m_sparse_bo; unsigned int m_sparse_bo_size; unsigned int m_sparse_bo_size_rounded; tcu::TestContext& m_testCtx; unsigned char* m_texture_data_ptr; unsigned int m_texture_data_size; glw::GLuint m_to; unsigned char* m_to_data_zero; const unsigned int m_to_height; const unsigned int m_to_width; }; /** Implements test cases a1-a6 for the test 2: * * a1. Use the sparse buffer as a VBO. * * The render-target should be drawn a total of 100 x 100 green quads * (built of triangles). Fill the buffer with vertex data (use four * components, even though we need the rectangles to be rendered in * screen space, in order to assure that the data-set spans across * multiple pages by exceeding the maximum permitted page size of 64KB). * * The quads should be 5px in width & height, and be separated from each * other by a delta of 5px. * * Render the quads to a render-target of 1024x1024 resolution. * * All the pages, to which the vertex data has been submitted, should * be committed. The test case passes if the rendered data is correct. * * a2. Follow the same approach as described for a1. However, this time, * after the vertex data is uploaded, the test should de-commit all the * pages and attempt to do the draw call. * * The test passes if the GL implementation does not crash. Do not * validate the rendered data. * * a3. Follow the same approach as described for a1. However, this time, * make sure to also provide an IBO and issue an indexed draw call * (both ranged and non-ranged). All required VBO and IBO pages should * be committed. * * The pass condition described in a1 is not changed. * * a4. Follow the same approach as described for a2. However, this time, * after the vertex and index data is uploaded, the test should de-commit * pages storing both IBO and VBO data. Both draw calls should be issued * then. * * The pass condition described in a2 is not changed. * * a5. Follow the same approach as described for a1. Apply the following * change: * * - Each rectangle should now be assigned a color, exposed to the VS * via a Vertex Attribute Array. The color data should come from committed * sparse buffer pages. * * a6. Follow the same approach as described for a5. Apply the following * change: * * - De-commit color data, after it has been uploaded. Try to execute the * draw call. * * The test passes if the GL implementation does not crash. Do not * validate the rendered data. */ class QuadsBufferStorageTestCase : public BufferStorageTestCase { public: /* Type definitions */ enum _ibo_usage { /* Use glDrawArrays() for the draw call */ IBO_USAGE_NONE, /* Use glDrawElements() for the draw call */ IBO_USAGE_INDEXED_DRAW_CALL, /* Use glDrawRangeElements() for the draw call */ IBO_USAGE_INDEXED_RANGED_DRAW_CALL }; /* Public methods */ QuadsBufferStorageTestCase(const glw::Functions& gl, tcu::TestContext& testContext, glw::GLint page_size, _ibo_usage ibo_usage, bool use_color_data); /* BufferStorageTestCase implementation */ void deinitTestCaseGlobal(); void deinitTestCaseIteration(); bool execute(glw::GLuint sparse_bo_storage_flags); bool initTestCaseGlobal(); bool initTestCaseIteration(glw::GLuint sparse_bo); const char* getName() { return (!m_use_color_data && m_ibo_usage == IBO_USAGE_NONE) ? "cases a1-a2" : (!m_use_color_data && m_ibo_usage != IBO_USAGE_NONE) ? "cases a3-a4" : (m_use_color_data && m_ibo_usage != IBO_USAGE_NONE) ? "casea a5-a6" : "?!"; } private: /* Private methods */ void createTestData(unsigned char** out_data, unsigned int* out_vbo_data_offset, unsigned int* out_ibo_data_offset, unsigned int* out_color_data_offset) const; void initHelperBO(); void initSparseBO(bool decommit_data_pages_after_upload, bool is_dynamic_storage); /* Private fields */ glw::GLuint m_attribute_color_location; glw::GLuint m_attribute_position_location; glw::GLuint m_color_data_offset; unsigned char* m_data; glw::GLuint m_data_size; /* ibo, vbo, color data */ glw::GLuint m_data_size_rounded; /* rounded up to page size */ glw::GLuint m_fbo; const glw::Functions& m_gl; glw::GLuint m_helper_bo; glw::GLuint m_ibo_data_offset; _ibo_usage m_ibo_usage; const unsigned int m_n_quad_delta_x; const unsigned int m_n_quad_delta_y; const unsigned int m_n_quad_height; const unsigned int m_n_quad_width; const unsigned int m_n_quads_x; const unsigned int m_n_quads_y; unsigned int m_n_vertices_to_draw; bool m_pages_committed; glw::GLuint m_po; glw::GLuint m_sparse_bo; tcu::TestContext& m_testCtx; glw::GLuint m_to; const unsigned int m_to_height; const unsigned int m_to_width; bool m_use_color_data; glw::GLuint m_vao; glw::GLuint m_vbo_data_offset; }; /** Implements test case m for the test 2: * * m. Verify query functionality works correctly, when a sparse buffer is bound * to the query buffer binding point. Render a number of triangles while * a GL_PRIMITIVES_GENERATED query is enabled and the BO is bound to the * GL_QUERY_BUFFER binding point. Read back the value of the query from * the BO and verify it is correct using glGetQueryObjectiv(), * glGetQueryObjectuiv(), glGetQueryObjecti64v() and glGetQueryObjectui64v() * functions. * * Consider two scenarios: * * a) The page holding the result value is committed. * b) The page holding the result value is NOT committed. In this case, * the draw call glGetQueryObjectuiv() and all the getter functions should * not crash, but the reported values are irrelevant. */ class QueryBufferStorageTestCase : public BufferStorageTestCase { public: /* Public methods */ QueryBufferStorageTestCase(const glw::Functions& gl, tcu::TestContext& testContext, glw::GLint page_size); /* BufferStorageTestCase implementation */ void deinitTestCaseGlobal(); void deinitTestCaseIteration(); bool execute(glw::GLuint sparse_bo_storage_flags); bool initTestCaseGlobal(); bool initTestCaseIteration(glw::GLuint sparse_bo); const char* getName() { return "case m"; } private: /* Private fields */ const glw::Functions& m_gl; glw::GLuint m_helper_bo; const unsigned int m_n_triangles; glw::GLint m_page_size; glw::GLuint m_po; glw::GLuint m_qo; glw::GLuint m_sparse_bo; unsigned int m_sparse_bo_size; unsigned int m_sparse_bo_size_rounded; tcu::TestContext& m_testCtx; glw::GLuint m_vao; }; /** Implements test case i for the test 2: * * i. Verify a SSBO, holding an unsized array, accessed from a compute shader, * contains anticipated values. Each CS invocation should only fetch * a single invocation-specific value. If the value is found correct, it * should increment it. * * The test passes if all values accessed by the CS invocations are found * valid after the dispatch call. * * Make sure to test three scenarios: * * a) All values come from the committed memory pages. * b) Use the same memory page commitment layout as proposed in b2. Verify * only those values, which were available to the compute shader. * c) None of the value exposed via SSBO are backed by physical memory. * In this case, we do not really care about the outputs of the CS. * We only need to ensure that GL (or the GPU) does not crash. */ class SSBOStorageTestCase : public BufferStorageTestCase { public: /* Public methods */ SSBOStorageTestCase(const glw::Functions& gl, tcu::TestContext& testContext, glw::GLint page_size); /* BufferStorageTestCase implementation */ void deinitTestCaseGlobal(); void deinitTestCaseIteration(); bool execute(glw::GLuint sparse_bo_storage_flags); bool initTestCaseGlobal(); bool initTestCaseIteration(glw::GLuint sparse_bo); const char* getName() { return "case i"; } private: /* Private fields */ const glw::Functions& m_gl; glw::GLuint m_helper_bo; /* holds m_sparse_bo_size bytes */ glw::GLint m_page_size; glw::GLuint m_po; const unsigned int m_po_local_wg_size; glw::GLuint m_result_bo; glw::GLuint m_sparse_bo; unsigned int m_sparse_bo_size; unsigned int m_sparse_bo_size_rounded; unsigned int* m_ssbo_data; /* holds m_sparse_bo_size bytes */ tcu::TestContext& m_testCtx; }; /** Implements test cases b1-b2 for the test 2: * * b1. Use a sparse buffer as a target for separate & interleaved transform * feed-back (in separate iterations). A sufficient number of pages should * have been committed prior to issuing any draw call. * * The vertex shader should output vertex ID & instance ID data to two * different output variables captured by the TF. * * The test should only pass if the generated output is correct. * * For the purpose of this test, use the following draw call types: * * * regular * * regular indirect * * regular indirect multi * * regular instanced * * regular instanced + base instance * * regular multi * * indexed * * indexed indirect * * indexed indirect multi * * indexed multi * * indexed multi + base vertex * * indexed + base vertex * * indexed + base vertex + base instance * * instanced indexed * * instanced indexed + base vertex * * instanced indexed + base vertex + base instance * * * b2. Follow the same approach as described for b1. However, the commitment * state of memory pages used for the TF process should be laid out in * the following order: * * 1st page: committed * 2nd page: NOT committed * ... * (2N) -th page: committed * (2N+1)-th page: NOT committed * * Make sure to use at least 4 memory pages in this test case. * * Execute the test as described in b1, and make sure the results stored * in the committed pages used by the TF process holds valid result data. */ class TransformFeedbackBufferStorageTestCase : public BufferStorageTestCase { public: /* Type definitions */ enum _draw_call { /* glDrawElements() */ DRAW_CALL_INDEXED, /* glDrawElementsBaseVertex() */ DRAW_CALL_INDEXED_BASE_VERTEX, /* glDrawElementsIndirect() */ DRAW_CALL_INDEXED_INDIRECT, /* glMultiDrawElementIndirect() */ DRAW_CALL_INDEXED_INDIRECT_MULTI, /* glMultiDrawElements() */ DRAW_CALL_INDEXED_MULTI, /* glMultiDrawElementsBaseVertex() */ DRAW_CALL_INDEXED_MULTI_BASE_VERTEX, /* glDrawElementsInstanced() */ DRAW_CALL_INSTANCED_INDEXED, /* glDrawElementsInstancedBaseVertex() */ DRAW_CALL_INSTANCED_INDEXED_BASE_VERTEX, /* glDrawElementsInstancedBaseVertexBaseInstance() */ DRAW_CALL_INSTANCED_INDEXED_BASE_VERTEX_BASE_INSTANCE, /* glDrawArrays() */ DRAW_CALL_REGULAR, /* glDrawArraysIndirect() */ DRAW_CALL_REGULAR_INDIRECT, /* glMultiDrawArraysIndirect() */ DRAW_CALL_REGULAR_INDIRECT_MULTI, /* glDrawArraysInstanced() */ DRAW_CALL_REGULAR_INSTANCED, /* glDrawArraysInstancedBaseInstance() */ DRAW_CALL_REGULAR_INSTANCED_BASE_INSTANCE, /* glMultiDrawArrays() */ DRAW_CALL_REGULAR_MULTI, /* Always last */ DRAW_CALL_COUNT }; /* Public methods */ TransformFeedbackBufferStorageTestCase(const glw::Functions& gl, tcu::TestContext& testContext, glw::GLint page_size, bool all_pages_committed); /* BufferStorageTestCase implementation */ void deinitTestCaseGlobal(); bool execute(glw::GLuint sparse_bo_storage_flags); bool initTestCaseGlobal(); bool initTestCaseIteration(glw::GLuint sparse_bo); const char* getName() { return (m_all_pages_committed) ? "case b1" : "case b2"; } private: /* Private methods */ const char* getDrawCallTypeString(_draw_call draw_call); void initDataBO(); void initTestData(); /* Private fields */ bool m_all_pages_committed; glw::GLuint m_data_bo; unsigned int m_data_bo_index_data_offset; unsigned int m_data_bo_indexed_indirect_arg_offset; unsigned int m_data_bo_indexed_mdi_arg_offset; unsigned int m_data_bo_regular_indirect_arg_offset; unsigned int m_data_bo_regular_mdi_arg_offset; glw::GLuint m_data_bo_size; const unsigned int m_draw_call_baseInstance; const unsigned int m_draw_call_baseVertex; const unsigned int m_draw_call_first; const unsigned int m_draw_call_firstIndex; const glw::Functions& m_gl; glw::GLuint m_helper_bo; /* of m_result_bo_size size */ glw::GLuint* m_index_data; glw::GLuint m_index_data_size; glw::GLuint* m_indirect_arg_data; glw::GLuint m_indirect_arg_data_size; const unsigned int m_min_memory_page_span; glw::GLint m_multidrawcall_basevertex[2]; glw::GLsizei m_multidrawcall_count[2]; unsigned int m_multidrawcall_drawcount; glw::GLint m_multidrawcall_first[2]; glw::GLvoid* m_multidrawcall_index[2]; unsigned int m_multidrawcall_primcount; const unsigned int m_n_instances_to_test; unsigned int m_n_vertices_per_instance; glw::GLint m_page_size; glw::GLuint m_po_ia; /* interleave attribs TF */ glw::GLuint m_po_sa; /* separate attribs TF */ glw::GLuint m_result_bo; glw::GLuint m_result_bo_size; glw::GLuint m_result_bo_size_rounded; tcu::TestContext& m_testCtx; glw::GLuint m_vao; }; /** Implements test case j for the test 2: * * j. Verify an UBO, backed by a sparse buffer, accessed from a vertex shader, * holds values as expected. Each VS invocation should only check * an invocation-specific arrayed member item and set gl_Position to * vec4(1.0), if the retrieved value is valid. * * Make sure to test three scenarios as described for case i). */ class UniformBufferStorageTestCase : public BufferStorageTestCase { public: /* Public methods */ UniformBufferStorageTestCase(const glw::Functions& gl, tcu::TestContext& testContext, glw::GLint page_size); /* BufferStorageTestCase implementation */ void deinitTestCaseGlobal(); void deinitTestCaseIteration(); bool execute(glw::GLuint sparse_bo_storage_flags); bool initTestCaseGlobal(); bool initTestCaseIteration(glw::GLuint sparse_bo); const char* getName() { return "case j"; } private: /* Private fields */ const glw::Functions& m_gl; glw::GLint m_gl_uniform_buffer_offset_alignment_value; glw::GLuint m_helper_bo; const unsigned int m_n_pages_to_use; unsigned int m_n_ubo_uints; glw::GLint m_page_size; glw::GLuint m_po; glw::GLuint m_sparse_bo; unsigned int m_sparse_bo_data_size; unsigned int m_sparse_bo_data_start_offset; unsigned int m_sparse_bo_size; unsigned int m_sparse_bo_size_rounded; tcu::TestContext& m_testCtx; glw::GLuint m_tf_bo; unsigned char* m_ubo_data; glw::GLuint m_vao; }; /** Implements conformance test 2 from the test specification: * * 2. Make sure glBufferStorage() accepts the new GL_SPARSE_STORAGE_BIT_ARB flag * in all valid flag combinations. For each such combination, allocate * a sparse buffer of 1GB size and verify the following test cases work as * expected. After all tests have been run for a particular flag combination, * the sparse buffer should be deleted, and a new sparse buffer should be * created, if there are any outstanding flag combinations. * * Test cases, whose verification behavior is incompatible with * the requested flag combination should skip the validation part: * * (for test case descriptions, please check test case class prototypes) */ class BufferStorageTest : public deqp::TestCase { public: /* Public methods */ BufferStorageTest(deqp::Context& context); void deinit(); void init(); tcu::TestNode::IterateResult iterate(); private: /* Private type definitions */ typedef std::vector TestCasesVector; typedef TestCasesVector::const_iterator TestCasesVectorConstIterator; typedef TestCasesVector::iterator TestCasesVectorIterator; /* Private methods */ void initTestCases(); /* Private members */ glw::GLuint m_sparse_bo; TestCasesVector m_testCases; }; /** Test group which encapsulates all sparse buffer conformance tests */ class SparseBufferTests : public deqp::TestCaseGroup { public: /* Public methods */ SparseBufferTests(deqp::Context& context); void init(); private: SparseBufferTests(const SparseBufferTests& other); SparseBufferTests& operator=(const SparseBufferTests& other); }; } /* glcts namespace */ #endif // _GL4CSPARSEBUFFERTESTS_HPP