| /*------------------------------------------------------------------------- |
| * drawElements Quality Program OpenGL ES 3.1 Module |
| * ------------------------------------------------- |
| * |
| * Copyright 2014 The Android Open Source Project |
| * |
| * Licensed under the Apache License, Version 2.0 (the "License"); |
| * you may not use this file except in compliance with the License. |
| * You may obtain a copy of the License at |
| * |
| * http://www.apache.org/licenses/LICENSE-2.0 |
| * |
| * Unless required by applicable law or agreed to in writing, software |
| * distributed under the License is distributed on an "AS IS" BASIS, |
| * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. |
| * See the License for the specific language governing permissions and |
| * limitations under the License. |
| * |
| *//*! |
| * \file |
| * \brief Vertex attribute binding tests. |
| *//*--------------------------------------------------------------------*/ |
| |
| #include "es31fVertexAttributeBindingTests.hpp" |
| #include "tcuRenderTarget.hpp" |
| #include "tcuSurface.hpp" |
| #include "gluCallLogWrapper.hpp" |
| #include "gluRenderContext.hpp" |
| #include "gluPixelTransfer.hpp" |
| #include "gluShaderProgram.hpp" |
| #include "gluObjectWrapper.hpp" |
| #include "gluStrUtil.hpp" |
| #include "glwFunctions.hpp" |
| #include "glwEnums.hpp" |
| #include "deStringUtil.hpp" |
| #include "deInt32.h" |
| |
| namespace deqp |
| { |
| namespace gles31 |
| { |
| namespace Functional |
| { |
| namespace |
| { |
| |
| static const char* const s_colorFragmentShader = "#version 310 es\n" |
| "in mediump vec4 v_color;\n" |
| "layout(location = 0) out mediump vec4 fragColor;\n" |
| "void main (void)\n" |
| "{\n" |
| " fragColor = v_color;\n" |
| "}\n"; |
| |
| static const char* const s_positionColorShader = "#version 310 es\n" |
| "in highp vec4 a_position;\n" |
| "in highp vec4 a_color;\n" |
| "out highp vec4 v_color;\n" |
| "void main (void)\n" |
| "{\n" |
| " gl_Position = a_position;\n" |
| " v_color = a_color;\n" |
| "}\n"; |
| |
| static const char* const s_positionColorOffsetShader = "#version 310 es\n" |
| "in highp vec4 a_position;\n" |
| "in highp vec4 a_offset;\n" |
| "in highp vec4 a_color;\n" |
| "out highp vec4 v_color;\n" |
| "void main (void)\n" |
| "{\n" |
| " gl_Position = a_position + a_offset;\n" |
| " v_color = a_color;\n" |
| "}\n"; |
| |
| // Verifies image contains only yellow or greeen, or a linear combination |
| // of these colors. |
| static bool verifyImageYellowGreen (const tcu::Surface& image, tcu::TestLog& log, bool logImageOnSuccess) |
| { |
| using tcu::TestLog; |
| |
| const int colorThreshold = 20; |
| |
| tcu::Surface error (image.getWidth(), image.getHeight()); |
| bool isOk = true; |
| |
| log << TestLog::Message << "Verifying image contents." << TestLog::EndMessage; |
| |
| for (int y = 0; y < image.getHeight(); y++) |
| for (int x = 0; x < image.getWidth(); x++) |
| { |
| const tcu::RGBA pixel = image.getPixel(x, y); |
| bool pixelOk = true; |
| |
| // Any pixel with !(G ~= 255) is faulty (not a linear combinations of green and yellow) |
| if (de::abs(pixel.getGreen() - 255) > colorThreshold) |
| pixelOk = false; |
| |
| // Any pixel with !(B ~= 0) is faulty (not a linear combinations of green and yellow) |
| if (de::abs(pixel.getBlue() - 0) > colorThreshold) |
| pixelOk = false; |
| |
| error.setPixel(x, y, (pixelOk) ? (tcu::RGBA(0, 255, 0, 255)) : (tcu::RGBA(255, 0, 0, 255))); |
| isOk = isOk && pixelOk; |
| } |
| |
| if (!isOk) |
| { |
| log << TestLog::Message << "Image verification failed." << TestLog::EndMessage; |
| log << TestLog::ImageSet("Verfication result", "Result of rendering") |
| << TestLog::Image("Result", "Result", image) |
| << TestLog::Image("ErrorMask", "Error mask", error) |
| << TestLog::EndImageSet; |
| } |
| else |
| { |
| log << TestLog::Message << "Image verification passed." << TestLog::EndMessage; |
| |
| if (logImageOnSuccess) |
| log << TestLog::ImageSet("Verfication result", "Result of rendering") |
| << TestLog::Image("Result", "Result", image) |
| << TestLog::EndImageSet; |
| } |
| |
| return isOk; |
| } |
| |
| class BindingRenderCase : public TestCase |
| { |
| public: |
| enum |
| { |
| TEST_RENDER_SIZE = 64 |
| }; |
| |
| BindingRenderCase (Context& ctx, const char* name, const char* desc, bool unalignedData); |
| virtual ~BindingRenderCase (void); |
| |
| virtual void init (void); |
| virtual void deinit (void); |
| IterateResult iterate (void); |
| |
| private: |
| virtual void renderTo (tcu::Surface& dst) = 0; |
| virtual void createBuffers (void) = 0; |
| virtual void createShader (void) = 0; |
| |
| protected: |
| const bool m_unalignedData; |
| glw::GLuint m_vao; |
| glu::ShaderProgram* m_program; |
| }; |
| |
| BindingRenderCase::BindingRenderCase (Context& ctx, const char* name, const char* desc, bool unalignedData) |
| : TestCase (ctx, name, desc) |
| , m_unalignedData (unalignedData) |
| , m_vao (0) |
| , m_program (DE_NULL) |
| { |
| } |
| |
| BindingRenderCase::~BindingRenderCase (void) |
| { |
| deinit(); |
| } |
| |
| void BindingRenderCase::init (void) |
| { |
| // check requirements |
| if (m_context.getRenderTarget().getWidth() < TEST_RENDER_SIZE || m_context.getRenderTarget().getHeight() < TEST_RENDER_SIZE) |
| throw tcu::NotSupportedError("Test requires at least " + de::toString<int>(TEST_RENDER_SIZE) + "x" + de::toString<int>(TEST_RENDER_SIZE) + " render target"); |
| |
| // resources |
| m_context.getRenderContext().getFunctions().genVertexArrays(1, &m_vao); |
| if (m_context.getRenderContext().getFunctions().getError() != GL_NO_ERROR) |
| throw tcu::TestError("could not gen vao"); |
| |
| createBuffers(); |
| createShader(); |
| } |
| |
| void BindingRenderCase::deinit (void) |
| { |
| if (m_vao) |
| { |
| m_context.getRenderContext().getFunctions().deleteVertexArrays(1, &m_vao); |
| m_vao = 0; |
| } |
| |
| delete m_program; |
| m_program = DE_NULL; |
| } |
| |
| BindingRenderCase::IterateResult BindingRenderCase::iterate (void) |
| { |
| tcu::Surface surface(TEST_RENDER_SIZE, TEST_RENDER_SIZE); |
| |
| // draw pattern |
| |
| renderTo(surface); |
| |
| // verify results |
| |
| if (verifyImageYellowGreen(surface, m_testCtx.getLog(), false)) |
| m_testCtx.setTestResult(QP_TEST_RESULT_PASS, "Pass"); |
| else if (m_unalignedData) |
| m_testCtx.setTestResult(QP_TEST_RESULT_COMPATIBILITY_WARNING, "Failed to draw with unaligned data"); |
| else |
| m_testCtx.setTestResult(QP_TEST_RESULT_FAIL, "Image verification failed"); |
| |
| return STOP; |
| } |
| |
| class SingleBindingCase : public BindingRenderCase |
| { |
| public: |
| |
| enum CaseFlag |
| { |
| FLAG_ATTRIB_UNALIGNED = (1<<0), // !< unalign attributes with relativeOffset |
| FLAG_ATTRIB_ALIGNED = (1<<1), // !< align attributes with relativeOffset to the buffer begin (and not buffer offset) |
| FLAG_ATTRIBS_MULTIPLE_ELEMS = (1<<2), // !< use multiple attribute elements |
| FLAG_ATTRIBS_SHARED_ELEMS = (1<<3), // !< use multiple shared attribute elements. xyzw & rgba stored as (x, y, zr, wg, b, a) |
| |
| FLAG_BUF_ALIGNED_OFFSET = (1<<4), // !< use aligned offset to the buffer object |
| FLAG_BUF_UNALIGNED_OFFSET = (1<<5), // !< use unaligned offset to the buffer object |
| FLAG_BUF_UNALIGNED_STRIDE = (1<<6), // !< unalign buffer elements |
| }; |
| SingleBindingCase (Context& ctx, const char* name, int flags); |
| ~SingleBindingCase (void); |
| |
| void init (void); |
| void deinit (void); |
| |
| private: |
| struct TestSpec |
| { |
| int bufferOffset; |
| int bufferStride; |
| int positionAttrOffset; |
| int colorAttrOffset; |
| bool hasColorAttr; |
| }; |
| |
| enum |
| { |
| GRID_SIZE = 20 |
| }; |
| |
| void renderTo (tcu::Surface& dst); |
| |
| static TestSpec genTestSpec (int flags); |
| static std::string genTestDescription (int flags); |
| static bool isDataUnaligned (int flags); |
| |
| void createBuffers (void); |
| void createShader (void); |
| std::string genVertexSource (void); |
| |
| const TestSpec m_spec; |
| glw::GLuint m_buf; |
| }; |
| |
| SingleBindingCase::SingleBindingCase (Context& ctx, const char* name, int flags) |
| : BindingRenderCase (ctx, name, genTestDescription(flags).c_str(), isDataUnaligned(flags)) |
| , m_spec (genTestSpec(flags)) |
| , m_buf (0) |
| { |
| DE_ASSERT(!((flags & FLAG_ATTRIB_UNALIGNED) && (flags & FLAG_ATTRIB_ALIGNED))); |
| DE_ASSERT(!((flags & FLAG_ATTRIB_ALIGNED) && (flags & FLAG_BUF_UNALIGNED_STRIDE))); |
| |
| DE_ASSERT(!isDataUnaligned(flags)); |
| } |
| |
| SingleBindingCase::~SingleBindingCase (void) |
| { |
| deinit(); |
| } |
| |
| void SingleBindingCase::init (void) |
| { |
| // log what we are trying to do |
| |
| m_testCtx.getLog() << tcu::TestLog::Message |
| << "Rendering " << (int)GRID_SIZE << "x" << (int)GRID_SIZE << " grid.\n" |
| << "Buffer format:\n" |
| << " bufferOffset: " << m_spec.bufferOffset << "\n" |
| << " bufferStride: " << m_spec.bufferStride << "\n" |
| << "Vertex position format:\n" |
| << " type: float4\n" |
| << " offset: " << m_spec.positionAttrOffset << "\n" |
| << " total offset: " << m_spec.bufferOffset + m_spec.positionAttrOffset << "\n" |
| << tcu::TestLog::EndMessage; |
| |
| if (m_spec.hasColorAttr) |
| m_testCtx.getLog() << tcu::TestLog::Message |
| << "Color:\n" |
| << " type: float4\n" |
| << " offset: " << m_spec.colorAttrOffset << "\n" |
| << " total offset: " << m_spec.bufferOffset + m_spec.colorAttrOffset << "\n" |
| << tcu::TestLog::EndMessage; |
| // init |
| |
| BindingRenderCase::init(); |
| } |
| |
| void SingleBindingCase::deinit (void) |
| { |
| if (m_buf) |
| { |
| m_context.getRenderContext().getFunctions().deleteBuffers(1, &m_buf); |
| m_buf = 0; |
| } |
| |
| BindingRenderCase::deinit(); |
| } |
| |
| void SingleBindingCase::renderTo (tcu::Surface& dst) |
| { |
| glu::CallLogWrapper gl (m_context.getRenderContext().getFunctions(), m_testCtx.getLog()); |
| const int positionLoc = gl.glGetAttribLocation(m_program->getProgram(), "a_position"); |
| const int colorLoc = gl.glGetAttribLocation(m_program->getProgram(), "a_color"); |
| const int colorUniformLoc = gl.glGetUniformLocation(m_program->getProgram(), "u_color"); |
| |
| gl.enableLogging(true); |
| |
| gl.glClearColor(0.0f, 0.0f, 0.0f, 0.0f); |
| gl.glClear(GL_COLOR_BUFFER_BIT); |
| gl.glViewport(0, 0, dst.getWidth(), dst.getHeight()); |
| gl.glBindVertexArray(m_vao); |
| GLU_EXPECT_NO_ERROR(gl.glGetError(), "set vao"); |
| |
| gl.glUseProgram(m_program->getProgram()); |
| GLU_EXPECT_NO_ERROR(gl.glGetError(), "use program"); |
| |
| if (m_spec.hasColorAttr) |
| { |
| gl.glBindVertexBuffer(3, m_buf, m_spec.bufferOffset, m_spec.bufferStride); |
| |
| gl.glVertexAttribBinding(positionLoc, 3); |
| gl.glVertexAttribFormat(positionLoc, 4, GL_FLOAT, GL_FALSE, m_spec.positionAttrOffset); |
| gl.glEnableVertexAttribArray(positionLoc); |
| |
| gl.glVertexAttribBinding(colorLoc, 3); |
| gl.glVertexAttribFormat(colorLoc, 4, GL_FLOAT, GL_FALSE, m_spec.colorAttrOffset); |
| gl.glEnableVertexAttribArray(colorLoc); |
| |
| GLU_EXPECT_NO_ERROR(gl.glGetError(), "set va"); |
| |
| gl.glDrawArrays(GL_TRIANGLES, 0, GRID_SIZE*GRID_SIZE*6); |
| GLU_EXPECT_NO_ERROR(gl.glGetError(), "draw"); |
| } |
| else |
| { |
| gl.glBindVertexBuffer(3, m_buf, m_spec.bufferOffset, m_spec.bufferStride); |
| gl.glVertexAttribBinding(positionLoc, 3); |
| gl.glVertexAttribFormat(positionLoc, 4, GL_FLOAT, GL_FALSE, m_spec.positionAttrOffset); |
| gl.glEnableVertexAttribArray(positionLoc); |
| |
| GLU_EXPECT_NO_ERROR(gl.glGetError(), "set va"); |
| gl.glUniform4f(colorUniformLoc, 0.0f, 1.0f, 0.0f, 1.0f); |
| |
| gl.glDrawArrays(GL_TRIANGLES, 0, GRID_SIZE*GRID_SIZE*6); |
| GLU_EXPECT_NO_ERROR(gl.glGetError(), "draw"); |
| } |
| |
| gl.glFinish(); |
| gl.glBindVertexArray(0); |
| gl.glUseProgram(0); |
| GLU_EXPECT_NO_ERROR(gl.glGetError(), "clean"); |
| |
| glu::readPixels(m_context.getRenderContext(), 0, 0, dst.getAccess()); |
| } |
| |
| SingleBindingCase::TestSpec SingleBindingCase::genTestSpec (int flags) |
| { |
| const int datumSize = 4; |
| const int bufferOffset = (flags & FLAG_BUF_ALIGNED_OFFSET) ? (32) : (flags & FLAG_BUF_UNALIGNED_OFFSET) ? (19) : (0); |
| const int attrBufAlignment = ((bufferOffset % datumSize) == 0) ? (0) : (datumSize - (bufferOffset % datumSize)); |
| const int positionAttrOffset = (flags & FLAG_ATTRIB_UNALIGNED) ? (3) : (flags & FLAG_ATTRIB_ALIGNED) ? (attrBufAlignment) : (0); |
| const bool hasColorAttr = (flags & FLAG_ATTRIBS_SHARED_ELEMS) || (flags & FLAG_ATTRIBS_MULTIPLE_ELEMS); |
| const int colorAttrOffset = (flags & FLAG_ATTRIBS_SHARED_ELEMS) ? (2 * datumSize) : (flags & FLAG_ATTRIBS_MULTIPLE_ELEMS) ? (4 * datumSize) : (-1); |
| |
| const int bufferStrideBase = de::max(positionAttrOffset + 4 * datumSize, colorAttrOffset + 4 * datumSize); |
| const int bufferStrideAlignment = ((bufferStrideBase % datumSize) == 0) ? (0) : (datumSize - (bufferStrideBase % datumSize)); |
| const int bufferStridePadding = ((flags & FLAG_BUF_UNALIGNED_STRIDE) && deIsAligned32(bufferStrideBase, datumSize)) ? (13) : (!(flags & FLAG_BUF_UNALIGNED_STRIDE) && !deIsAligned32(bufferStrideBase, datumSize)) ? (bufferStrideAlignment) : (0); |
| |
| TestSpec spec; |
| |
| spec.bufferOffset = bufferOffset; |
| spec.bufferStride = bufferStrideBase + bufferStridePadding; |
| spec.positionAttrOffset = positionAttrOffset; |
| spec.colorAttrOffset = colorAttrOffset; |
| spec.hasColorAttr = hasColorAttr; |
| |
| if (flags & FLAG_ATTRIB_UNALIGNED) |
| DE_ASSERT(!deIsAligned32(spec.bufferOffset + spec.positionAttrOffset, datumSize)); |
| else if (flags & FLAG_ATTRIB_ALIGNED) |
| DE_ASSERT(deIsAligned32(spec.bufferOffset + spec.positionAttrOffset, datumSize)); |
| |
| if (flags & FLAG_BUF_UNALIGNED_STRIDE) |
| DE_ASSERT(!deIsAligned32(spec.bufferStride, datumSize)); |
| else |
| DE_ASSERT(deIsAligned32(spec.bufferStride, datumSize)); |
| |
| return spec; |
| } |
| |
| std::string SingleBindingCase::genTestDescription (int flags) |
| { |
| std::ostringstream buf; |
| buf << "draw test pattern"; |
| |
| if (flags & FLAG_ATTRIB_UNALIGNED) |
| buf << ", attribute offset (unaligned)"; |
| if (flags & FLAG_ATTRIB_ALIGNED) |
| buf << ", attribute offset (aligned)"; |
| |
| if (flags & FLAG_ATTRIBS_MULTIPLE_ELEMS) |
| buf << ", 2 attributes"; |
| if (flags & FLAG_ATTRIBS_SHARED_ELEMS) |
| buf << ", 2 attributes (some components shared)"; |
| |
| if (flags & FLAG_BUF_ALIGNED_OFFSET) |
| buf << ", buffer offset aligned"; |
| if (flags & FLAG_BUF_UNALIGNED_OFFSET) |
| buf << ", buffer offset unaligned"; |
| if (flags & FLAG_BUF_UNALIGNED_STRIDE) |
| buf << ", buffer stride unaligned"; |
| |
| return buf.str(); |
| } |
| |
| bool SingleBindingCase::isDataUnaligned (int flags) |
| { |
| if (flags & FLAG_ATTRIB_UNALIGNED) |
| return true; |
| if (flags & FLAG_ATTRIB_ALIGNED) |
| return false; |
| |
| return (flags & FLAG_BUF_UNALIGNED_OFFSET) || (flags & FLAG_BUF_UNALIGNED_STRIDE); |
| } |
| |
| void SingleBindingCase::createBuffers (void) |
| { |
| const glw::Functions& gl = m_context.getRenderContext().getFunctions(); |
| std::vector<deUint8> dataBuf (m_spec.bufferOffset + m_spec.bufferStride * GRID_SIZE * GRID_SIZE * 6); |
| |
| // In interleaved mode color rg and position zw are the same. Select "good" values for r and g |
| const tcu::Vec4 colorA (0.0f, 1.0f, 0.0f, 1.0f); |
| const tcu::Vec4 colorB (0.5f, 1.0f, 0.0f, 1.0f); |
| |
| for (int y = 0; y < GRID_SIZE; ++y) |
| for (int x = 0; x < GRID_SIZE; ++x) |
| { |
| const tcu::Vec4& color = ((x + y) % 2 == 0) ? (colorA) : (colorB); |
| const tcu::Vec4 positions[6] = |
| { |
| tcu::Vec4(float(x+0) / float(GRID_SIZE) * 2.0f - 1.0f, float(y+0) / float(GRID_SIZE) * 2.0f - 1.0f, 0.0f, 1.0f), |
| tcu::Vec4(float(x+0) / float(GRID_SIZE) * 2.0f - 1.0f, float(y+1) / float(GRID_SIZE) * 2.0f - 1.0f, 0.0f, 1.0f), |
| tcu::Vec4(float(x+1) / float(GRID_SIZE) * 2.0f - 1.0f, float(y+1) / float(GRID_SIZE) * 2.0f - 1.0f, 0.0f, 1.0f), |
| tcu::Vec4(float(x+0) / float(GRID_SIZE) * 2.0f - 1.0f, float(y+0) / float(GRID_SIZE) * 2.0f - 1.0f, 0.0f, 1.0f), |
| tcu::Vec4(float(x+1) / float(GRID_SIZE) * 2.0f - 1.0f, float(y+1) / float(GRID_SIZE) * 2.0f - 1.0f, 0.0f, 1.0f), |
| tcu::Vec4(float(x+1) / float(GRID_SIZE) * 2.0f - 1.0f, float(y+0) / float(GRID_SIZE) * 2.0f - 1.0f, 0.0f, 1.0f), |
| }; |
| |
| // copy cell vertices to the buffer. |
| for (int v = 0; v < 6; ++v) |
| memcpy(&dataBuf[m_spec.bufferOffset + m_spec.positionAttrOffset + m_spec.bufferStride * ((y * GRID_SIZE + x) * 6 + v)], positions[v].getPtr(), sizeof(positions[v])); |
| |
| // copy color to buffer |
| if (m_spec.hasColorAttr) |
| for (int v = 0; v < 6; ++v) |
| memcpy(&dataBuf[m_spec.bufferOffset + m_spec.colorAttrOffset + m_spec.bufferStride * ((y * GRID_SIZE + x) * 6 + v)], color.getPtr(), sizeof(color)); |
| } |
| |
| gl.genBuffers(1, &m_buf); |
| gl.bindBuffer(GL_ARRAY_BUFFER, m_buf); |
| gl.bufferData(GL_ARRAY_BUFFER, (glw::GLsizeiptr)dataBuf.size(), &dataBuf[0], GL_STATIC_DRAW); |
| gl.bindBuffer(GL_ARRAY_BUFFER, 0); |
| |
| if (gl.getError() != GL_NO_ERROR) |
| throw tcu::TestError("could not init buffer"); |
| } |
| |
| void SingleBindingCase::createShader (void) |
| { |
| m_program = new glu::ShaderProgram(m_context.getRenderContext(), glu::ProgramSources() << glu::VertexSource(genVertexSource()) << glu::FragmentSource(s_colorFragmentShader)); |
| m_testCtx.getLog() << *m_program; |
| |
| if (!m_program->isOk()) |
| throw tcu::TestError("could not build shader"); |
| } |
| |
| std::string SingleBindingCase::genVertexSource (void) |
| { |
| const bool useUniformColor = !m_spec.hasColorAttr; |
| std::ostringstream buf; |
| |
| buf << "#version 310 es\n" |
| "in highp vec4 a_position;\n"; |
| |
| if (!useUniformColor) |
| buf << "in highp vec4 a_color;\n"; |
| else |
| buf << "uniform highp vec4 u_color;\n"; |
| |
| buf << "out highp vec4 v_color;\n" |
| "void main (void)\n" |
| "{\n" |
| " gl_Position = a_position;\n" |
| " v_color = " << ((useUniformColor) ? ("u_color") : ("a_color")) << ";\n" |
| "}\n"; |
| |
| return buf.str(); |
| } |
| |
| class MultipleBindingCase : public BindingRenderCase |
| { |
| public: |
| |
| enum CaseFlag |
| { |
| FLAG_ZERO_STRIDE = (1<<0), // !< set a buffer stride to zero |
| FLAG_INSTANCED = (1<<1), // !< set a buffer instance divisor to non-zero |
| FLAG_ALIASING_BUFFERS = (1<<2), // !< bind buffer to multiple binding points |
| }; |
| |
| MultipleBindingCase (Context& ctx, const char* name, int flags); |
| ~MultipleBindingCase (void); |
| |
| void init (void); |
| void deinit (void); |
| |
| private: |
| struct TestSpec |
| { |
| bool zeroStride; |
| bool instanced; |
| bool aliasingBuffers; |
| }; |
| |
| enum |
| { |
| GRID_SIZE = 20 |
| }; |
| |
| void renderTo (tcu::Surface& dst); |
| |
| TestSpec genTestSpec (int flags) const; |
| std::string genTestDescription (int flags) const; |
| void createBuffers (void); |
| void createShader (void); |
| |
| const TestSpec m_spec; |
| glw::GLuint m_primitiveBuf; |
| glw::GLuint m_colorOffsetBuf; |
| }; |
| |
| MultipleBindingCase::MultipleBindingCase (Context& ctx, const char* name, int flags) |
| : BindingRenderCase (ctx, name, genTestDescription(flags).c_str(), false) |
| , m_spec (genTestSpec(flags)) |
| , m_primitiveBuf (0) |
| , m_colorOffsetBuf (0) |
| { |
| DE_ASSERT(!(m_spec.instanced && m_spec.zeroStride)); |
| } |
| |
| MultipleBindingCase::~MultipleBindingCase (void) |
| { |
| deinit(); |
| } |
| |
| void MultipleBindingCase::init (void) |
| { |
| BindingRenderCase::init(); |
| |
| // log what we are trying to do |
| |
| m_testCtx.getLog() << tcu::TestLog::Message |
| << "Rendering " << (int)GRID_SIZE << "x" << (int)GRID_SIZE << " grid.\n" |
| << "Vertex positions:\n" |
| << " binding point: 1\n" |
| << "Vertex offsets:\n" |
| << " binding point: 2\n" |
| << "Vertex colors:\n" |
| << " binding point: 2\n" |
| << "Binding point 1:\n" |
| << " buffer object: " << m_primitiveBuf << "\n" |
| << "Binding point 2:\n" |
| << " buffer object: " << ((m_spec.aliasingBuffers) ? (m_primitiveBuf) : (m_colorOffsetBuf)) << "\n" |
| << " instance divisor: " << ((m_spec.instanced) ? (1) : (0)) << "\n" |
| << " stride: " << ((m_spec.zeroStride) ? (0) : (4*4*2)) << "\n" |
| << tcu::TestLog::EndMessage; |
| } |
| |
| void MultipleBindingCase::deinit (void) |
| { |
| if (m_primitiveBuf) |
| { |
| m_context.getRenderContext().getFunctions().deleteBuffers(1, &m_primitiveBuf); |
| m_primitiveBuf = DE_NULL; |
| } |
| |
| if (m_colorOffsetBuf) |
| { |
| m_context.getRenderContext().getFunctions().deleteBuffers(1, &m_colorOffsetBuf); |
| m_colorOffsetBuf = DE_NULL; |
| } |
| |
| BindingRenderCase::deinit(); |
| } |
| |
| void MultipleBindingCase::renderTo (tcu::Surface& dst) |
| { |
| glu::CallLogWrapper gl (m_context.getRenderContext().getFunctions(), m_testCtx.getLog()); |
| const int positionLoc = gl.glGetAttribLocation(m_program->getProgram(), "a_position"); |
| const int colorLoc = gl.glGetAttribLocation(m_program->getProgram(), "a_color"); |
| const int offsetLoc = gl.glGetAttribLocation(m_program->getProgram(), "a_offset"); |
| |
| const int positionBinding = 1; |
| const int colorOffsetBinding = 2; |
| |
| gl.enableLogging(true); |
| |
| gl.glClearColor(0.0f, 0.0f, 0.0f, 0.0f); |
| gl.glClear(GL_COLOR_BUFFER_BIT); |
| gl.glViewport(0, 0, dst.getWidth(), dst.getHeight()); |
| gl.glBindVertexArray(m_vao); |
| GLU_EXPECT_NO_ERROR(gl.glGetError(), "set vao"); |
| |
| gl.glUseProgram(m_program->getProgram()); |
| GLU_EXPECT_NO_ERROR(gl.glGetError(), "use program"); |
| |
| // Setup format & binding |
| |
| gl.glEnableVertexAttribArray(positionLoc); |
| gl.glEnableVertexAttribArray(colorLoc); |
| gl.glEnableVertexAttribArray(offsetLoc); |
| |
| gl.glVertexAttribFormat(positionLoc, 4, GL_FLOAT, GL_FALSE, 0); |
| gl.glVertexAttribFormat(colorLoc, 4, GL_FLOAT, GL_FALSE, 0); |
| gl.glVertexAttribFormat(offsetLoc, 4, GL_FLOAT, GL_FALSE, sizeof(tcu::Vec4)); |
| |
| gl.glVertexAttribBinding(positionLoc, positionBinding); |
| gl.glVertexAttribBinding(colorLoc, colorOffsetBinding); |
| gl.glVertexAttribBinding(offsetLoc, colorOffsetBinding); |
| |
| GLU_EXPECT_NO_ERROR(gl.glGetError(), "setup attribs"); |
| |
| // setup binding points |
| |
| gl.glVertexBindingDivisor(positionBinding, 0); |
| gl.glBindVertexBuffer(positionBinding, m_primitiveBuf, 0, sizeof(tcu::Vec4)); |
| |
| { |
| const int stride = (m_spec.zeroStride) ? (0) : (2 * (int)sizeof(tcu::Vec4)); |
| const int offset = (!m_spec.aliasingBuffers) ? (0) : (m_spec.instanced) ? (6 * (int)sizeof(tcu::Vec4)) : (6 * GRID_SIZE * GRID_SIZE * (int)sizeof(tcu::Vec4)); |
| const glw::GLuint buffer = (m_spec.aliasingBuffers) ? (m_primitiveBuf) : (m_colorOffsetBuf); |
| const int divisor = (m_spec.instanced) ? (1) : (0); |
| |
| gl.glVertexBindingDivisor(colorOffsetBinding, divisor); |
| gl.glBindVertexBuffer(colorOffsetBinding, buffer, offset, (glw::GLsizei)stride); |
| } |
| |
| GLU_EXPECT_NO_ERROR(gl.glGetError(), "set binding points"); |
| |
| if (m_spec.instanced) |
| gl.glDrawArraysInstanced(GL_TRIANGLES, 0, 6, GRID_SIZE*GRID_SIZE); |
| else |
| gl.glDrawArrays(GL_TRIANGLES, 0, GRID_SIZE*GRID_SIZE*6); |
| GLU_EXPECT_NO_ERROR(gl.glGetError(), "draw"); |
| |
| gl.glFinish(); |
| gl.glBindVertexArray(0); |
| gl.glUseProgram(0); |
| GLU_EXPECT_NO_ERROR(gl.glGetError(), "clean"); |
| |
| glu::readPixels(m_context.getRenderContext(), 0, 0, dst.getAccess()); |
| } |
| |
| MultipleBindingCase::TestSpec MultipleBindingCase::genTestSpec (int flags) const |
| { |
| MultipleBindingCase::TestSpec spec; |
| |
| spec.zeroStride = !!(flags & FLAG_ZERO_STRIDE); |
| spec.instanced = !!(flags & FLAG_INSTANCED); |
| spec.aliasingBuffers = !!(flags & FLAG_ALIASING_BUFFERS); |
| |
| return spec; |
| } |
| |
| std::string MultipleBindingCase::genTestDescription (int flags) const |
| { |
| std::ostringstream buf; |
| buf << "draw test pattern"; |
| |
| if (flags & FLAG_ZERO_STRIDE) |
| buf << ", zero stride"; |
| if (flags & FLAG_INSTANCED) |
| buf << ", instanced binding point"; |
| if (flags & FLAG_ALIASING_BUFFERS) |
| buf << ", binding points share buffer object"; |
| |
| return buf.str(); |
| } |
| |
| void MultipleBindingCase::createBuffers (void) |
| { |
| const glw::Functions& gl = m_context.getRenderContext().getFunctions(); |
| const tcu::Vec4 green = tcu::Vec4(0.0f, 1.0f, 0.0f, 1.0f); |
| const tcu::Vec4 yellow = tcu::Vec4(1.0f, 1.0f, 0.0f, 1.0f); |
| |
| const int vertexDataSize = (m_spec.instanced) ? (6) : (6 * GRID_SIZE * GRID_SIZE); |
| const int offsetColorSize = (m_spec.zeroStride) ? (2) : (m_spec.instanced) ? (2 * GRID_SIZE * GRID_SIZE) : (2 * 6 * GRID_SIZE * GRID_SIZE); |
| const int primitiveBufSize = (m_spec.aliasingBuffers) ? (vertexDataSize + offsetColorSize) : (vertexDataSize); |
| const int colorOffsetBufSize = (m_spec.aliasingBuffers) ? (0) : (offsetColorSize); |
| |
| std::vector<tcu::Vec4> primitiveData (primitiveBufSize); |
| std::vector<tcu::Vec4> colorOffsetData (colorOffsetBufSize); |
| tcu::Vec4* colorOffsetWritePtr = DE_NULL; |
| |
| if (m_spec.aliasingBuffers) |
| { |
| if (m_spec.instanced) |
| colorOffsetWritePtr = &primitiveData[6]; |
| else |
| colorOffsetWritePtr = &primitiveData[GRID_SIZE*GRID_SIZE*6]; |
| } |
| else |
| colorOffsetWritePtr = &colorOffsetData[0]; |
| |
| // write vertex position |
| |
| if (m_spec.instanced) |
| { |
| // store single basic primitive |
| primitiveData[0] = tcu::Vec4(0.0f, 0.0f, 0.0f, 1.0f); |
| primitiveData[1] = tcu::Vec4(0.0f, 2.0f / float(GRID_SIZE), 0.0f, 1.0f); |
| primitiveData[2] = tcu::Vec4(2.0f / float(GRID_SIZE), 2.0f / float(GRID_SIZE), 0.0f, 1.0f); |
| primitiveData[3] = tcu::Vec4(0.0f, 0.0f, 0.0f, 1.0f); |
| primitiveData[4] = tcu::Vec4(2.0f / float(GRID_SIZE), 2.0f / float(GRID_SIZE), 0.0f, 1.0f); |
| primitiveData[5] = tcu::Vec4(2.0f / float(GRID_SIZE), 0.0f, 0.0f, 1.0f); |
| } |
| else |
| { |
| // store whole grid |
| for (int y = 0; y < GRID_SIZE; ++y) |
| for (int x = 0; x < GRID_SIZE; ++x) |
| { |
| primitiveData[(y * GRID_SIZE + x) * 6 + 0] = tcu::Vec4(float(x+0) / float(GRID_SIZE) * 2.0f - 1.0f, float(y+0) / float(GRID_SIZE) * 2.0f - 1.0f, 0.0f, 1.0f); |
| primitiveData[(y * GRID_SIZE + x) * 6 + 1] = tcu::Vec4(float(x+0) / float(GRID_SIZE) * 2.0f - 1.0f, float(y+1) / float(GRID_SIZE) * 2.0f - 1.0f, 0.0f, 1.0f); |
| primitiveData[(y * GRID_SIZE + x) * 6 + 2] = tcu::Vec4(float(x+1) / float(GRID_SIZE) * 2.0f - 1.0f, float(y+1) / float(GRID_SIZE) * 2.0f - 1.0f, 0.0f, 1.0f); |
| primitiveData[(y * GRID_SIZE + x) * 6 + 3] = tcu::Vec4(float(x+0) / float(GRID_SIZE) * 2.0f - 1.0f, float(y+0) / float(GRID_SIZE) * 2.0f - 1.0f, 0.0f, 1.0f); |
| primitiveData[(y * GRID_SIZE + x) * 6 + 4] = tcu::Vec4(float(x+1) / float(GRID_SIZE) * 2.0f - 1.0f, float(y+1) / float(GRID_SIZE) * 2.0f - 1.0f, 0.0f, 1.0f); |
| primitiveData[(y * GRID_SIZE + x) * 6 + 5] = tcu::Vec4(float(x+1) / float(GRID_SIZE) * 2.0f - 1.0f, float(y+0) / float(GRID_SIZE) * 2.0f - 1.0f, 0.0f, 1.0f); |
| } |
| } |
| |
| // store color&offset |
| |
| if (m_spec.zeroStride) |
| { |
| colorOffsetWritePtr[0] = green; |
| colorOffsetWritePtr[1] = tcu::Vec4(0.0f); |
| } |
| else if (m_spec.instanced) |
| { |
| for (int y = 0; y < GRID_SIZE; ++y) |
| for (int x = 0; x < GRID_SIZE; ++x) |
| { |
| const tcu::Vec4& color = ((x + y) % 2 == 0) ? (green) : (yellow); |
| |
| colorOffsetWritePtr[(y * GRID_SIZE + x) * 2 + 0] = color; |
| colorOffsetWritePtr[(y * GRID_SIZE + x) * 2 + 1] = tcu::Vec4(float(x) / float(GRID_SIZE) * 2.0f - 1.0f, float(y) / float(GRID_SIZE) * 2.0f - 1.0f, 0.0f, 0.0f); |
| } |
| } |
| else |
| { |
| for (int y = 0; y < GRID_SIZE; ++y) |
| for (int x = 0; x < GRID_SIZE; ++x) |
| for (int v = 0; v < 6; ++v) |
| { |
| const tcu::Vec4& color = ((x + y) % 2 == 0) ? (green) : (yellow); |
| |
| colorOffsetWritePtr[((y * GRID_SIZE + x) * 6 + v) * 2 + 0] = color; |
| colorOffsetWritePtr[((y * GRID_SIZE + x) * 6 + v) * 2 + 1] = tcu::Vec4(0.0f); |
| } |
| } |
| |
| // upload vertex data |
| |
| gl.genBuffers(1, &m_primitiveBuf); |
| gl.bindBuffer(GL_ARRAY_BUFFER, m_primitiveBuf); |
| gl.bufferData(GL_ARRAY_BUFFER, (int)(primitiveData.size() * sizeof(tcu::Vec4)), primitiveData[0].getPtr(), GL_STATIC_DRAW); |
| GLU_EXPECT_NO_ERROR(gl.getError(), "upload data"); |
| |
| if (!m_spec.aliasingBuffers) |
| { |
| // upload color & offset data |
| |
| gl.genBuffers(1, &m_colorOffsetBuf); |
| gl.bindBuffer(GL_ARRAY_BUFFER, m_colorOffsetBuf); |
| gl.bufferData(GL_ARRAY_BUFFER, (int)(colorOffsetData.size() * sizeof(tcu::Vec4)), colorOffsetData[0].getPtr(), GL_STATIC_DRAW); |
| GLU_EXPECT_NO_ERROR(gl.getError(), "upload colordata"); |
| } |
| } |
| |
| void MultipleBindingCase::createShader (void) |
| { |
| m_program = new glu::ShaderProgram(m_context.getRenderContext(), glu::ProgramSources() << glu::VertexSource(s_positionColorOffsetShader) << glu::FragmentSource(s_colorFragmentShader)); |
| m_testCtx.getLog() << *m_program; |
| |
| if (!m_program->isOk()) |
| throw tcu::TestError("could not build shader"); |
| } |
| |
| class MixedBindingCase : public BindingRenderCase |
| { |
| public: |
| |
| enum CaseType |
| { |
| CASE_BASIC = 0, |
| CASE_INSTANCED_BINDING, |
| CASE_INSTANCED_ATTRIB, |
| |
| CASE_LAST |
| }; |
| |
| MixedBindingCase (Context& ctx, const char* name, const char* desc, CaseType caseType); |
| ~MixedBindingCase (void); |
| |
| void init (void); |
| void deinit (void); |
| |
| private: |
| enum |
| { |
| GRID_SIZE = 20 |
| }; |
| |
| void renderTo (tcu::Surface& dst); |
| void createBuffers (void); |
| void createShader (void); |
| |
| const CaseType m_case; |
| glw::GLuint m_posBuffer; |
| glw::GLuint m_colorOffsetBuffer; |
| }; |
| |
| MixedBindingCase::MixedBindingCase (Context& ctx, const char* name, const char* desc, CaseType caseType) |
| : BindingRenderCase (ctx, name, desc, false) |
| , m_case (caseType) |
| , m_posBuffer (0) |
| , m_colorOffsetBuffer (0) |
| { |
| DE_ASSERT(caseType < CASE_LAST); |
| } |
| |
| MixedBindingCase::~MixedBindingCase (void) |
| { |
| deinit(); |
| } |
| |
| void MixedBindingCase::init (void) |
| { |
| BindingRenderCase::init(); |
| } |
| |
| void MixedBindingCase::deinit (void) |
| { |
| if (m_posBuffer) |
| { |
| m_context.getRenderContext().getFunctions().deleteBuffers(1, &m_posBuffer); |
| m_posBuffer = DE_NULL; |
| } |
| |
| if (m_colorOffsetBuffer) |
| { |
| m_context.getRenderContext().getFunctions().deleteBuffers(1, &m_colorOffsetBuffer); |
| m_colorOffsetBuffer = DE_NULL; |
| } |
| |
| BindingRenderCase::deinit(); |
| } |
| |
| void MixedBindingCase::renderTo (tcu::Surface& dst) |
| { |
| glu::CallLogWrapper gl (m_context.getRenderContext().getFunctions(), m_testCtx.getLog()); |
| const int positionLoc = gl.glGetAttribLocation(m_program->getProgram(), "a_position"); |
| const int colorLoc = gl.glGetAttribLocation(m_program->getProgram(), "a_color"); |
| const int offsetLoc = gl.glGetAttribLocation(m_program->getProgram(), "a_offset"); |
| |
| gl.enableLogging(true); |
| |
| gl.glClearColor(0.0f, 0.0f, 0.0f, 0.0f); |
| gl.glClear(GL_COLOR_BUFFER_BIT); |
| gl.glViewport(0, 0, dst.getWidth(), dst.getHeight()); |
| gl.glBindVertexArray(m_vao); |
| GLU_EXPECT_NO_ERROR(gl.glGetError(), "set vao"); |
| |
| gl.glUseProgram(m_program->getProgram()); |
| GLU_EXPECT_NO_ERROR(gl.glGetError(), "use program"); |
| |
| switch (m_case) |
| { |
| case CASE_BASIC: |
| { |
| // bind position using vertex_attrib_binding api |
| |
| gl.glBindVertexBuffer(positionLoc, m_posBuffer, 0, (glw::GLsizei)sizeof(tcu::Vec4)); |
| gl.glVertexAttribBinding(positionLoc, positionLoc); |
| gl.glVertexAttribFormat(positionLoc, 4, GL_FLOAT, GL_FALSE, 0); |
| GLU_EXPECT_NO_ERROR(gl.glGetError(), "set binding"); |
| |
| // bind color using old api |
| |
| gl.glBindBuffer(GL_ARRAY_BUFFER, m_colorOffsetBuffer); |
| gl.glVertexAttribPointer(colorLoc, 4, GL_FLOAT, GL_FALSE, glw::GLsizei(2 * sizeof(tcu::Vec4)), DE_NULL); |
| gl.glVertexAttribPointer(offsetLoc, 4, GL_FLOAT, GL_FALSE, glw::GLsizei(2 * sizeof(tcu::Vec4)), glu::BufferOffsetAsPointer(sizeof(tcu::Vec4))); |
| GLU_EXPECT_NO_ERROR(gl.glGetError(), "set va"); |
| |
| // draw |
| gl.glEnableVertexAttribArray(positionLoc); |
| gl.glEnableVertexAttribArray(colorLoc); |
| gl.glEnableVertexAttribArray(offsetLoc); |
| gl.glDrawArrays(GL_TRIANGLES, 0, 6*GRID_SIZE*GRID_SIZE); |
| break; |
| } |
| |
| case CASE_INSTANCED_BINDING: |
| { |
| // bind position using old api |
| gl.glBindBuffer(GL_ARRAY_BUFFER, m_posBuffer); |
| gl.glVertexAttribPointer(positionLoc, 4, GL_FLOAT, GL_FALSE, 0, DE_NULL); |
| GLU_EXPECT_NO_ERROR(gl.glGetError(), "set va"); |
| |
| // bind color using vertex_attrib_binding api |
| gl.glBindVertexBuffer(colorLoc, m_colorOffsetBuffer, 0, (glw::GLsizei)(2 * sizeof(tcu::Vec4))); |
| gl.glVertexBindingDivisor(colorLoc, 1); |
| |
| gl.glVertexAttribBinding(colorLoc, colorLoc); |
| gl.glVertexAttribBinding(offsetLoc, colorLoc); |
| |
| gl.glVertexAttribFormat(colorLoc, 4, GL_FLOAT, GL_FALSE, 0); |
| gl.glVertexAttribFormat(offsetLoc, 4, GL_FLOAT, GL_FALSE, sizeof(tcu::Vec4)); |
| |
| GLU_EXPECT_NO_ERROR(gl.glGetError(), "set binding"); |
| |
| // draw |
| gl.glEnableVertexAttribArray(positionLoc); |
| gl.glEnableVertexAttribArray(colorLoc); |
| gl.glEnableVertexAttribArray(offsetLoc); |
| gl.glDrawArraysInstanced(GL_TRIANGLES, 0, 6, GRID_SIZE*GRID_SIZE); |
| break; |
| } |
| |
| case CASE_INSTANCED_ATTRIB: |
| { |
| // bind position using vertex_attrib_binding api |
| gl.glBindVertexBuffer(positionLoc, m_posBuffer, 0, (glw::GLsizei)sizeof(tcu::Vec4)); |
| gl.glVertexAttribBinding(positionLoc, positionLoc); |
| gl.glVertexAttribFormat(positionLoc, 4, GL_FLOAT, GL_FALSE, 0); |
| GLU_EXPECT_NO_ERROR(gl.glGetError(), "set binding"); |
| |
| // bind color using old api |
| gl.glBindBuffer(GL_ARRAY_BUFFER, m_colorOffsetBuffer); |
| gl.glVertexAttribPointer(colorLoc, 4, GL_FLOAT, GL_FALSE, glw::GLsizei(2 * sizeof(tcu::Vec4)), DE_NULL); |
| gl.glVertexAttribPointer(offsetLoc, 4, GL_FLOAT, GL_FALSE, glw::GLsizei(2 * sizeof(tcu::Vec4)), glu::BufferOffsetAsPointer(sizeof(tcu::Vec4))); |
| gl.glVertexAttribDivisor(colorLoc, 1); |
| gl.glVertexAttribDivisor(offsetLoc, 1); |
| GLU_EXPECT_NO_ERROR(gl.glGetError(), "set va"); |
| |
| // draw |
| gl.glEnableVertexAttribArray(positionLoc); |
| gl.glEnableVertexAttribArray(colorLoc); |
| gl.glEnableVertexAttribArray(offsetLoc); |
| gl.glDrawArraysInstanced(GL_TRIANGLES, 0, 6, GRID_SIZE*GRID_SIZE); |
| break; |
| } |
| |
| default: |
| DE_ASSERT(DE_FALSE); |
| } |
| |
| gl.glFinish(); |
| gl.glBindVertexArray(0); |
| gl.glUseProgram(0); |
| GLU_EXPECT_NO_ERROR(gl.glGetError(), "clean"); |
| |
| glu::readPixels(m_context.getRenderContext(), 0, 0, dst.getAccess()); |
| } |
| |
| void MixedBindingCase::createBuffers (void) |
| { |
| const glw::Functions& gl = m_context.getRenderContext().getFunctions(); |
| const tcu::Vec4 green = tcu::Vec4(0.0f, 1.0f, 0.0f, 1.0f); |
| const tcu::Vec4 yellow = tcu::Vec4(1.0f, 1.0f, 0.0f, 1.0f); |
| |
| // draw grid. In instanced mode, each cell is an instance |
| const bool instanced = (m_case == CASE_INSTANCED_BINDING) || (m_case == CASE_INSTANCED_ATTRIB); |
| const int numCells = GRID_SIZE*GRID_SIZE; |
| const int numPositionCells = (instanced) ? (1) : (numCells); |
| const int numPositionElements = 6 * numPositionCells; |
| const int numInstanceElementsPerCell = (instanced) ? (1) : (6); |
| const int numColorOffsetElements = numInstanceElementsPerCell * numCells; |
| |
| std::vector<tcu::Vec4> positionData (numPositionElements); |
| std::vector<tcu::Vec4> colorOffsetData (2 * numColorOffsetElements); |
| |
| // positions |
| |
| for (int primNdx = 0; primNdx < numPositionCells; ++primNdx) |
| { |
| positionData[primNdx*6 + 0] = tcu::Vec4(0.0f, 0.0f, 0.0f, 1.0f); |
| positionData[primNdx*6 + 1] = tcu::Vec4(0.0f, 2.0f / float(GRID_SIZE), 0.0f, 1.0f); |
| positionData[primNdx*6 + 2] = tcu::Vec4(2.0f / float(GRID_SIZE), 2.0f / float(GRID_SIZE), 0.0f, 1.0f); |
| positionData[primNdx*6 + 3] = tcu::Vec4(0.0f, 0.0f, 0.0f, 1.0f); |
| positionData[primNdx*6 + 4] = tcu::Vec4(2.0f / float(GRID_SIZE), 2.0f / float(GRID_SIZE), 0.0f, 1.0f); |
| positionData[primNdx*6 + 5] = tcu::Vec4(2.0f / float(GRID_SIZE), 0.0f, 0.0f, 1.0f); |
| } |
| |
| // color & offset |
| |
| for (int y = 0; y < GRID_SIZE; ++y) |
| for (int x = 0; x < GRID_SIZE; ++x) |
| { |
| for (int v = 0; v < numInstanceElementsPerCell; ++v) |
| { |
| const tcu::Vec4& color = ((x + y) % 2 == 0) ? (green) : (yellow); |
| |
| colorOffsetData[((y * GRID_SIZE + x) * numInstanceElementsPerCell + v) * 2 + 0] = color; |
| colorOffsetData[((y * GRID_SIZE + x) * numInstanceElementsPerCell + v) * 2 + 1] = tcu::Vec4(float(x) / float(GRID_SIZE) * 2.0f - 1.0f, float(y) / float(GRID_SIZE) * 2.0f - 1.0f, 0.0f, 0.0f); |
| } |
| } |
| |
| // upload vertex data |
| |
| gl.genBuffers(1, &m_posBuffer); |
| gl.bindBuffer(GL_ARRAY_BUFFER, m_posBuffer); |
| gl.bufferData(GL_ARRAY_BUFFER, (int)(positionData.size() * sizeof(tcu::Vec4)), positionData[0].getPtr(), GL_STATIC_DRAW); |
| GLU_EXPECT_NO_ERROR(gl.getError(), "upload position data"); |
| |
| gl.genBuffers(1, &m_colorOffsetBuffer); |
| gl.bindBuffer(GL_ARRAY_BUFFER, m_colorOffsetBuffer); |
| gl.bufferData(GL_ARRAY_BUFFER, (int)(colorOffsetData.size() * sizeof(tcu::Vec4)), colorOffsetData[0].getPtr(), GL_STATIC_DRAW); |
| GLU_EXPECT_NO_ERROR(gl.getError(), "upload position data"); |
| } |
| |
| void MixedBindingCase::createShader (void) |
| { |
| m_program = new glu::ShaderProgram(m_context.getRenderContext(), glu::ProgramSources() << glu::VertexSource(s_positionColorOffsetShader) << glu::FragmentSource(s_colorFragmentShader)); |
| m_testCtx.getLog() << *m_program; |
| |
| if (!m_program->isOk()) |
| throw tcu::TestError("could not build shader"); |
| } |
| |
| class MixedApiCase : public BindingRenderCase |
| { |
| public: |
| |
| enum CaseType |
| { |
| CASE_CHANGE_BUFFER = 0, |
| CASE_CHANGE_BUFFER_OFFSET, |
| CASE_CHANGE_BUFFER_STRIDE, |
| CASE_CHANGE_BINDING_POINT, |
| |
| CASE_LAST |
| }; |
| |
| MixedApiCase (Context& ctx, const char* name, const char* desc, CaseType caseType); |
| ~MixedApiCase (void); |
| |
| void init (void); |
| void deinit (void); |
| |
| private: |
| enum |
| { |
| GRID_SIZE = 20 |
| }; |
| |
| void renderTo (tcu::Surface& dst); |
| void createBuffers (void); |
| void createShader (void); |
| |
| const CaseType m_case; |
| glw::GLuint m_buffer; |
| }; |
| |
| |
| MixedApiCase::MixedApiCase (Context& ctx, const char* name, const char* desc, CaseType caseType) |
| : BindingRenderCase (ctx, name, desc, false) |
| , m_case (caseType) |
| , m_buffer (0) |
| { |
| DE_ASSERT(caseType < CASE_LAST); |
| } |
| |
| MixedApiCase::~MixedApiCase (void) |
| { |
| deinit(); |
| } |
| |
| void MixedApiCase::init (void) |
| { |
| BindingRenderCase::init(); |
| } |
| |
| void MixedApiCase::deinit (void) |
| { |
| if (m_buffer) |
| { |
| m_context.getRenderContext().getFunctions().deleteBuffers(1, &m_buffer); |
| m_buffer = DE_NULL; |
| } |
| |
| BindingRenderCase::deinit(); |
| } |
| |
| void MixedApiCase::renderTo (tcu::Surface& dst) |
| { |
| glu::CallLogWrapper gl (m_context.getRenderContext().getFunctions(), m_testCtx.getLog()); |
| const int positionLoc = gl.glGetAttribLocation(m_program->getProgram(), "a_position"); |
| const int colorLoc = gl.glGetAttribLocation(m_program->getProgram(), "a_color"); |
| glu::Buffer unusedBuffer (m_context.getRenderContext()); |
| |
| gl.enableLogging(true); |
| |
| gl.glClearColor(0.0f, 0.0f, 0.0f, 0.0f); |
| gl.glClear(GL_COLOR_BUFFER_BIT); |
| gl.glViewport(0, 0, dst.getWidth(), dst.getHeight()); |
| gl.glBindVertexArray(m_vao); |
| GLU_EXPECT_NO_ERROR(gl.glGetError(), "set vao"); |
| |
| gl.glUseProgram(m_program->getProgram()); |
| GLU_EXPECT_NO_ERROR(gl.glGetError(), "use program"); |
| |
| switch (m_case) |
| { |
| case CASE_CHANGE_BUFFER: |
| { |
| // bind data using old api |
| |
| gl.glBindBuffer(GL_ARRAY_BUFFER, *unusedBuffer); |
| gl.glVertexAttribPointer(positionLoc, 4, GL_FLOAT, GL_FALSE, (glw::GLsizei)(2 * sizeof(tcu::Vec4)), (const deUint8*)DE_NULL); |
| gl.glVertexAttribPointer(colorLoc, 4, GL_FLOAT, GL_FALSE, (glw::GLsizei)(2 * sizeof(tcu::Vec4)), glu::BufferOffsetAsPointer(sizeof(tcu::Vec4))); |
| |
| // change buffer with vertex_attrib_binding |
| |
| gl.glBindVertexBuffer(positionLoc, m_buffer, 0, (glw::GLsizei)(2 * sizeof(tcu::Vec4))); |
| gl.glBindVertexBuffer(colorLoc, m_buffer, sizeof(tcu::Vec4), (glw::GLsizei)(2 * sizeof(tcu::Vec4))); |
| |
| GLU_EXPECT_NO_ERROR(gl.glGetError(), ""); |
| break; |
| } |
| |
| case CASE_CHANGE_BUFFER_OFFSET: |
| { |
| // bind data using old api |
| |
| gl.glBindBuffer(GL_ARRAY_BUFFER, m_buffer); |
| gl.glVertexAttribPointer(positionLoc, 4, GL_FLOAT, GL_FALSE, (glw::GLsizei)(2 * sizeof(tcu::Vec4)), (const deUint8*)DE_NULL); |
| gl.glVertexAttribPointer(colorLoc, 4, GL_FLOAT, GL_FALSE, (glw::GLsizei)(2 * sizeof(tcu::Vec4)), (const deUint8*)DE_NULL); |
| |
| // change buffer offset with vertex_attrib_binding |
| |
| gl.glBindVertexBuffer(positionLoc, m_buffer, 0, (glw::GLsizei)(2 * sizeof(tcu::Vec4))); |
| gl.glBindVertexBuffer(colorLoc, m_buffer, sizeof(tcu::Vec4), (glw::GLsizei)(2 * sizeof(tcu::Vec4))); |
| |
| GLU_EXPECT_NO_ERROR(gl.glGetError(), ""); |
| break; |
| } |
| |
| case CASE_CHANGE_BUFFER_STRIDE: |
| { |
| // bind data using old api |
| |
| gl.glBindBuffer(GL_ARRAY_BUFFER, m_buffer); |
| gl.glVertexAttribPointer(positionLoc, 4, GL_FLOAT, GL_FALSE, 8, (const deUint8*)DE_NULL); |
| gl.glVertexAttribPointer(colorLoc, 4, GL_FLOAT, GL_FALSE, 4, (const deUint8*)DE_NULL); |
| |
| // change buffer stride with vertex_attrib_binding |
| |
| gl.glBindVertexBuffer(positionLoc, m_buffer, 0, (glw::GLsizei)(2 * sizeof(tcu::Vec4))); |
| gl.glBindVertexBuffer(colorLoc, m_buffer, sizeof(tcu::Vec4), (glw::GLsizei)(2 * sizeof(tcu::Vec4))); |
| |
| GLU_EXPECT_NO_ERROR(gl.glGetError(), ""); |
| break; |
| } |
| |
| case CASE_CHANGE_BINDING_POINT: |
| { |
| const int maxUsedLocation = de::max(positionLoc, colorLoc); |
| const int bindingPoint1 = maxUsedLocation + 1; |
| const int bindingPoint2 = maxUsedLocation + 2; |
| |
| // bind data using old api |
| |
| gl.glBindBuffer(GL_ARRAY_BUFFER, m_buffer); |
| gl.glVertexAttribPointer(bindingPoint1, 4, GL_FLOAT, GL_FALSE, (glw::GLsizei)(2 * sizeof(tcu::Vec4)), (const deUint8*)DE_NULL); |
| gl.glVertexAttribPointer(bindingPoint2, 4, GL_FLOAT, GL_FALSE, (glw::GLsizei)(2 * sizeof(tcu::Vec4)), glu::BufferOffsetAsPointer(sizeof(tcu::Vec4))); |
| |
| // change buffer binding point with vertex_attrib_binding |
| |
| gl.glVertexAttribFormat(positionLoc, 4, GL_FLOAT, GL_FALSE, 0); |
| gl.glVertexAttribFormat(colorLoc, 4, GL_FLOAT, GL_FALSE, 0); |
| |
| gl.glVertexAttribBinding(positionLoc, bindingPoint1); |
| gl.glVertexAttribBinding(colorLoc, bindingPoint2); |
| |
| GLU_EXPECT_NO_ERROR(gl.glGetError(), ""); |
| break; |
| } |
| |
| default: |
| DE_ASSERT(DE_FALSE); |
| } |
| |
| // draw |
| gl.glEnableVertexAttribArray(positionLoc); |
| gl.glEnableVertexAttribArray(colorLoc); |
| gl.glDrawArrays(GL_TRIANGLES, 0, 6*GRID_SIZE*GRID_SIZE); |
| |
| gl.glFinish(); |
| gl.glBindVertexArray(0); |
| gl.glUseProgram(0); |
| GLU_EXPECT_NO_ERROR(gl.glGetError(), "clean"); |
| |
| glu::readPixels(m_context.getRenderContext(), 0, 0, dst.getAccess()); |
| } |
| |
| void MixedApiCase::createBuffers (void) |
| { |
| const tcu::Vec4 green = tcu::Vec4(0.0f, 1.0f, 0.0f, 1.0f); |
| const tcu::Vec4 yellow = tcu::Vec4(1.0f, 1.0f, 0.0f, 1.0f); |
| |
| const glw::Functions& gl = m_context.getRenderContext().getFunctions(); |
| std::vector<tcu::Vec4> vertexData (12 * GRID_SIZE * GRID_SIZE); |
| |
| for (int y = 0; y < GRID_SIZE; ++y) |
| for (int x = 0; x < GRID_SIZE; ++x) |
| { |
| const tcu::Vec4& color = ((x + y) % 2 == 0) ? (green) : (yellow); |
| |
| vertexData[(y * GRID_SIZE + x) * 12 + 0] = tcu::Vec4(float(x+0) / float(GRID_SIZE) * 2.0f - 1.0f, float(y+0) / float(GRID_SIZE) * 2.0f - 1.0f, 0.0f, 1.0f); |
| vertexData[(y * GRID_SIZE + x) * 12 + 1] = color; |
| vertexData[(y * GRID_SIZE + x) * 12 + 2] = tcu::Vec4(float(x+0) / float(GRID_SIZE) * 2.0f - 1.0f, float(y+1) / float(GRID_SIZE) * 2.0f - 1.0f, 0.0f, 1.0f); |
| vertexData[(y * GRID_SIZE + x) * 12 + 3] = color; |
| vertexData[(y * GRID_SIZE + x) * 12 + 4] = tcu::Vec4(float(x+1) / float(GRID_SIZE) * 2.0f - 1.0f, float(y+1) / float(GRID_SIZE) * 2.0f - 1.0f, 0.0f, 1.0f); |
| vertexData[(y * GRID_SIZE + x) * 12 + 5] = color; |
| vertexData[(y * GRID_SIZE + x) * 12 + 6] = tcu::Vec4(float(x+0) / float(GRID_SIZE) * 2.0f - 1.0f, float(y+0) / float(GRID_SIZE) * 2.0f - 1.0f, 0.0f, 1.0f); |
| vertexData[(y * GRID_SIZE + x) * 12 + 7] = color; |
| vertexData[(y * GRID_SIZE + x) * 12 + 8] = tcu::Vec4(float(x+1) / float(GRID_SIZE) * 2.0f - 1.0f, float(y+1) / float(GRID_SIZE) * 2.0f - 1.0f, 0.0f, 1.0f); |
| vertexData[(y * GRID_SIZE + x) * 12 + 9] = color; |
| vertexData[(y * GRID_SIZE + x) * 12 + 10] = tcu::Vec4(float(x+1) / float(GRID_SIZE) * 2.0f - 1.0f, float(y+0) / float(GRID_SIZE) * 2.0f - 1.0f, 0.0f, 1.0f); |
| vertexData[(y * GRID_SIZE + x) * 12 + 11] = color; |
| } |
| |
| // upload vertex data |
| |
| gl.genBuffers(1, &m_buffer); |
| gl.bindBuffer(GL_ARRAY_BUFFER, m_buffer); |
| gl.bufferData(GL_ARRAY_BUFFER, (int)(vertexData.size() * sizeof(tcu::Vec4)), vertexData[0].getPtr(), GL_STATIC_DRAW); |
| GLU_EXPECT_NO_ERROR(gl.getError(), "upload data"); |
| } |
| |
| void MixedApiCase::createShader (void) |
| { |
| m_program = new glu::ShaderProgram(m_context.getRenderContext(), glu::ProgramSources() << glu::VertexSource(s_positionColorShader) << glu::FragmentSource(s_colorFragmentShader)); |
| m_testCtx.getLog() << *m_program; |
| |
| if (!m_program->isOk()) |
| throw tcu::TestError("could not build shader"); |
| } |
| |
| class DefaultVAOCase : public TestCase |
| { |
| public: |
| enum CaseType |
| { |
| CASE_BIND_VERTEX_BUFFER, |
| CASE_VERTEX_ATTRIB_FORMAT, |
| CASE_VERTEX_ATTRIB_I_FORMAT, |
| CASE_VERTEX_ATTRIB_BINDING, |
| CASE_VERTEX_BINDING_DIVISOR, |
| |
| CASE_LAST |
| }; |
| |
| DefaultVAOCase (Context& ctx, const char* name, const char* desc, CaseType caseType); |
| ~DefaultVAOCase (void); |
| |
| IterateResult iterate (void); |
| |
| private: |
| const CaseType m_caseType; |
| }; |
| |
| DefaultVAOCase::DefaultVAOCase (Context& ctx, const char* name, const char* desc, CaseType caseType) |
| : TestCase (ctx, name, desc) |
| , m_caseType (caseType) |
| { |
| DE_ASSERT(caseType < CASE_LAST); |
| } |
| |
| DefaultVAOCase::~DefaultVAOCase (void) |
| { |
| } |
| |
| DefaultVAOCase::IterateResult DefaultVAOCase::iterate (void) |
| { |
| glw::GLenum error = 0; |
| glu::CallLogWrapper gl (m_context.getRenderContext().getFunctions(), m_context.getTestContext().getLog()); |
| |
| gl.enableLogging(true); |
| |
| switch (m_caseType) |
| { |
| case CASE_BIND_VERTEX_BUFFER: |
| { |
| glu::Buffer buffer(m_context.getRenderContext()); |
| gl.glBindVertexBuffer(0, *buffer, 0, 0); |
| break; |
| } |
| |
| case CASE_VERTEX_ATTRIB_FORMAT: |
| gl.glVertexAttribFormat(0, 4, GL_FLOAT, GL_FALSE, 0); |
| break; |
| |
| case CASE_VERTEX_ATTRIB_I_FORMAT: |
| gl.glVertexAttribIFormat(0, 4, GL_INT, 0); |
| break; |
| |
| case CASE_VERTEX_ATTRIB_BINDING: |
| gl.glVertexAttribBinding(0, 0); |
| break; |
| |
| case CASE_VERTEX_BINDING_DIVISOR: |
| gl.glVertexBindingDivisor(0, 1); |
| break; |
| |
| default: |
| DE_ASSERT(false); |
| } |
| |
| error = gl.glGetError(); |
| |
| if (error != GL_INVALID_OPERATION) |
| { |
| m_testCtx.getLog() << tcu::TestLog::Message << "ERROR! Expected GL_INVALID_OPERATION, got " << glu::getErrorStr(error) << tcu::TestLog::EndMessage; |
| m_testCtx.setTestResult(QP_TEST_RESULT_FAIL, "Invalid error"); |
| } |
| else |
| m_testCtx.setTestResult(QP_TEST_RESULT_PASS, "Pass"); |
| |
| return STOP; |
| } |
| |
| class BindToCreateCase : public TestCase |
| { |
| public: |
| BindToCreateCase (Context& ctx, const char* name, const char* desc); |
| ~BindToCreateCase (void); |
| |
| IterateResult iterate (void); |
| }; |
| |
| BindToCreateCase::BindToCreateCase (Context& ctx, const char* name, const char* desc) |
| : TestCase(ctx, name, desc) |
| { |
| } |
| |
| BindToCreateCase::~BindToCreateCase (void) |
| { |
| } |
| |
| BindToCreateCase::IterateResult BindToCreateCase::iterate (void) |
| { |
| glw::GLuint buffer = 0; |
| glw::GLenum error; |
| glu::CallLogWrapper gl (m_context.getRenderContext().getFunctions(), m_context.getTestContext().getLog()); |
| glu::VertexArray vao (m_context.getRenderContext()); |
| |
| gl.enableLogging(true); |
| |
| gl.glGenBuffers(1, &buffer); |
| gl.glDeleteBuffers(1, &buffer); |
| GLU_EXPECT_NO_ERROR(gl.glGetError(), ""); |
| |
| gl.glBindVertexArray(*vao); |
| gl.glBindVertexBuffer(0, buffer, 0, 0); |
| |
| error = gl.glGetError(); |
| |
| if (error != GL_INVALID_OPERATION) |
| { |
| m_testCtx.getLog() << tcu::TestLog::Message << "ERROR! Expected GL_INVALID_OPERATION, got " << glu::getErrorStr(error) << tcu::TestLog::EndMessage; |
| m_testCtx.setTestResult(QP_TEST_RESULT_FAIL, "Invalid error"); |
| } |
| else |
| m_testCtx.setTestResult(QP_TEST_RESULT_PASS, "Pass"); |
| |
| return STOP; |
| } |
| |
| class NegativeApiCase : public TestCase |
| { |
| public: |
| enum CaseType |
| { |
| CASE_LARGE_OFFSET, |
| CASE_LARGE_STRIDE, |
| CASE_NEGATIVE_STRIDE, |
| CASE_NEGATIVE_OFFSET, |
| CASE_INVALID_ATTR, |
| CASE_INVALID_BINDING, |
| |
| CASE_LAST |
| }; |
| NegativeApiCase (Context& ctx, const char* name, const char* desc, CaseType caseType); |
| ~NegativeApiCase (void); |
| |
| IterateResult iterate (void); |
| |
| private: |
| const CaseType m_caseType; |
| }; |
| |
| NegativeApiCase::NegativeApiCase (Context& ctx, const char* name, const char* desc, CaseType caseType) |
| : TestCase (ctx, name, desc) |
| , m_caseType (caseType) |
| { |
| } |
| |
| NegativeApiCase::~NegativeApiCase (void) |
| { |
| } |
| |
| NegativeApiCase::IterateResult NegativeApiCase::iterate (void) |
| { |
| glw::GLenum error; |
| glu::CallLogWrapper gl (m_context.getRenderContext().getFunctions(), m_context.getTestContext().getLog()); |
| glu::VertexArray vao (m_context.getRenderContext()); |
| |
| gl.enableLogging(true); |
| gl.glBindVertexArray(*vao); |
| |
| switch (m_caseType) |
| { |
| case CASE_LARGE_OFFSET: |
| { |
| glw::GLint maxOffset = -1; |
| glw::GLint largeOffset; |
| |
| gl.glGetIntegerv(GL_MAX_VERTEX_ATTRIB_RELATIVE_OFFSET, &maxOffset); |
| GLU_EXPECT_NO_ERROR(gl.glGetError(), ""); |
| |
| largeOffset = maxOffset + 1; |
| |
| // skip if maximum unsigned or signed values |
| if (maxOffset == -1 || maxOffset == 0x7FFFFFFF) |
| throw tcu::NotSupportedError("Implementation supports all offsets"); |
| |
| gl.glVertexAttribFormat(0, 4, GL_FLOAT, GL_FALSE, largeOffset); |
| break; |
| } |
| |
| case CASE_LARGE_STRIDE: |
| { |
| glu::Buffer buffer (m_context.getRenderContext()); |
| glw::GLint maxStride = -1; |
| glw::GLint largeStride; |
| |
| gl.glGetIntegerv(GL_MAX_VERTEX_ATTRIB_STRIDE, &maxStride); |
| GLU_EXPECT_NO_ERROR(gl.glGetError(), ""); |
| |
| largeStride = maxStride + 1; |
| |
| // skip if maximum unsigned or signed values |
| if (maxStride == -1 || maxStride == 0x7FFFFFFF) |
| throw tcu::NotSupportedError("Implementation supports all strides"); |
| |
| gl.glBindVertexBuffer(0, *buffer, 0, largeStride); |
| break; |
| } |
| |
| case CASE_NEGATIVE_STRIDE: |
| { |
| glu::Buffer buffer(m_context.getRenderContext()); |
| gl.glBindVertexBuffer(0, *buffer, 0, -20); |
| break; |
| } |
| |
| case CASE_NEGATIVE_OFFSET: |
| { |
| glu::Buffer buffer(m_context.getRenderContext()); |
| gl.glBindVertexBuffer(0, *buffer, -20, 0); |
| break; |
| } |
| |
| case CASE_INVALID_ATTR: |
| { |
| glw::GLint maxIndex = -1; |
| glw::GLint largeIndex; |
| |
| gl.glGetIntegerv(GL_MAX_VERTEX_ATTRIBS, &maxIndex); |
| GLU_EXPECT_NO_ERROR(gl.glGetError(), ""); |
| |
| largeIndex = maxIndex + 1; |
| |
| // skip if maximum unsigned or signed values |
| if (maxIndex == -1 || maxIndex == 0x7FFFFFFF) |
| throw tcu::NotSupportedError("Implementation supports any attribute index"); |
| |
| gl.glVertexAttribBinding(largeIndex, 0); |
| break; |
| } |
| |
| case CASE_INVALID_BINDING: |
| { |
| glw::GLint maxBindings = -1; |
| glw::GLint largeBinding; |
| |
| gl.glGetIntegerv(GL_MAX_VERTEX_ATTRIB_BINDINGS, &maxBindings); |
| GLU_EXPECT_NO_ERROR(gl.glGetError(), ""); |
| |
| largeBinding = maxBindings + 1; |
| |
| // skip if maximum unsigned or signed values |
| if (maxBindings == -1 || maxBindings == 0x7FFFFFFF) |
| throw tcu::NotSupportedError("Implementation supports any binding"); |
| |
| gl.glVertexAttribBinding(0, largeBinding); |
| break; |
| } |
| |
| default: |
| DE_ASSERT(false); |
| } |
| |
| error = gl.glGetError(); |
| |
| if (error != GL_INVALID_VALUE) |
| { |
| m_testCtx.getLog() << tcu::TestLog::Message << "ERROR! Expected GL_INVALID_VALUE, got " << glu::getErrorStr(error) << tcu::TestLog::EndMessage; |
| m_testCtx.setTestResult(QP_TEST_RESULT_FAIL, "Invalid error"); |
| } |
| else |
| m_testCtx.setTestResult(QP_TEST_RESULT_PASS, "Pass"); |
| |
| return STOP; |
| } |
| |
| } // anonymous |
| |
| VertexAttributeBindingTests::VertexAttributeBindingTests (Context& context) |
| : TestCaseGroup(context, "vertex_attribute_binding", "Test vertex attribute binding") |
| { |
| } |
| |
| VertexAttributeBindingTests::~VertexAttributeBindingTests (void) |
| { |
| } |
| |
| void VertexAttributeBindingTests::init (void) |
| { |
| tcu::TestCaseGroup* const usageGroup = new tcu::TestCaseGroup(m_testCtx, "usage", "Test using binding points"); |
| tcu::TestCaseGroup* const negativeGroup = new tcu::TestCaseGroup(m_testCtx, "negative", "Negative test"); |
| |
| addChild(usageGroup); |
| addChild(negativeGroup); |
| |
| // .usage |
| { |
| tcu::TestCaseGroup* const singleGroup = new tcu::TestCaseGroup(m_testCtx, "single_binding", "Test using single binding point"); |
| tcu::TestCaseGroup* const multipleGroup = new tcu::TestCaseGroup(m_testCtx, "multiple_bindings", "Test using multiple binding points"); |
| tcu::TestCaseGroup* const mixedGroup = new tcu::TestCaseGroup(m_testCtx, "mixed_usage", "Test using binding point and non binding point api variants"); |
| |
| usageGroup->addChild(singleGroup); |
| usageGroup->addChild(multipleGroup); |
| usageGroup->addChild(mixedGroup); |
| |
| // single binding |
| |
| singleGroup->addChild(new SingleBindingCase(m_context, "elements_1", 0)); |
| singleGroup->addChild(new SingleBindingCase(m_context, "elements_2", SingleBindingCase::FLAG_ATTRIBS_MULTIPLE_ELEMS)); |
| singleGroup->addChild(new SingleBindingCase(m_context, "elements_2_share_elements", SingleBindingCase::FLAG_ATTRIBS_SHARED_ELEMS)); |
| singleGroup->addChild(new SingleBindingCase(m_context, "offset_elements_1", SingleBindingCase::FLAG_BUF_ALIGNED_OFFSET | 0)); |
| singleGroup->addChild(new SingleBindingCase(m_context, "offset_elements_2", SingleBindingCase::FLAG_BUF_ALIGNED_OFFSET | SingleBindingCase::FLAG_ATTRIBS_MULTIPLE_ELEMS)); |
| singleGroup->addChild(new SingleBindingCase(m_context, "offset_elements_2_share_elements", SingleBindingCase::FLAG_BUF_ALIGNED_OFFSET | SingleBindingCase::FLAG_ATTRIBS_SHARED_ELEMS)); |
| singleGroup->addChild(new SingleBindingCase(m_context, "unaligned_offset_elements_1_aligned_elements", SingleBindingCase::FLAG_BUF_UNALIGNED_OFFSET | SingleBindingCase::FLAG_ATTRIB_ALIGNED)); // !< total offset is aligned |
| |
| // multiple bindings |
| |
| multipleGroup->addChild(new MultipleBindingCase(m_context, "basic", 0)); |
| multipleGroup->addChild(new MultipleBindingCase(m_context, "zero_stride", MultipleBindingCase::FLAG_ZERO_STRIDE)); |
| multipleGroup->addChild(new MultipleBindingCase(m_context, "instanced", MultipleBindingCase::FLAG_INSTANCED)); |
| multipleGroup->addChild(new MultipleBindingCase(m_context, "aliasing_buffer_zero_stride", MultipleBindingCase::FLAG_ALIASING_BUFFERS | MultipleBindingCase::FLAG_ZERO_STRIDE)); |
| multipleGroup->addChild(new MultipleBindingCase(m_context, "aliasing_buffer_instanced", MultipleBindingCase::FLAG_ALIASING_BUFFERS | MultipleBindingCase::FLAG_INSTANCED)); |
| |
| // mixed cases |
| mixedGroup->addChild(new MixedBindingCase(m_context, "mixed_attribs_basic", "Use different api for different attributes", MixedBindingCase::CASE_BASIC)); |
| mixedGroup->addChild(new MixedBindingCase(m_context, "mixed_attribs_instanced_binding", "Use different api for different attributes", MixedBindingCase::CASE_INSTANCED_BINDING)); |
| mixedGroup->addChild(new MixedBindingCase(m_context, "mixed_attribs_instanced_attrib", "Use different api for different attributes", MixedBindingCase::CASE_INSTANCED_ATTRIB)); |
| |
| mixedGroup->addChild(new MixedApiCase(m_context, "mixed_api_change_buffer", "change buffer with vertex_attrib_binding api", MixedApiCase::CASE_CHANGE_BUFFER)); |
| mixedGroup->addChild(new MixedApiCase(m_context, "mixed_api_change_buffer_offset", "change buffer offset with vertex_attrib_binding api", MixedApiCase::CASE_CHANGE_BUFFER_OFFSET)); |
| mixedGroup->addChild(new MixedApiCase(m_context, "mixed_api_change_buffer_stride", "change buffer stride with vertex_attrib_binding api", MixedApiCase::CASE_CHANGE_BUFFER_STRIDE)); |
| mixedGroup->addChild(new MixedApiCase(m_context, "mixed_api_change_binding_point", "change binding point with vertex_attrib_binding api", MixedApiCase::CASE_CHANGE_BINDING_POINT)); |
| } |
| |
| // negative |
| { |
| negativeGroup->addChild(new DefaultVAOCase(m_context, "default_vao_bind_vertex_buffer", "use with default vao", DefaultVAOCase::CASE_BIND_VERTEX_BUFFER)); |
| negativeGroup->addChild(new DefaultVAOCase(m_context, "default_vao_vertex_attrib_format", "use with default vao", DefaultVAOCase::CASE_VERTEX_ATTRIB_FORMAT)); |
| negativeGroup->addChild(new DefaultVAOCase(m_context, "default_vao_vertex_attrib_i_format", "use with default vao", DefaultVAOCase::CASE_VERTEX_ATTRIB_I_FORMAT)); |
| negativeGroup->addChild(new DefaultVAOCase(m_context, "default_vao_vertex_attrib_binding", "use with default vao", DefaultVAOCase::CASE_VERTEX_ATTRIB_BINDING)); |
| negativeGroup->addChild(new DefaultVAOCase(m_context, "default_vao_vertex_binding_divisor", "use with default vao", DefaultVAOCase::CASE_VERTEX_BINDING_DIVISOR)); |
| |
| negativeGroup->addChild(new BindToCreateCase(m_context, "bind_create_new_buffer", "bind not existing buffer")); |
| |
| negativeGroup->addChild(new NegativeApiCase(m_context, "vertex_attrib_format_large_offset", "large relative offset", NegativeApiCase::CASE_LARGE_OFFSET)); |
| negativeGroup->addChild(new NegativeApiCase(m_context, "bind_vertex_buffer_large_stride", "large stride", NegativeApiCase::CASE_LARGE_STRIDE)); |
| negativeGroup->addChild(new NegativeApiCase(m_context, "bind_vertex_buffer_negative_stride", "negative stride", NegativeApiCase::CASE_NEGATIVE_STRIDE)); |
| negativeGroup->addChild(new NegativeApiCase(m_context, "bind_vertex_buffer_negative_offset", "negative offset", NegativeApiCase::CASE_NEGATIVE_OFFSET)); |
| negativeGroup->addChild(new NegativeApiCase(m_context, "vertex_attrib_binding_invalid_attr", "bind invalid attr", NegativeApiCase::CASE_INVALID_ATTR)); |
| negativeGroup->addChild(new NegativeApiCase(m_context, "vertex_attrib_binding_invalid_binding", "bind invalid binding", NegativeApiCase::CASE_INVALID_BINDING)); |
| } |
| } |
| |
| } // Functional |
| } // gles31 |
| } // deqp |