/*------------------------------------------------------------------------- * drawElements Quality Program OpenGL (ES) 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 Texture buffer test case *//*--------------------------------------------------------------------*/ #include "glsTextureBufferCase.hpp" #include "tcuFormatUtil.hpp" #include "tcuImageCompare.hpp" #include "tcuRenderTarget.hpp" #include "tcuStringTemplate.hpp" #include "tcuSurface.hpp" #include "tcuTestLog.hpp" #include "tcuTextureUtil.hpp" #include "tcuResultCollector.hpp" #include "rrRenderer.hpp" #include "rrShaders.hpp" #include "gluObjectWrapper.hpp" #include "gluPixelTransfer.hpp" #include "gluShaderProgram.hpp" #include "gluShaderUtil.hpp" #include "gluStrUtil.hpp" #include "gluTexture.hpp" #include "gluTextureUtil.hpp" #include "glwEnums.hpp" #include "glwFunctions.hpp" #include "deRandom.hpp" #include "deStringUtil.hpp" #include "deUniquePtr.hpp" #include "deMemory.h" #include "deString.h" #include "deMath.h" #include <sstream> #include <string> #include <vector> using tcu::TestLog; using std::map; using std::string; using std::vector; using namespace deqp::gls::TextureBufferCaseUtil; namespace deqp { namespace gls { namespace { enum { MAX_VIEWPORT_WIDTH = 256, MAX_VIEWPORT_HEIGHT = 256, MIN_VIEWPORT_WIDTH = 64, MIN_VIEWPORT_HEIGHT = 64, }; deUint8 extend2BitsToByte (deUint8 bits) { DE_ASSERT((bits & (~0x03u)) == 0); return (deUint8)(bits | (bits << 2) | (bits << 4) | (bits << 6)); } void genRandomCoords (de::Random rng, vector<deUint8>& coords, size_t offset, size_t size) { const deUint8 bits = 2; const deUint8 bitMask = deUint8((0x1u << bits) - 1); coords.resize(size); for (int i = 0; i < (int)size; i++) { const deUint8 xBits = deUint8(rng.getUint32() & bitMask); coords[i] = extend2BitsToByte(xBits); } // Fill indices with nice quad { const deUint8 indices[] = { extend2BitsToByte(0x0u), extend2BitsToByte(0x1u), extend2BitsToByte(0x2u), extend2BitsToByte(0x3u) }; for (int i = 0; i < DE_LENGTH_OF_ARRAY(indices); i++) { const deUint8 index = indices[i]; const size_t posX = (size_t(index) * 2) + 0; const size_t posY = (size_t(index) * 2) + 1; if (posX >= offset && posX < offset+size) coords[posX - offset] = ((i % 2) == 0 ? extend2BitsToByte(0x0u) : extend2BitsToByte(0x3u)); if (posY >= offset && posY < offset+size) coords[posY - offset] = ((i / 2) == 1 ? extend2BitsToByte(0x3u) : extend2BitsToByte(0x0u)); } } // Fill beginning of buffer { const deUint8 indices[] = { extend2BitsToByte(0x0u), extend2BitsToByte(0x3u), extend2BitsToByte(0x1u), extend2BitsToByte(0x1u), extend2BitsToByte(0x2u), extend2BitsToByte(0x0u), extend2BitsToByte(0x0u), extend2BitsToByte(0x2u), extend2BitsToByte(0x1u), extend2BitsToByte(0x1u), extend2BitsToByte(0x3u), extend2BitsToByte(0x0u) }; for (int i = (int)offset; i < DE_LENGTH_OF_ARRAY(indices) && i < (int)(offset + size); i++) coords[i-offset] = indices[i]; } } class CoordVertexShader : public rr::VertexShader { public: CoordVertexShader (void) : rr::VertexShader(1, 1) { m_inputs[0].type = rr::GENERICVECTYPE_FLOAT; m_outputs[0].type = rr::GENERICVECTYPE_FLOAT; } void shadeVertices (const rr::VertexAttrib* inputs, rr::VertexPacket* const* packets, const int numPackets) const { for (int packetNdx = 0; packetNdx < numPackets; packetNdx++) { rr::VertexPacket* const packet = packets[packetNdx]; tcu::Vec4 position; readVertexAttrib(position, inputs[0], packet->instanceNdx, packet->vertexNdx); packet->outputs[0] = tcu::Vec4(1.0f); packet->position = tcu::Vec4(2.0f * (position.x() - 0.5f), 2.0f * (position.y() - 0.5f), 0.0f, 1.0f); } } }; class TextureVertexShader : public rr::VertexShader { public: TextureVertexShader (const tcu::ConstPixelBufferAccess& texture) : rr::VertexShader (1, 1) , m_texture (texture) { m_inputs[0].type = rr::GENERICVECTYPE_FLOAT; m_outputs[0].type = rr::GENERICVECTYPE_FLOAT; } void shadeVertices (const rr::VertexAttrib* inputs, rr::VertexPacket* const* packets, const int numPackets) const { for (int packetNdx = 0; packetNdx < numPackets; packetNdx++) { rr::VertexPacket* const packet = packets[packetNdx]; tcu::Vec4 position; tcu::Vec4 texelValue; readVertexAttrib(position, inputs[0], packet->instanceNdx, packet->vertexNdx); texelValue = tcu::Vec4(m_texture.getPixel(de::clamp<int>((deRoundFloatToInt32(position.x() * 4) + 4) * (deRoundFloatToInt32(position.y() * 4) + 4), 0, m_texture.getWidth()-1), 0)); packet->outputs[0] = texelValue; packet->position = tcu::Vec4(2.0f * (position.x() - 0.5f), 2.0f * (position.y() - 0.5f), 0.0f, 1.0f); } } private: const tcu::ConstPixelBufferAccess m_texture; }; class CoordFragmentShader : public rr::FragmentShader { public: CoordFragmentShader (void) : rr::FragmentShader (1, 1) { m_inputs[0].type = rr::GENERICVECTYPE_FLOAT; m_outputs[0].type = rr::GENERICVECTYPE_FLOAT; } void shadeFragments (rr::FragmentPacket* packets, const int numPackets, const rr::FragmentShadingContext& context) const { for (int packetNdx = 0; packetNdx < numPackets; packetNdx++) { rr::FragmentPacket& packet = packets[packetNdx]; const tcu::Vec4 vtxColor0 = rr::readVarying<float>(packet, context, 0, 0); const tcu::Vec4 vtxColor1 = rr::readVarying<float>(packet, context, 0, 1); const tcu::Vec4 vtxColor2 = rr::readVarying<float>(packet, context, 0, 2); const tcu::Vec4 vtxColor3 = rr::readVarying<float>(packet, context, 0, 3); const tcu::Vec4 color0 = vtxColor0; const tcu::Vec4 color1 = vtxColor1; const tcu::Vec4 color2 = vtxColor2; const tcu::Vec4 color3 = vtxColor3; rr::writeFragmentOutput(context, packetNdx, 0, 0, tcu::Vec4(color0.x() * color0.w(), color0.y() * color0.w(), color0.z() * color0.w(), 1.0f)); rr::writeFragmentOutput(context, packetNdx, 1, 0, tcu::Vec4(color1.x() * color1.w(), color1.y() * color1.w(), color1.z() * color1.w(), 1.0f)); rr::writeFragmentOutput(context, packetNdx, 2, 0, tcu::Vec4(color2.x() * color2.w(), color2.y() * color2.w(), color2.z() * color2.w(), 1.0f)); rr::writeFragmentOutput(context, packetNdx, 3, 0, tcu::Vec4(color3.x() * color3.w(), color3.y() * color3.w(), color3.z() * color3.w(), 1.0f)); } } }; class TextureFragmentShader : public rr::FragmentShader { public: TextureFragmentShader (const tcu::ConstPixelBufferAccess& texture) : rr::FragmentShader (1, 1) , m_texture (texture) { m_inputs[0].type = rr::GENERICVECTYPE_FLOAT; m_outputs[0].type = rr::GENERICVECTYPE_FLOAT; } void shadeFragments (rr::FragmentPacket* packets, const int numPackets, const rr::FragmentShadingContext& context) const { for (int packetNdx = 0; packetNdx < numPackets; packetNdx++) { rr::FragmentPacket& packet = packets[packetNdx]; const tcu::IVec2 position0 = packet.position + tcu::IVec2(0, 0); const tcu::IVec2 position1 = packet.position + tcu::IVec2(1, 0); const tcu::IVec2 position2 = packet.position + tcu::IVec2(0, 1); const tcu::IVec2 position3 = packet.position + tcu::IVec2(1, 1); const tcu::Vec4 texColor0 = m_texture.getPixel(de::clamp((position0.x() * position0.y()), 0, m_texture.getWidth()-1), 0); const tcu::Vec4 texColor1 = m_texture.getPixel(de::clamp((position1.x() * position1.y()), 0, m_texture.getWidth()-1), 0); const tcu::Vec4 texColor2 = m_texture.getPixel(de::clamp((position2.x() * position2.y()), 0, m_texture.getWidth()-1), 0); const tcu::Vec4 texColor3 = m_texture.getPixel(de::clamp((position3.x() * position3.y()), 0, m_texture.getWidth()-1), 0); const tcu::Vec4 vtxColor0 = rr::readVarying<float>(packet, context, 0, 0); const tcu::Vec4 vtxColor1 = rr::readVarying<float>(packet, context, 0, 1); const tcu::Vec4 vtxColor2 = rr::readVarying<float>(packet, context, 0, 2); const tcu::Vec4 vtxColor3 = rr::readVarying<float>(packet, context, 0, 3); const tcu::Vec4 color0 = 0.5f * (vtxColor0 + texColor0); const tcu::Vec4 color1 = 0.5f * (vtxColor1 + texColor1); const tcu::Vec4 color2 = 0.5f * (vtxColor2 + texColor2); const tcu::Vec4 color3 = 0.5f * (vtxColor3 + texColor3); rr::writeFragmentOutput(context, packetNdx, 0, 0, tcu::Vec4(color0.x() * color0.w(), color0.y() * color0.w(), color0.z() * color0.w(), 1.0f)); rr::writeFragmentOutput(context, packetNdx, 1, 0, tcu::Vec4(color1.x() * color1.w(), color1.y() * color1.w(), color1.z() * color1.w(), 1.0f)); rr::writeFragmentOutput(context, packetNdx, 2, 0, tcu::Vec4(color2.x() * color2.w(), color2.y() * color2.w(), color2.z() * color2.w(), 1.0f)); rr::writeFragmentOutput(context, packetNdx, 3, 0, tcu::Vec4(color3.x() * color3.w(), color3.y() * color3.w(), color3.z() * color3.w(), 1.0f)); } } private: const tcu::ConstPixelBufferAccess m_texture; }; string generateVertexShaderTemplate (RenderBits renderBits) { std::ostringstream stream; stream << "${VERSION_HEADER}\n"; if (renderBits & RENDERBITS_AS_VERTEX_TEXTURE) stream << "${TEXTURE_BUFFER_EXT}"; stream << "${VTX_INPUT} layout(location = 0) ${HIGHP} vec2 i_coord;\n" "${VTX_OUTPUT} ${HIGHP} vec4 v_color;\n"; if (renderBits & RENDERBITS_AS_VERTEX_TEXTURE) { stream << "uniform ${HIGHP} samplerBuffer u_vtxSampler;\n"; } stream << "\n" "void main (void)\n" "{\n"; if (renderBits & RENDERBITS_AS_VERTEX_TEXTURE) stream << "\tv_color = texelFetch(u_vtxSampler, clamp((int(round(i_coord.x * 4.0)) + 4) * (int(round(i_coord.y * 4.0)) + 4), 0, textureSize(u_vtxSampler)-1));\n"; else stream << "\tv_color = vec4(1.0);\n"; stream << "\tgl_Position = vec4(2.0 * (i_coord - vec2(0.5)), 0.0, 1.0);\n" "}\n"; return stream.str(); } string generateFragmentShaderTemplate (RenderBits renderBits) { std::ostringstream stream; stream << "${VERSION_HEADER}\n"; if (renderBits & RENDERBITS_AS_FRAGMENT_TEXTURE) stream << "${TEXTURE_BUFFER_EXT}"; stream << "${FRAG_OUTPUT} layout(location = 0) ${HIGHP} vec4 dEQP_FragColor;\n" "${FRAG_INPUT} ${HIGHP} vec4 v_color;\n"; if (renderBits & RENDERBITS_AS_FRAGMENT_TEXTURE) stream << "uniform ${HIGHP} samplerBuffer u_fragSampler;\n"; stream << "\n" "void main (void)\n" "{\n"; if (renderBits & RENDERBITS_AS_FRAGMENT_TEXTURE) stream << "\t${HIGHP} vec4 color = 0.5 * (v_color + texelFetch(u_fragSampler, clamp(int(gl_FragCoord.x) * int(gl_FragCoord.y), 0, textureSize(u_fragSampler)-1)));\n"; else stream << "\t${HIGHP} vec4 color = v_color;\n"; stream << "\tdEQP_FragColor = vec4(color.xyz * color.w, 1.0);\n" "}\n"; return stream.str(); } string specializeShader (const string& shaderTemplateString, glu::GLSLVersion glslVersion) { const tcu::StringTemplate shaderTemplate(shaderTemplateString); map<string, string> parameters; parameters["VERSION_HEADER"] = glu::getGLSLVersionDeclaration(glslVersion); parameters["VTX_OUTPUT"] = "out"; parameters["VTX_INPUT"] = "in"; parameters["FRAG_INPUT"] = "in"; parameters["FRAG_OUTPUT"] = "out"; parameters["HIGHP"] = (glslVersion == glu::GLSL_VERSION_330 ? "" : "highp"); parameters["TEXTURE_BUFFER_EXT"] = (glslVersion == glu::GLSL_VERSION_330 ? "" : "#extension GL_EXT_texture_buffer : enable\n"); return shaderTemplate.specialize(parameters); } glu::ShaderProgram* createRenderProgram (glu::RenderContext& renderContext, RenderBits renderBits) { const string vertexShaderTemplate = generateVertexShaderTemplate(renderBits); const string fragmentShaderTemplate = generateFragmentShaderTemplate(renderBits); const glu::GLSLVersion glslVersion = glu::getContextTypeGLSLVersion(renderContext.getType()); const string vertexShaderSource = specializeShader(vertexShaderTemplate, glslVersion); const string fragmentShaderSource = specializeShader(fragmentShaderTemplate, glslVersion); glu::ShaderProgram* const program = new glu::ShaderProgram(renderContext, glu::makeVtxFragSources(vertexShaderSource, fragmentShaderSource)); return program; } void logModifications (TestLog& log, ModifyBits modifyBits) { tcu::ScopedLogSection section(log, "Modify Operations", "Modify Operations"); const struct { ModifyBits bit; const char* str; } bitInfos[] = { { MODIFYBITS_BUFFERDATA, "Recreate buffer data with glBufferData()." }, { MODIFYBITS_BUFFERSUBDATA, "Modify texture buffer with glBufferSubData()." }, { MODIFYBITS_MAPBUFFER_WRITE, "Map buffer write-only and rewrite data." }, { MODIFYBITS_MAPBUFFER_READWRITE, "Map buffer readw-write check and rewrite data." } }; DE_ASSERT(modifyBits != 0); for (int infoNdx = 0; infoNdx < DE_LENGTH_OF_ARRAY(bitInfos); infoNdx++) { if (modifyBits & bitInfos[infoNdx].bit) log << TestLog::Message << bitInfos[infoNdx].str << TestLog::EndMessage; } } void modifyBufferData (TestLog& log, de::Random& rng, glu::TextureBuffer& texture) { vector<deUint8> data; genRandomCoords(rng, data, 0, texture.getBufferSize()); log << TestLog::Message << "BufferData, Size: " << data.size() << TestLog::EndMessage; { // replace getRefBuffer with a new buffer de::ArrayBuffer<deUint8> buffer(&(data[0]), data.size()); texture.getRefBuffer().swap(buffer); } texture.upload(); } void modifyBufferSubData (TestLog& log, de::Random& rng, const glw::Functions& gl, glu::TextureBuffer& texture) { const size_t minSize = 4*16; const size_t size = de::max<size_t>(minSize, size_t((float)(texture.getSize() != 0 ? texture.getSize() : texture.getBufferSize()) * (0.7f + 0.3f * rng.getFloat()))); const size_t minOffset = texture.getOffset(); const size_t offset = minOffset + (rng.getUint32() % (texture.getBufferSize() - (size + minOffset))); vector<deUint8> data; genRandomCoords(rng, data, offset, size); log << TestLog::Message << "BufferSubData, Offset: " << offset << ", Size: " << size << TestLog::EndMessage; gl.bindBuffer(GL_TEXTURE_BUFFER, texture.getGLBuffer()); gl.bufferSubData(GL_TEXTURE_BUFFER, (glw::GLsizei)offset, (glw::GLsizei)data.size(), &(data[0])); gl.bindBuffer(GL_TEXTURE_BUFFER, 0); GLU_EXPECT_NO_ERROR(gl.getError(), "Failed to update data with glBufferSubData()"); deMemcpy((deUint8*)texture.getRefBuffer().getPtr() + offset, &(data[0]), int(data.size())); } void modifyMapWrite (TestLog& log, de::Random& rng, const glw::Functions& gl, glu::TextureBuffer& texture) { const size_t minSize = 4*16; const size_t size = de::max<size_t>(minSize, size_t((float)(texture.getSize() != 0 ? texture.getSize() : texture.getBufferSize()) * (0.7f + 0.3f * rng.getFloat()))); const size_t minOffset = texture.getOffset(); const size_t offset = minOffset + (rng.getUint32() % (texture.getBufferSize() - (size + minOffset))); vector<deUint8> data; genRandomCoords(rng, data, offset, size); log << TestLog::Message << "glMapBufferRange, Write Only, Offset: " << offset << ", Size: " << size << TestLog::EndMessage; gl.bindBuffer(GL_TEXTURE_BUFFER, texture.getGLBuffer()); { deUint8* ptr = (deUint8*)gl.mapBufferRange(GL_TEXTURE_BUFFER, (glw::GLsizei)offset, (glw::GLsizei)size, GL_MAP_WRITE_BIT); GLU_EXPECT_NO_ERROR(gl.getError(), "glMapBufferRange()"); TCU_CHECK(ptr); for (int i = 0; i < (int)data.size(); i++) ptr[i] = data[i]; TCU_CHECK(gl.unmapBuffer(GL_TEXTURE_BUFFER)); } gl.bindBuffer(GL_TEXTURE_BUFFER, 0); GLU_EXPECT_NO_ERROR(gl.getError(), "Failed to update data with glMapBufferRange()"); deMemcpy((deUint8*)texture.getRefBuffer().getPtr()+offset, &(data[0]), int(data.size())); } void modifyMapReadWrite (TestLog& log, tcu::ResultCollector& resultCollector, de::Random& rng, const glw::Functions& gl, glu::TextureBuffer& texture) { const size_t minSize = 4*16; const size_t size = de::max<size_t>(minSize, size_t((float)(texture.getSize() != 0 ? texture.getSize() : texture.getBufferSize()) * (0.7f + 0.3f * rng.getFloat()))); const size_t minOffset = texture.getOffset(); const size_t offset = minOffset + (rng.getUint32() % (texture.getBufferSize() - (size + minOffset))); deUint8* const refPtr = (deUint8*)texture.getRefBuffer().getPtr() + offset; vector<deUint8> data; genRandomCoords(rng, data, offset, size); log << TestLog::Message << "glMapBufferRange, Read Write, Offset: " << offset << ", Size: " << size << TestLog::EndMessage; gl.bindBuffer(GL_TEXTURE_BUFFER, texture.getGLBuffer()); { size_t invalidBytes = 0; deUint8* const ptr = (deUint8*)gl.mapBufferRange(GL_TEXTURE_BUFFER, (glw::GLsizei)offset, (glw::GLsizei)size, GL_MAP_WRITE_BIT|GL_MAP_READ_BIT); GLU_EXPECT_NO_ERROR(gl.getError(), "glMapBufferRange()"); TCU_CHECK(ptr); for (int i = 0; i < (int)data.size(); i++) { if (ptr[i] != refPtr[i]) { if (invalidBytes < 24) log << TestLog::Message << "Invalid byte in mapped buffer. " << tcu::Format::Hex<2>(data[i]).toString() << " at " << i << ", expected " << tcu::Format::Hex<2>(refPtr[i]).toString() << TestLog::EndMessage; invalidBytes++; } ptr[i] = data[i]; } TCU_CHECK(gl.unmapBuffer(GL_TEXTURE_BUFFER)); if (invalidBytes > 0) { log << TestLog::Message << "Total of " << invalidBytes << " invalid bytes." << TestLog::EndMessage; resultCollector.fail("Invalid data in mapped buffer"); } } gl.bindBuffer(GL_TEXTURE_BUFFER, 0); GLU_EXPECT_NO_ERROR(gl.getError(), "Failed to update data with glMapBufferRange()"); for (int i = 0; i < (int)data.size(); i++) refPtr[i] = data[i]; } void modify (TestLog& log, tcu::ResultCollector& resultCollector, glu::RenderContext& renderContext, ModifyBits modifyBits, de::Random& rng, glu::TextureBuffer& texture) { const tcu::ScopedLogSection modifySection(log, "Modifying Texture buffer", "Modifying Texture Buffer"); logModifications(log, modifyBits); if (modifyBits & MODIFYBITS_BUFFERDATA) modifyBufferData(log, rng, texture); if (modifyBits & MODIFYBITS_BUFFERSUBDATA) modifyBufferSubData(log, rng, renderContext.getFunctions(), texture); if (modifyBits & MODIFYBITS_MAPBUFFER_WRITE) modifyMapWrite(log, rng, renderContext.getFunctions(), texture); if (modifyBits & MODIFYBITS_MAPBUFFER_READWRITE) modifyMapReadWrite(log, resultCollector, rng, renderContext.getFunctions(), texture); } void renderGL (glu::RenderContext& renderContext, RenderBits renderBits, deUint32 coordSeed, int triangleCount, glu::ShaderProgram& program, glu::TextureBuffer& texture) { const glw::Functions& gl = renderContext.getFunctions(); const glu::VertexArray vao (renderContext); const glu::Buffer coordBuffer (renderContext); gl.useProgram(program.getProgram()); gl.bindVertexArray(*vao); gl.enableVertexAttribArray(0); if (renderBits & RENDERBITS_AS_VERTEX_ARRAY) { gl.bindBuffer(GL_ARRAY_BUFFER, texture.getGLBuffer()); gl.vertexAttribPointer(0, 2, GL_UNSIGNED_BYTE, true, 0, DE_NULL); } else { de::Random rng(coordSeed); vector<deUint8> coords; genRandomCoords(rng, coords, 0, 256*2); gl.bindBuffer(GL_ARRAY_BUFFER, *coordBuffer); gl.bufferData(GL_ARRAY_BUFFER, (glw::GLsizei)coords.size(), &(coords[0]), GL_STREAM_DRAW); gl.vertexAttribPointer(0, 2, GL_UNSIGNED_BYTE, true, 0, DE_NULL); } if (renderBits & RENDERBITS_AS_VERTEX_TEXTURE) { const deInt32 location = gl.getUniformLocation(program.getProgram(), "u_vtxSampler"); gl.activeTexture(GL_TEXTURE0); gl.bindTexture(GL_TEXTURE_BUFFER, texture.getGLTexture()); gl.uniform1i(location, 0); } if (renderBits & RENDERBITS_AS_FRAGMENT_TEXTURE) { const deInt32 location = gl.getUniformLocation(program.getProgram(), "u_fragSampler"); gl.activeTexture(GL_TEXTURE1); gl.bindTexture(GL_TEXTURE_BUFFER, texture.getGLTexture()); gl.uniform1i(location, 1); gl.activeTexture(GL_TEXTURE0); } if (renderBits & RENDERBITS_AS_INDEX_ARRAY) { gl.bindBuffer(GL_ELEMENT_ARRAY_BUFFER, texture.getGLBuffer()); gl.drawElements(GL_TRIANGLES, triangleCount * 3, GL_UNSIGNED_BYTE, DE_NULL); gl.bindBuffer(GL_ELEMENT_ARRAY_BUFFER, 0); } else gl.drawArrays(GL_TRIANGLES, 0, triangleCount * 3); if (renderBits & RENDERBITS_AS_FRAGMENT_TEXTURE) { gl.activeTexture(GL_TEXTURE1); gl.bindTexture(GL_TEXTURE_BUFFER, 0); } if (renderBits & RENDERBITS_AS_VERTEX_TEXTURE) { gl.activeTexture(GL_TEXTURE0); gl.bindTexture(GL_TEXTURE_BUFFER, 0); } gl.bindBuffer(GL_ARRAY_BUFFER, 0); gl.disableVertexAttribArray(0); gl.bindVertexArray(0); gl.useProgram(0); GLU_EXPECT_NO_ERROR(gl.getError(), "Rendering failed"); } void renderReference (RenderBits renderBits, deUint32 coordSeed, int triangleCount, const glu::TextureBuffer& texture, int maxTextureBufferSize, const tcu::PixelBufferAccess& target) { const tcu::ConstPixelBufferAccess effectiveAccess = glu::getTextureBufferEffectiveRefTexture(texture, maxTextureBufferSize); const CoordVertexShader coordVertexShader; const TextureVertexShader textureVertexShader (effectiveAccess); const rr::VertexShader* const vertexShader = (renderBits & RENDERBITS_AS_VERTEX_TEXTURE ? static_cast<const rr::VertexShader*>(&textureVertexShader) : &coordVertexShader); const CoordFragmentShader coordFragmmentShader; const TextureFragmentShader textureFragmentShader (effectiveAccess); const rr::FragmentShader* const fragmentShader = (renderBits & RENDERBITS_AS_FRAGMENT_TEXTURE ? static_cast<const rr::FragmentShader*>(&textureFragmentShader) : &coordFragmmentShader); const rr::Renderer renderer; const rr::RenderState renderState(rr::ViewportState(rr::WindowRectangle(0, 0, target.getWidth(), target.getHeight()))); const rr::RenderTarget renderTarget(rr::MultisamplePixelBufferAccess::fromSinglesampleAccess(target)); const rr::Program program(vertexShader, fragmentShader); rr::VertexAttrib vertexAttribs[1]; vector<deUint8> coords; if (renderBits & RENDERBITS_AS_VERTEX_ARRAY) { vertexAttribs[0].type = rr::VERTEXATTRIBTYPE_NONPURE_UNORM8; vertexAttribs[0].size = 2; vertexAttribs[0].pointer = texture.getRefBuffer().getPtr(); } else { de::Random rng(coordSeed); genRandomCoords(rng, coords, 0, 256*2); vertexAttribs[0].type = rr::VERTEXATTRIBTYPE_NONPURE_UNORM8; vertexAttribs[0].size = 2; vertexAttribs[0].pointer = &(coords[0]); } if (renderBits & RENDERBITS_AS_INDEX_ARRAY) { const rr::PrimitiveList primitives(rr::PRIMITIVETYPE_TRIANGLES, triangleCount * 3, rr::DrawIndices(texture.getRefBuffer().getPtr(), rr::INDEXTYPE_UINT8)); const rr::DrawCommand cmd(renderState, renderTarget, program, 1, vertexAttribs, primitives); renderer.draw(cmd); } else { const rr::PrimitiveList primitives(rr::PRIMITIVETYPE_TRIANGLES, triangleCount * 3, 0); const rr::DrawCommand cmd(renderState, renderTarget, program, 1, vertexAttribs, primitives); renderer.draw(cmd); } } void logRendering (TestLog& log, RenderBits renderBits) { const struct { RenderBits bit; const char* str; } bitInfos[] = { { RENDERBITS_AS_VERTEX_ARRAY, "vertex array" }, { RENDERBITS_AS_INDEX_ARRAY, "index array" }, { RENDERBITS_AS_VERTEX_TEXTURE, "vertex texture" }, { RENDERBITS_AS_FRAGMENT_TEXTURE, "fragment texture" } }; std::ostringstream stream; vector<const char*> usedAs; DE_ASSERT(renderBits != 0); for (int infoNdx = 0; infoNdx < DE_LENGTH_OF_ARRAY(bitInfos); infoNdx++) { if (renderBits & bitInfos[infoNdx].bit) usedAs.push_back(bitInfos[infoNdx].str); } stream << "Render using texture buffer as "; for (int asNdx = 0; asNdx < (int)usedAs.size(); asNdx++) { if (asNdx+1 == (int)usedAs.size() && (int)usedAs.size() > 1) stream << " and "; else if (asNdx > 0) stream << ", "; stream << usedAs[asNdx]; } stream << "."; log << TestLog::Message << stream.str() << TestLog::EndMessage; } void render (TestLog& log, glu::RenderContext& renderContext, RenderBits renderBits, de::Random& rng, glu::ShaderProgram& program, glu::TextureBuffer& texture, const tcu::PixelBufferAccess& target) { const tcu::ScopedLogSection renderSection (log, "Render Texture buffer", "Render Texture Buffer"); const int triangleCount = 8; const deUint32 coordSeed = rng.getUint32(); int maxTextureBufferSize = 0; renderContext.getFunctions().getIntegerv(GL_MAX_TEXTURE_BUFFER_SIZE, &maxTextureBufferSize); GLU_EXPECT_NO_ERROR(renderContext.getFunctions().getError(), "query GL_MAX_TEXTURE_BUFFER_SIZE"); DE_ASSERT(maxTextureBufferSize > 0); // checked in init() logRendering(log, renderBits); renderGL(renderContext, renderBits, coordSeed, triangleCount, program, texture); renderReference(renderBits, coordSeed, triangleCount, texture, maxTextureBufferSize, target); } void verifyScreen (TestLog& log, tcu::ResultCollector& resultCollector, glu::RenderContext& renderContext, const tcu::ConstPixelBufferAccess& referenceTarget) { const tcu::ScopedLogSection verifySection (log, "Verify screen contents", "Verify screen contents"); tcu::Surface screen (referenceTarget.getWidth(), referenceTarget.getHeight()); glu::readPixels(renderContext, 0, 0, screen.getAccess()); if (!tcu::fuzzyCompare(log, "Result of rendering", "Result of rendering", referenceTarget, screen.getAccess(), 0.05f, tcu::COMPARE_LOG_RESULT)) resultCollector.fail("Rendering failed"); } void logImplementationInfo (TestLog& log, glu::RenderContext& renderContext) { const tcu::ScopedLogSection section (log, "Implementation Values", "Implementation Values"); de::UniquePtr<glu::ContextInfo> info (glu::ContextInfo::create(renderContext)); const glw::Functions& gl = renderContext.getFunctions(); if (glu::contextSupports(renderContext.getType(), glu::ApiType(3, 3, glu::PROFILE_CORE))) { deInt32 maxTextureSize = 0; gl.getIntegerv(GL_MAX_TEXTURE_BUFFER_SIZE, &maxTextureSize); GLU_EXPECT_NO_ERROR(gl.getError(), "glGetIntegerv(GL_MAX_TEXTURE_BUFFER_SIZE)"); log << TestLog::Message << "GL_MAX_TEXTURE_BUFFER_SIZE : " << maxTextureSize << TestLog::EndMessage; } else if (glu::contextSupports(renderContext.getType(), glu::ApiType(3, 1, glu::PROFILE_ES)) && info->isExtensionSupported("GL_EXT_texture_buffer")) { { deInt32 maxTextureSize = 0; gl.getIntegerv(GL_MAX_TEXTURE_BUFFER_SIZE, &maxTextureSize); GLU_EXPECT_NO_ERROR(gl.getError(), "glGetIntegerv(GL_MAX_TEXTURE_BUFFER_SIZE_EXT)"); log << TestLog::Message << "GL_MAX_TEXTURE_BUFFER_SIZE_EXT : " << maxTextureSize << TestLog::EndMessage; } { deInt32 textureBufferAlignment = 0; gl.getIntegerv(GL_TEXTURE_BUFFER_OFFSET_ALIGNMENT, &textureBufferAlignment); GLU_EXPECT_NO_ERROR(gl.getError(), "glGetIntegerv(GL_TEXTURE_BUFFER_OFFSET_ALIGNMENT_EXT)"); log << TestLog::Message << "GL_TEXTURE_BUFFER_OFFSET_ALIGNMENT_EXT : " << textureBufferAlignment << TestLog::EndMessage; } } else DE_ASSERT(DE_FALSE); } void logTextureInfo (TestLog& log, deUint32 format, size_t bufferSize, size_t offset, size_t size) { const tcu::ScopedLogSection section(log, "Texture Info", "Texture Info"); log << TestLog::Message << "Texture format : " << glu::getTextureFormatStr(format) << TestLog::EndMessage; log << TestLog::Message << "Buffer size : " << bufferSize << TestLog::EndMessage; if (offset != 0 || size != 0) { log << TestLog::Message << "Buffer range offset: " << offset << TestLog::EndMessage; log << TestLog::Message << "Buffer range size: " << size << TestLog::EndMessage; } } void runTests (tcu::TestContext& testCtx, glu::RenderContext& renderContext, de::Random& rng, deUint32 format, size_t bufferSize, size_t offset, size_t size, RenderBits preRender, glu::ShaderProgram* preRenderProgram, ModifyBits modifyType, RenderBits postRender, glu::ShaderProgram* postRenderProgram) { const tcu::RenderTarget renderTarget (renderContext.getRenderTarget()); const glw::Functions& gl = renderContext.getFunctions(); const int width = de::min<int>(renderTarget.getWidth(), MAX_VIEWPORT_WIDTH); const int height = de::min<int>(renderTarget.getHeight(), MAX_VIEWPORT_HEIGHT); const tcu::Vec4 clearColor (0.25f, 0.5f, 0.75f, 1.0f); TestLog& log = testCtx.getLog(); tcu::ResultCollector resultCollector (log); logImplementationInfo(log, renderContext); logTextureInfo(log, format, bufferSize, offset, size); { tcu::Surface referenceTarget (width, height); vector<deUint8> bufferData; genRandomCoords(rng, bufferData, 0, bufferSize); for (deUint8 i = 0; i < 4; i++) { const deUint8 val = extend2BitsToByte(i); if (val >= offset && val < offset + size) { bufferData[val*2 + 0] = (i / 2 == 0 ? extend2BitsToByte(0x2u) : extend2BitsToByte(0x01u)); bufferData[val*2 + 1] = (i % 2 == 0 ? extend2BitsToByte(0x2u) : extend2BitsToByte(0x01u)); } } { glu::TextureBuffer texture (renderContext, format, bufferSize, offset, size, &(bufferData[0])); TCU_CHECK_MSG(width >= MIN_VIEWPORT_WIDTH || height >= MIN_VIEWPORT_HEIGHT, "Too small viewport"); DE_ASSERT(preRender == 0 || preRenderProgram); DE_ASSERT(postRender == 0 || postRenderProgram); gl.viewport(0, 0, width, height); gl.clearColor(clearColor.x(), clearColor.y(), clearColor.z(), clearColor.w()); gl.clear(GL_COLOR_BUFFER_BIT); GLU_EXPECT_NO_ERROR(gl.getError(), "Screen setup failed"); tcu::clear(referenceTarget.getAccess(), clearColor); texture.upload(); if (preRender != 0) render(log, renderContext, preRender, rng, *preRenderProgram, texture, referenceTarget.getAccess()); if (modifyType != 0) modify(log, resultCollector, renderContext, modifyType, rng, texture); if (postRender != 0) render(log, renderContext, postRender, rng, *postRenderProgram, texture, referenceTarget.getAccess()); } verifyScreen(log, resultCollector, renderContext, referenceTarget.getAccess()); resultCollector.setTestContextResult(testCtx); } } } // anonymous TextureBufferCase::TextureBufferCase (tcu::TestContext& testCtx, glu::RenderContext& renderCtx, deUint32 format, size_t bufferSize, size_t offset, size_t size, RenderBits preRender, ModifyBits modify, RenderBits postRender, const char* name, const char* description) : tcu::TestCase (testCtx, name, description) , m_renderCtx (renderCtx) , m_format (format) , m_bufferSize (bufferSize) , m_offset (offset) , m_size (size) , m_preRender (preRender) , m_modify (modify) , m_postRender (postRender) , m_preRenderProgram (DE_NULL) , m_postRenderProgram (DE_NULL) { } TextureBufferCase::~TextureBufferCase (void) { TextureBufferCase::deinit(); } void TextureBufferCase::init (void) { de::UniquePtr<glu::ContextInfo> info (glu::ContextInfo::create(m_renderCtx)); if (!glu::contextSupports(m_renderCtx.getType(), glu::ApiType(3, 3, glu::PROFILE_CORE)) && !(glu::contextSupports(m_renderCtx.getType(), glu::ApiType(3, 1, glu::PROFILE_ES)) && info->isExtensionSupported("GL_EXT_texture_buffer"))) throw tcu::NotSupportedError("Texture buffers not supported", "", __FILE__, __LINE__); { const int maxTextureBufferSize = info->getInt(GL_MAX_TEXTURE_BUFFER_SIZE); if (maxTextureBufferSize <= 0) TCU_THROW(NotSupportedError, "GL_MAX_TEXTURE_BUFFER_SIZE > 0 required"); } if (m_preRender != 0) { TestLog& log = m_testCtx.getLog(); const char* const sectionName = (m_postRender != 0 ? "Primary render program" : "Render program"); const tcu::ScopedLogSection section (log, sectionName, sectionName); m_preRenderProgram = createRenderProgram(m_renderCtx, m_preRender); m_testCtx.getLog() << (*m_preRenderProgram); TCU_CHECK(m_preRenderProgram->isOk()); } if (m_postRender != 0) { // Reusing program if (m_preRender == m_postRender) { m_postRenderProgram = m_preRenderProgram; } else { TestLog& log = m_testCtx.getLog(); const char* const sectionName = (m_preRender!= 0 ? "Secondary render program" : "Render program"); const tcu::ScopedLogSection section (log, sectionName, sectionName); m_postRenderProgram = createRenderProgram(m_renderCtx, m_postRender); m_testCtx.getLog() << (*m_postRenderProgram); TCU_CHECK(m_postRenderProgram->isOk()); } } } void TextureBufferCase::deinit (void) { if (m_preRenderProgram == m_postRenderProgram) m_postRenderProgram = DE_NULL; delete m_preRenderProgram; m_preRenderProgram = DE_NULL; delete m_postRenderProgram; m_postRenderProgram = DE_NULL; } tcu::TestCase::IterateResult TextureBufferCase::iterate (void) { de::Random rng (deInt32Hash(deStringHash(getName()))); size_t offset; if (m_offset != 0) { const glw::Functions& gl = m_renderCtx.getFunctions(); deInt32 alignment = 0; gl.getIntegerv(GL_TEXTURE_BUFFER_OFFSET_ALIGNMENT, &alignment); GLU_EXPECT_NO_ERROR(gl.getError(), "glGetIntegerv(GL_TEXTURE_BUFFER_OFFSET_ALIGNMENT)"); offset = m_offset * alignment; } else offset = 0; runTests(m_testCtx, m_renderCtx, rng, m_format, m_bufferSize, offset, m_size, m_preRender, m_preRenderProgram, m_modify, m_postRender, m_postRenderProgram); return STOP; } } // gls } // deqp