| #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 <vector> |
| |
| 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 <target> is |
| * set to GL_INTERLEAVED_ATTRIBS. |
| * |
| * * Verify glBufferStorage() throws a GL_INVALID_VALUE error if <flags> 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 <offset> |
| * 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 <size> |
| * 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 <offset> is |
| * set to -1, but all other arguments are valid. |
| * |
| * * Verify glBufferPageCommitmentARB() returns GL_INVALID_VALUE if <size> 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 <offset> is set to 0 and <size> |
| * 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 <offset> is set to |
| * GL_SPARSE_BUFFER_PAGE_SIZE_ARB * 1 and <size> 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<BufferStorageTestCase*> 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 |