| /*------------------------------------------------------------------------ |
| * OpenGL Conformance Tests |
| * ------------------------ |
| * |
| * Copyright (c) 2017-2019 The Khronos Group Inc. |
| * Copyright (c) 2017 Codeplay Software Ltd. |
| * Copyright (c) 2019 NVIDIA Corporation. |
| * |
| * 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 Subgroups Tests Utils |
| */ /*--------------------------------------------------------------------*/ |
| |
| #include "glcSubgroupsTestsUtils.hpp" |
| #include "deRandom.hpp" |
| #include "tcuCommandLine.hpp" |
| #include "tcuStringTemplate.hpp" |
| #include "gluContextInfo.hpp" |
| #include "gluShaderUtil.hpp" |
| |
| using namespace deqp; |
| using namespace std; |
| using namespace glc; |
| using namespace glw; |
| |
| namespace |
| { |
| // debug callback function |
| // To use: |
| // gl.enable(GL_DEBUG_OUTPUT); |
| // gl.debugMessageCallback(debugCallback, &context); |
| // |
| void debugCallback(GLenum source, GLenum type, GLuint id, GLenum severity, |
| GLsizei length, const char * message, const void * userParam) |
| { |
| glc::Context *context = (glc::Context *)userParam; |
| |
| tcu::TestLog& log = context->getDeqpContext().getTestContext().getLog(); |
| |
| log << tcu::TestLog::Message |
| << "DEBUG: source = " << source << ", type= " << type << ", id = " << id << ", severity = " << severity |
| << ", length = " << length << "\n" |
| << "DEBUG: `" << message << "`" |
| << tcu::TestLog::EndMessage; |
| |
| } |
| |
| // getFormatReadInfo |
| // returns the stride in bytes |
| deUint32 getFormatReadInfo(const subgroups::Format format, GLenum &readFormat, GLenum &readType) |
| { |
| using namespace subgroups; |
| switch (format) |
| { |
| default: |
| DE_FATAL("Unhandled format!"); |
| // fall-through |
| case FORMAT_R32G32B32A32_SFLOAT: |
| readFormat = GL_RGBA; |
| readType = GL_FLOAT; |
| return 4u; |
| case FORMAT_R32G32_SFLOAT: |
| readFormat = GL_RG; |
| readType = GL_FLOAT; |
| return 2u; |
| case FORMAT_R32_UINT: |
| readFormat = GL_RED_INTEGER; |
| readType = GL_UNSIGNED_INT; |
| return 1u; |
| case FORMAT_R32G32B32A32_UINT: |
| readFormat = GL_RGBA_INTEGER; |
| readType = GL_UNSIGNED_INT; |
| return 4u; |
| } |
| } |
| |
| deUint32 getMaxWidth () |
| { |
| return 1024u; |
| } |
| |
| deUint32 getNextWidth (const deUint32 width) |
| { |
| if (width < 128) |
| { |
| // This ensures we test every value up to 128 (the max subgroup size). |
| return width + 1; |
| } |
| else |
| { |
| // And once we hit 128 we increment to only power of 2's to reduce testing time. |
| return width * 2; |
| } |
| } |
| |
| deUint32 getFormatSizeInBytes(const subgroups::Format format) |
| { |
| using namespace subgroups; |
| switch (format) |
| { |
| default: |
| DE_FATAL("Unhandled format!"); |
| return 0; |
| case FORMAT_R32_SINT: |
| case FORMAT_R32_UINT: |
| return sizeof(deInt32); |
| case FORMAT_R32G32_SINT: |
| case FORMAT_R32G32_UINT: |
| return static_cast<deUint32>(sizeof(deInt32) * 2); |
| case FORMAT_R32G32B32_SINT: |
| case FORMAT_R32G32B32_UINT: |
| case FORMAT_R32G32B32A32_SINT: |
| case FORMAT_R32G32B32A32_UINT: |
| return static_cast<deUint32>(sizeof(deInt32) * 4); |
| case FORMAT_R32_SFLOAT: |
| return 4; |
| case FORMAT_R32G32_SFLOAT: |
| return 8; |
| case FORMAT_R32G32B32_SFLOAT: |
| return 16; |
| case FORMAT_R32G32B32A32_SFLOAT: |
| return 16; |
| case FORMAT_R64_SFLOAT: |
| return 8; |
| case FORMAT_R64G64_SFLOAT: |
| return 16; |
| case FORMAT_R64G64B64_SFLOAT: |
| return 32; |
| case FORMAT_R64G64B64A64_SFLOAT: |
| return 32; |
| // The below formats are used to represent bool and bvec* types. These |
| // types are passed to the shader as int and ivec* types, before the |
| // calculations are done as booleans. We need a distinct type here so |
| // that the shader generators can switch on it and generate the correct |
| // shader source for testing. |
| case FORMAT_R32_BOOL: |
| return sizeof(deInt32); |
| case FORMAT_R32G32_BOOL: |
| return static_cast<deUint32>(sizeof(deInt32) * 2); |
| case FORMAT_R32G32B32_BOOL: |
| case FORMAT_R32G32B32A32_BOOL: |
| return static_cast<deUint32>(sizeof(deInt32) * 4); |
| } |
| } |
| |
| deUint32 getElementSizeInBytes( |
| const subgroups::Format format, |
| const subgroups::SSBOData::InputDataLayoutType layout) |
| { |
| deUint32 bytes = getFormatSizeInBytes(format); |
| if (layout == subgroups::SSBOData::LayoutStd140) |
| return bytes < 16 ? 16 : bytes; |
| else |
| return bytes; |
| } |
| |
| |
| de::MovePtr<glu::ShaderProgram> makeGraphicsPipeline(glc::Context& context, |
| const subgroups::ShaderStageFlags stages, |
| const GlslSource * vshader, |
| const GlslSource * fshader, |
| const GlslSource * gshader, |
| const GlslSource * tcshader, |
| const GlslSource * teshader) |
| { |
| tcu::TestLog& log = context.getDeqpContext().getTestContext().getLog(); |
| const bool doShaderLog = log.isShaderLoggingEnabled(); |
| DE_UNREF(stages); // only used for asserts |
| |
| map<string, string> templateArgs; |
| string versionDecl(getGLSLVersionDeclaration(context.getGLSLVersion())); |
| templateArgs.insert(pair<string, string>("VERSION_DECL", versionDecl)); |
| |
| string vertSource, tescSource, teseSource, geomSource, fragSource; |
| if (vshader) |
| { |
| DE_ASSERT(stages & subgroups::SHADER_STAGE_VERTEX_BIT); |
| tcu::StringTemplate shaderTemplate(vshader->sources[glu::SHADERTYPE_VERTEX][0]); |
| string shaderSource(shaderTemplate.specialize(templateArgs)); |
| if (doShaderLog) |
| { |
| log << tcu::TestLog::Message << "vertex shader:\n" |
| << shaderSource << "\n:end:" << tcu::TestLog::EndMessage; |
| } |
| vertSource = shaderSource; |
| } |
| if (tcshader) |
| { |
| DE_ASSERT(stages & subgroups::SHADER_STAGE_TESS_CONTROL_BIT); |
| tcu::StringTemplate shaderTemplate(tcshader->sources[glu::SHADERTYPE_TESSELLATION_CONTROL][0]); |
| string shaderSource(shaderTemplate.specialize(templateArgs)); |
| if (doShaderLog) |
| { |
| log << tcu::TestLog::Message << "tess control shader:\n" |
| << shaderSource << "\n:end:" << tcu::TestLog::EndMessage; |
| } |
| tescSource = shaderSource; |
| } |
| if (teshader) |
| { |
| DE_ASSERT(stages & subgroups::SHADER_STAGE_TESS_EVALUATION_BIT); |
| tcu::StringTemplate shaderTemplate(teshader->sources[glu::SHADERTYPE_TESSELLATION_EVALUATION][0]); |
| string shaderSource(shaderTemplate.specialize(templateArgs)); |
| if (doShaderLog) |
| { |
| log << tcu::TestLog::Message << "tess eval shader:\n" |
| << shaderSource << "\n:end:" << tcu::TestLog::EndMessage; |
| } |
| teseSource = shaderSource; |
| } |
| if (gshader) |
| { |
| DE_ASSERT(stages & subgroups::SHADER_STAGE_GEOMETRY_BIT); |
| tcu::StringTemplate shaderTemplate(gshader->sources[glu::SHADERTYPE_GEOMETRY][0]); |
| string shaderSource(shaderTemplate.specialize(templateArgs)); |
| if (doShaderLog) |
| { |
| log << tcu::TestLog::Message << "geometry shader:\n" |
| << shaderSource << "\n:end:" << tcu::TestLog::EndMessage; |
| } |
| geomSource = shaderSource; |
| } |
| if (fshader) |
| { |
| DE_ASSERT(stages & subgroups::SHADER_STAGE_FRAGMENT_BIT); |
| tcu::StringTemplate shaderTemplate(fshader->sources[glu::SHADERTYPE_FRAGMENT][0]); |
| string shaderSource(shaderTemplate.specialize(templateArgs)); |
| if (doShaderLog) |
| { |
| log << tcu::TestLog::Message << "fragment shader:\n" |
| << shaderSource << "\n:end:" << tcu::TestLog::EndMessage; |
| } |
| fragSource = shaderSource; |
| } |
| |
| glu::ShaderProgram *program = DE_NULL; |
| if(context.getShaderType() == SHADER_TYPE_GLSL) |
| { |
| glu::ProgramSources sources; |
| if (vshader) |
| sources << glu::VertexSource(vertSource); |
| if (tcshader) |
| sources << glu::TessellationControlSource(tescSource); |
| if (teshader) |
| sources << glu::TessellationEvaluationSource(teseSource); |
| if (gshader) |
| sources << glu::GeometrySource(geomSource); |
| if (fshader) |
| sources << glu::FragmentSource(fragSource); |
| |
| program = new glu::ShaderProgram(context.getDeqpContext().getRenderContext().getFunctions(), sources); |
| } else { |
| DE_ASSERT(context.getShaderType() == SHADER_TYPE_SPIRV); |
| |
| glu::ProgramBinaries binaries; |
| if (vshader) |
| binaries << spirvUtils::makeSpirV(log, glu::VertexSource(vertSource), spirvUtils::SPIRV_VERSION_1_3); |
| if (tcshader) |
| binaries << spirvUtils::makeSpirV(log, glu::TessellationControlSource(tescSource), spirvUtils::SPIRV_VERSION_1_3); |
| if (teshader) |
| binaries << spirvUtils::makeSpirV(log, glu::TessellationEvaluationSource(teseSource), spirvUtils::SPIRV_VERSION_1_3); |
| if (gshader) |
| binaries << spirvUtils::makeSpirV(log, glu::GeometrySource(geomSource), spirvUtils::SPIRV_VERSION_1_3); |
| if (fshader) |
| binaries << spirvUtils::makeSpirV(log, glu::FragmentSource(fragSource), spirvUtils::SPIRV_VERSION_1_3); |
| |
| program = new glu::ShaderProgram(context.getDeqpContext().getRenderContext().getFunctions(), binaries); |
| } |
| |
| if (!program->isOk()) |
| { |
| log << tcu::TestLog::Message << "Shader build failed.\n" |
| << "Vertex: " << (vshader ? program->getShaderInfo(glu::SHADERTYPE_VERTEX).infoLog : "n/a") << "\n" |
| << "Tess Cont: " << (tcshader ? program->getShaderInfo(glu::SHADERTYPE_TESSELLATION_CONTROL).infoLog : "n/a") << "\n" |
| << "Tess Eval: " << (teshader ? program->getShaderInfo(glu::SHADERTYPE_TESSELLATION_EVALUATION).infoLog : "n/a") << "\n" |
| << "Geometry: " << (gshader ? program->getShaderInfo(glu::SHADERTYPE_GEOMETRY).infoLog : "n/a") << "\n" |
| << "Fragment: " << (fshader ? program->getShaderInfo(glu::SHADERTYPE_FRAGMENT).infoLog : "n/a") << "\n" |
| << "Program: " << program->getProgramInfo().infoLog << tcu::TestLog::EndMessage; |
| } |
| return de::MovePtr<glu::ShaderProgram>(program); |
| } |
| |
| de::MovePtr<glu::ShaderProgram> makeComputePipeline(glc::Context& context, const GlslSource &glslTemplate, |
| deUint32 localSizeX, deUint32 localSizeY, deUint32 localSizeZ) |
| { |
| |
| tcu::TestLog& log = context.getDeqpContext().getTestContext().getLog(); |
| const bool doShaderLog = log.isShaderLoggingEnabled(); |
| |
| tcu::StringTemplate computeTemplate(glslTemplate.sources[glu::SHADERTYPE_COMPUTE][0]); |
| |
| map<string, string> templateArgs; |
| { |
| stringstream localSize; |
| localSize << "local_size_x = " << localSizeX; |
| templateArgs.insert(pair<string, string>("LOCAL_SIZE_X", localSize.str())); |
| } |
| { |
| stringstream localSize; |
| localSize << "local_size_y = " << localSizeY; |
| templateArgs.insert(pair<string, string>("LOCAL_SIZE_Y", localSize.str())); |
| } |
| { |
| stringstream localSize; |
| localSize << "local_size_z = " << localSizeZ; |
| templateArgs.insert(pair<string, string>("LOCAL_SIZE_Z", localSize.str())); |
| } |
| string versionDecl(getGLSLVersionDeclaration(context.getGLSLVersion())); |
| templateArgs.insert(pair<string, string>("VERSION_DECL", versionDecl)); |
| |
| glu::ComputeSource cshader(glu::ComputeSource(computeTemplate.specialize(templateArgs))); |
| |
| if (doShaderLog) |
| { |
| log << tcu::TestLog::Message << "compute shader specialized source:\n" |
| << cshader.source << "\n:end:" << tcu::TestLog::EndMessage; |
| } |
| |
| glu::ShaderProgram *program = DE_NULL; |
| if(context.getShaderType() == SHADER_TYPE_GLSL) |
| { |
| glu::ProgramSources sources; |
| sources << cshader; |
| program = new glu::ShaderProgram(context.getDeqpContext().getRenderContext().getFunctions(), sources); |
| } else { |
| DE_ASSERT(context.getShaderType() == SHADER_TYPE_SPIRV); |
| |
| glu::ProgramBinaries binaries; |
| binaries << spirvUtils::makeSpirV(log, cshader, spirvUtils::SPIRV_VERSION_1_3); |
| |
| program = new glu::ShaderProgram(context.getDeqpContext().getRenderContext().getFunctions(), binaries); |
| } |
| |
| if (!program->isOk()) |
| { |
| log << tcu::TestLog::Message << "Shader build failed.\n" |
| << "Compute: " << program->getShaderInfo(glu::SHADERTYPE_COMPUTE).infoLog << "\n" |
| << "Program: " << program->getProgramInfo().infoLog << tcu::TestLog::EndMessage; |
| } |
| return de::MovePtr<glu::ShaderProgram>(program); |
| } |
| |
| struct Buffer; |
| struct Image; |
| |
| struct BufferOrImage |
| { |
| bool isImage() const |
| { |
| return m_isImage; |
| } |
| |
| Buffer* getAsBuffer() |
| { |
| if (m_isImage) DE_FATAL("Trying to get a buffer as an image!"); |
| return reinterpret_cast<Buffer* >(this); |
| } |
| |
| Image* getAsImage() |
| { |
| if (!m_isImage) DE_FATAL("Trying to get an image as a buffer!"); |
| return reinterpret_cast<Image*>(this); |
| } |
| |
| virtual subgroups::DescriptorType getType() const |
| { |
| if (m_isImage) |
| { |
| return subgroups::DESCRIPTOR_TYPE_STORAGE_IMAGE; |
| } |
| else |
| { |
| return subgroups::DESCRIPTOR_TYPE_STORAGE_BUFFER; |
| } |
| } |
| |
| GLuint getId() |
| { |
| return m_objectId; |
| } |
| |
| virtual ~BufferOrImage() {} |
| |
| protected: |
| explicit BufferOrImage(glc::Context& context, bool image) |
| : m_gl(context.getDeqpContext().getRenderContext().getFunctions()) |
| , m_isImage(image) |
| , m_objectId(0) {} |
| |
| const glw::Functions & m_gl; |
| bool m_isImage; |
| GLuint m_objectId; |
| }; |
| |
| struct Buffer : public BufferOrImage |
| { |
| explicit Buffer( |
| glc::Context& context, deUint64 sizeInBytes, GLenum target = GL_SHADER_STORAGE_BUFFER) |
| : BufferOrImage (context, false) |
| , m_sizeInBytes (sizeInBytes) |
| , m_target (target) |
| { |
| m_gl.genBuffers(1, &m_objectId); |
| GLU_EXPECT_NO_ERROR(m_gl.getError(), "genBuffers"); |
| m_gl.bindBuffer(m_target, m_objectId); |
| GLU_EXPECT_NO_ERROR(m_gl.getError(), "bindBuffer"); |
| m_gl.bufferData(m_target, m_sizeInBytes, NULL, GL_DYNAMIC_DRAW); |
| GLU_EXPECT_NO_ERROR(m_gl.getError(), "bufferData"); |
| m_gl.bindBuffer(m_target, 0); |
| GLU_EXPECT_NO_ERROR(m_gl.getError(), "bindBuffer(0)"); |
| } |
| |
| virtual ~Buffer() |
| { |
| if (m_objectId != 0) |
| { |
| m_gl.deleteBuffers(1, &m_objectId); |
| GLU_EXPECT_NO_ERROR(m_gl.getError(), "glDeleteBuffers"); |
| } |
| } |
| |
| virtual subgroups::DescriptorType getType() const |
| { |
| if (GL_UNIFORM_BUFFER == m_target) |
| { |
| return subgroups::DESCRIPTOR_TYPE_UNIFORM_BUFFER; |
| } |
| return subgroups::DESCRIPTOR_TYPE_STORAGE_BUFFER; |
| } |
| |
| glw::GLvoid* mapBufferPtr() { |
| glw::GLvoid *ptr; |
| |
| m_gl.bindBuffer(m_target, m_objectId); |
| GLU_EXPECT_NO_ERROR(m_gl.getError(), "glBindBuffer"); |
| |
| ptr = m_gl.mapBufferRange(m_target, 0, m_sizeInBytes, GL_MAP_READ_BIT | GL_MAP_WRITE_BIT); |
| GLU_EXPECT_NO_ERROR(m_gl.getError(), "glMapBuffer"); |
| |
| m_gl.bindBuffer(m_target, 0); |
| GLU_EXPECT_NO_ERROR(m_gl.getError(), "glBindBuffer(0)"); |
| |
| return ptr; |
| } |
| |
| void unmapBufferPtr() { |
| m_gl.bindBuffer(m_target, m_objectId); |
| GLU_EXPECT_NO_ERROR(m_gl.getError(), "glBindBuffer"); |
| |
| m_gl.unmapBuffer(m_target); |
| GLU_EXPECT_NO_ERROR(m_gl.getError(), "glUnmapBuffer"); |
| |
| m_gl.bindBuffer(m_target, 0); |
| GLU_EXPECT_NO_ERROR(m_gl.getError(), "glBindBuffer(0)"); |
| } |
| |
| deUint64 getSize() const { |
| return m_sizeInBytes; |
| } |
| |
| private: |
| deUint64 m_sizeInBytes; |
| const GLenum m_target; |
| }; |
| |
| struct Image : public BufferOrImage |
| { |
| explicit Image(glc::Context& context, deUint32 width, deUint32 height, |
| subgroups::Format format) |
| : BufferOrImage(context, true) |
| { |
| m_gl.genTextures(1, &m_objectId); |
| GLU_EXPECT_NO_ERROR(m_gl.getError(), "glGenTextures"); |
| m_gl.bindTexture(GL_TEXTURE_2D, m_objectId); |
| GLU_EXPECT_NO_ERROR(m_gl.getError(), "glBindTexture"); |
| m_gl.texStorage2D(GL_TEXTURE_2D, 1, format, width, height); |
| GLU_EXPECT_NO_ERROR(m_gl.getError(), "glTexStorage2D"); |
| } |
| |
| virtual ~Image() |
| { |
| if (m_objectId != 0) |
| { |
| m_gl.deleteTextures(1, &m_objectId); |
| GLU_EXPECT_NO_ERROR(m_gl.getError(), "glDeleteTextures"); |
| } |
| } |
| |
| private: |
| }; |
| |
| struct Vao |
| { |
| explicit Vao(glc::Context& context) |
| : m_gl(context.getDeqpContext().getRenderContext().getFunctions()) |
| , m_objectId(0) |
| { |
| m_gl.genVertexArrays(1, &m_objectId); |
| GLU_EXPECT_NO_ERROR(m_gl.getError(), "glGenVertexArrays"); |
| m_gl.bindVertexArray(m_objectId); |
| GLU_EXPECT_NO_ERROR(m_gl.getError(), "glBindVertexArray"); |
| } |
| |
| ~Vao() |
| { |
| if (m_objectId != 0) |
| { |
| m_gl.deleteVertexArrays(1, &m_objectId); |
| GLU_EXPECT_NO_ERROR(m_gl.getError(), "glDeleteVertexArrays"); |
| } |
| } |
| |
| private: |
| const glw::Functions & m_gl; |
| GLuint m_objectId; |
| }; |
| |
| struct Fbo |
| { |
| explicit Fbo(glc::Context& context) |
| : m_gl(context.getDeqpContext().getRenderContext().getFunctions()) |
| , m_objectId(0) |
| { |
| m_gl.genFramebuffers(1, &m_objectId); |
| GLU_EXPECT_NO_ERROR(m_gl.getError(), "glGenFramebuffers"); |
| m_gl.bindFramebuffer(GL_FRAMEBUFFER, m_objectId); |
| GLU_EXPECT_NO_ERROR(m_gl.getError(), "glBindFramebuffer"); |
| } |
| |
| ~Fbo() |
| { |
| if (m_objectId != 0) |
| { |
| m_gl.deleteFramebuffers(1, &m_objectId); |
| GLU_EXPECT_NO_ERROR(m_gl.getError(), "deleteFramebuffers"); |
| } |
| } |
| |
| void bind2D(Image &img) |
| { |
| m_gl.framebufferTexture2D(GL_FRAMEBUFFER, GL_COLOR_ATTACHMENT0, GL_TEXTURE_2D, img.getId(), 0); |
| GLU_EXPECT_NO_ERROR(m_gl.getError(), "glFramebufferTexture2D"); |
| } |
| |
| private: |
| const glw::Functions & m_gl; |
| GLuint m_objectId; |
| |
| }; |
| } |
| |
| std::string glc::subgroups::getSharedMemoryBallotHelper() |
| { |
| return "shared uvec4 superSecretComputeShaderHelper[gl_WorkGroupSize.x * gl_WorkGroupSize.y * gl_WorkGroupSize.z];\n" |
| "uvec4 sharedMemoryBallot(bool vote)\n" |
| "{\n" |
| " uint groupOffset = gl_SubgroupID;\n" |
| " // One invocation in the group 0's the whole group's data\n" |
| " if (subgroupElect())\n" |
| " {\n" |
| " superSecretComputeShaderHelper[groupOffset] = uvec4(0);\n" |
| " }\n" |
| " subgroupMemoryBarrierShared();\n" |
| " if (vote)\n" |
| " {\n" |
| " highp uint invocationId = gl_SubgroupInvocationID % 32u;\n" |
| " highp uint bitToSet = 1u << invocationId;\n" |
| " switch (gl_SubgroupInvocationID / 32u)\n" |
| " {\n" |
| " case 0u: atomicOr(superSecretComputeShaderHelper[groupOffset].x, bitToSet); break;\n" |
| " case 1u: atomicOr(superSecretComputeShaderHelper[groupOffset].y, bitToSet); break;\n" |
| " case 2u: atomicOr(superSecretComputeShaderHelper[groupOffset].z, bitToSet); break;\n" |
| " case 3u: atomicOr(superSecretComputeShaderHelper[groupOffset].w, bitToSet); break;\n" |
| " }\n" |
| " }\n" |
| " subgroupMemoryBarrierShared();\n" |
| " return superSecretComputeShaderHelper[groupOffset];\n" |
| "}\n"; |
| } |
| |
| deUint32 glc::subgroups::getSubgroupSize(Context& context) |
| { |
| int subgroupSize = context.getDeqpContext().getContextInfo().getInt(GL_SUBGROUP_SIZE_KHR); |
| |
| return subgroupSize; |
| } |
| |
| deUint32 glc::subgroups::maxSupportedSubgroupSize() { |
| return 128u; |
| } |
| |
| std::string glc::subgroups::getShaderStageName(ShaderStageFlags stage) |
| { |
| DE_ASSERT(stage & SHADER_STAGE_ALL_VALID); |
| switch (stage) |
| { |
| default: |
| DE_FATAL("Unhandled stage!"); |
| return ""; |
| case SHADER_STAGE_COMPUTE_BIT: |
| return "compute"; |
| case SHADER_STAGE_FRAGMENT_BIT: |
| return "fragment"; |
| case SHADER_STAGE_VERTEX_BIT: |
| return "vertex"; |
| case SHADER_STAGE_GEOMETRY_BIT: |
| return "geometry"; |
| case SHADER_STAGE_TESS_CONTROL_BIT: |
| return "tess_control"; |
| case SHADER_STAGE_TESS_EVALUATION_BIT: |
| return "tess_eval"; |
| } |
| } |
| |
| std::string glc::subgroups::getSubgroupFeatureName(SubgroupFeatureFlags bit) |
| { |
| DE_ASSERT(bit & SUBGROUP_FEATURE_ALL_VALID); |
| switch (bit) |
| { |
| default: |
| DE_FATAL("Unknown subgroup feature category!"); |
| return ""; |
| case SUBGROUP_FEATURE_BASIC_BIT: |
| return "GL_SUBGROUP_FEATURE_BASIC_BIT_KHR"; |
| case SUBGROUP_FEATURE_VOTE_BIT: |
| return "GL_SUBGROUP_FEATURE_VOTE_BIT_KHR"; |
| case SUBGROUP_FEATURE_ARITHMETIC_BIT: |
| return "GL_SUBGROUP_FEATURE_ARITHMETIC_BIT_KHR"; |
| case SUBGROUP_FEATURE_BALLOT_BIT: |
| return "GL_SUBGROUP_FEATURE_BALLOT_BIT_KHR"; |
| case SUBGROUP_FEATURE_SHUFFLE_BIT: |
| return "GL_SUBGROUP_FEATURE_SHUFFLE_BIT_KHR"; |
| case SUBGROUP_FEATURE_SHUFFLE_RELATIVE_BIT: |
| return "GL_SUBGROUP_FEATURE_SHUFFLE_RELATIVE_BIT_KHR"; |
| case SUBGROUP_FEATURE_CLUSTERED_BIT: |
| return "GL_SUBGROUP_FEATURE_CLUSTERED_BIT_KHR"; |
| case SUBGROUP_FEATURE_QUAD_BIT: |
| return "GL_SUBGROUP_FEATURE_QUAD_BIT_KHR"; |
| case SUBGROUP_FEATURE_PARTITIONED_BIT_NV: |
| return "GL_SUBGROUP_FEATURE_PARTITIONED_BIT_NV"; |
| } |
| } |
| |
| void glc::subgroups::addNoSubgroupShader (SourceCollections& programCollection) |
| { |
| { |
| const std::string vertNoSubgroupGLSL = |
| "${VERSION_DECL}\n" |
| "void main (void)\n" |
| "{\n" |
| " float pixelSize = 2.0f/1024.0f;\n" |
| " float pixelPosition = pixelSize/2.0f - 1.0f;\n" |
| " gl_Position = vec4(float(gl_VertexID) * pixelSize + pixelPosition, 0.0f, 0.0f, 1.0f);\n" |
| " gl_PointSize = 1.0f;\n" |
| "}\n"; |
| programCollection.add("vert_noSubgroup") << glu::VertexSource(vertNoSubgroupGLSL); |
| } |
| |
| { |
| const std::string tescNoSubgroupGLSL = |
| "${VERSION_DECL}\n" |
| "layout(vertices=1) out;\n" |
| "\n" |
| "void main (void)\n" |
| "{\n" |
| " if (gl_InvocationID == 0)\n" |
| " {\n" |
| " gl_TessLevelOuter[0] = 1.0f;\n" |
| " gl_TessLevelOuter[1] = 1.0f;\n" |
| " }\n" |
| " gl_out[gl_InvocationID].gl_Position = gl_in[gl_InvocationID].gl_Position;\n" |
| "}\n"; |
| programCollection.add("tesc_noSubgroup") << glu::TessellationControlSource(tescNoSubgroupGLSL); |
| } |
| |
| { |
| const std::string teseNoSubgroupGLSL = |
| "${VERSION_DECL}\n" |
| "layout(isolines) in;\n" |
| "\n" |
| "void main (void)\n" |
| "{\n" |
| " float pixelSize = 2.0f/1024.0f;\n" |
| " gl_Position = gl_in[0].gl_Position + gl_TessCoord.x * pixelSize / 2.0f;\n" |
| "}\n"; |
| programCollection.add("tese_noSubgroup") << glu::TessellationEvaluationSource(teseNoSubgroupGLSL); |
| } |
| |
| } |
| |
| std::string glc::subgroups::getVertShaderForStage(const ShaderStageFlags stage) |
| { |
| DE_ASSERT(stage & SHADER_STAGE_ALL_VALID); |
| switch (stage) |
| { |
| default: |
| DE_FATAL("Unhandled stage!"); |
| return ""; |
| case SHADER_STAGE_FRAGMENT_BIT: |
| return |
| "${VERSION_DECL}\n" |
| "void main (void)\n" |
| "{\n" |
| " float pixelSize = 2.0f/1024.0f;\n" |
| " float pixelPosition = pixelSize/2.0f - 1.0f;\n" |
| " gl_Position = vec4(float(gl_VertexID) * pixelSize + pixelPosition, 0.0f, 0.0f, 1.0f);\n" |
| "}\n"; |
| case SHADER_STAGE_GEOMETRY_BIT: |
| return |
| "${VERSION_DECL}\n" |
| "void main (void)\n" |
| "{\n" |
| "}\n"; |
| case SHADER_STAGE_TESS_CONTROL_BIT: |
| case SHADER_STAGE_TESS_EVALUATION_BIT: |
| return |
| "${VERSION_DECL}\n" |
| "void main (void)\n" |
| "{\n" |
| "}\n"; |
| } |
| } |
| |
| bool glc::subgroups::isSubgroupSupported(Context& context) |
| { |
| return context.getDeqpContext().getContextInfo().isExtensionSupported("GL_KHR_shader_subgroup"); |
| } |
| |
| bool glc::subgroups::areSubgroupOperationsSupportedForStage( |
| Context& context, const ShaderStageFlags stage) |
| { |
| DE_ASSERT(stage & SHADER_STAGE_ALL_VALID); |
| int supportedStages = context.getDeqpContext().getContextInfo().getInt(GL_SUBGROUP_SUPPORTED_STAGES_KHR); |
| |
| return (stage & supportedStages) ? true : false; |
| } |
| |
| bool glc::subgroups::areSubgroupOperationsRequiredForStage( |
| const ShaderStageFlags stage) |
| { |
| DE_ASSERT(stage & SHADER_STAGE_ALL_VALID); |
| switch (stage) |
| { |
| default: |
| return false; |
| case SHADER_STAGE_COMPUTE_BIT: |
| return true; |
| } |
| } |
| |
| bool glc::subgroups::isSubgroupFeatureSupportedForDevice( |
| Context& context, |
| const SubgroupFeatureFlags bit) |
| { |
| DE_ASSERT(bit & SUBGROUP_FEATURE_ALL_VALID); |
| |
| int supportedOperations = context.getDeqpContext().getContextInfo().getInt(GL_SUBGROUP_SUPPORTED_FEATURES_KHR); |
| |
| return (bit & supportedOperations) ? true : false; |
| } |
| |
| bool glc::subgroups::isFragmentSSBOSupportedForDevice(Context& context) |
| { |
| int numFragmentSSBOs = context.getDeqpContext().getContextInfo().getInt(GL_MAX_FRAGMENT_SHADER_STORAGE_BLOCKS); |
| |
| return (numFragmentSSBOs > 0) ? true : false; |
| } |
| |
| bool glc::subgroups::isVertexSSBOSupportedForDevice(Context& context) |
| { |
| int numVertexSSBOs = context.getDeqpContext().getContextInfo().getInt(GL_MAX_VERTEX_SHADER_STORAGE_BLOCKS); |
| |
| return (numVertexSSBOs > 0) ? true : false; |
| } |
| |
| bool glc::subgroups::isImageSupportedForStageOnDevice(Context& context, const ShaderStageFlags stage) |
| { |
| glw::GLint stageQuery; |
| DE_ASSERT(stage & SHADER_STAGE_ALL_VALID); |
| |
| // image uniforms are optional in VTG stages |
| switch (stage) |
| { |
| case SHADER_STAGE_FRAGMENT_BIT: |
| case SHADER_STAGE_COMPUTE_BIT: |
| default: |
| return true; |
| case SHADER_STAGE_VERTEX_BIT: |
| stageQuery = GL_MAX_VERTEX_IMAGE_UNIFORMS; |
| break; |
| case SHADER_STAGE_TESS_CONTROL_BIT: |
| stageQuery = GL_MAX_TESS_CONTROL_IMAGE_UNIFORMS; |
| break; |
| case SHADER_STAGE_TESS_EVALUATION_BIT: |
| stageQuery = GL_MAX_TESS_EVALUATION_IMAGE_UNIFORMS; |
| break; |
| case SHADER_STAGE_GEOMETRY_BIT: |
| stageQuery = GL_MAX_GEOMETRY_IMAGE_UNIFORMS; |
| break; |
| } |
| |
| int numImages = context.getDeqpContext().getContextInfo().getInt(stageQuery); |
| |
| return (numImages > 0) ? true : false; |
| } |
| |
| bool glc::subgroups::isDoubleSupportedForDevice(Context& context) |
| { |
| glu::ContextType contextType = context.getDeqpContext().getRenderContext().getType(); |
| return (glu::contextSupports(contextType, glu::ApiType::core(4, 0)) || |
| context.getDeqpContext().getContextInfo().isExtensionSupported("GL_ARB_gpu_shader_fp64")); |
| } |
| |
| bool glc::subgroups::isDoubleFormat(Format format) |
| { |
| switch (format) |
| { |
| default: |
| return false; |
| case FORMAT_R64_SFLOAT: |
| case FORMAT_R64G64_SFLOAT: |
| case FORMAT_R64G64B64_SFLOAT: |
| case FORMAT_R64G64B64A64_SFLOAT: |
| return true; |
| } |
| } |
| |
| std::string glc::subgroups::getFormatNameForGLSL (Format format) |
| { |
| switch (format) |
| { |
| default: |
| DE_FATAL("Unhandled format!"); |
| return ""; |
| case FORMAT_R32_SINT: |
| return "int"; |
| case FORMAT_R32G32_SINT: |
| return "ivec2"; |
| case FORMAT_R32G32B32_SINT: |
| return "ivec3"; |
| case FORMAT_R32G32B32A32_SINT: |
| return "ivec4"; |
| case FORMAT_R32_UINT: |
| return "uint"; |
| case FORMAT_R32G32_UINT: |
| return "uvec2"; |
| case FORMAT_R32G32B32_UINT: |
| return "uvec3"; |
| case FORMAT_R32G32B32A32_UINT: |
| return "uvec4"; |
| case FORMAT_R32_SFLOAT: |
| return "float"; |
| case FORMAT_R32G32_SFLOAT: |
| return "vec2"; |
| case FORMAT_R32G32B32_SFLOAT: |
| return "vec3"; |
| case FORMAT_R32G32B32A32_SFLOAT: |
| return "vec4"; |
| case FORMAT_R64_SFLOAT: |
| return "double"; |
| case FORMAT_R64G64_SFLOAT: |
| return "dvec2"; |
| case FORMAT_R64G64B64_SFLOAT: |
| return "dvec3"; |
| case FORMAT_R64G64B64A64_SFLOAT: |
| return "dvec4"; |
| case FORMAT_R32_BOOL: |
| return "bool"; |
| case FORMAT_R32G32_BOOL: |
| return "bvec2"; |
| case FORMAT_R32G32B32_BOOL: |
| return "bvec3"; |
| case FORMAT_R32G32B32A32_BOOL: |
| return "bvec4"; |
| } |
| } |
| |
| void glc::subgroups::setVertexShaderFrameBuffer (SourceCollections& programCollection) |
| { |
| programCollection.add("vert") << glu::VertexSource( |
| "${VERSION_DECL}\n" |
| "layout(location = 0) in highp vec4 in_position;\n" |
| "void main (void)\n" |
| "{\n" |
| " gl_Position = in_position;\n" |
| "}\n"); |
| } |
| |
| void glc::subgroups::setFragmentShaderFrameBuffer (SourceCollections& programCollection) |
| { |
| programCollection.add("fragment") << glu::FragmentSource( |
| "${VERSION_DECL}\n" |
| "precision highp int;\n" |
| "layout(location = 0) in highp float in_color;\n" |
| "layout(location = 0) out uint out_color;\n" |
| "void main()\n" |
| "{\n" |
| " out_color = uint(in_color);\n" |
| "}\n"); |
| } |
| |
| void glc::subgroups::setTesCtrlShaderFrameBuffer (SourceCollections& programCollection) |
| { |
| programCollection.add("tesc") << glu::TessellationControlSource( |
| "${VERSION_DECL}\n" |
| "#extension GL_KHR_shader_subgroup_basic: enable\n" |
| "#extension GL_EXT_tessellation_shader : require\n" |
| "layout(vertices = 2) out;\n" |
| "void main (void)\n" |
| "{\n" |
| " if (gl_InvocationID == 0)\n" |
| " {\n" |
| " gl_TessLevelOuter[0] = 1.0f;\n" |
| " gl_TessLevelOuter[1] = 1.0f;\n" |
| " }\n" |
| " gl_out[gl_InvocationID].gl_Position = gl_in[gl_InvocationID].gl_Position;\n" |
| "}\n"); |
| } |
| |
| void glc::subgroups::setTesEvalShaderFrameBuffer (SourceCollections& programCollection) |
| { |
| programCollection.add("tese") << glu::TessellationEvaluationSource( |
| "${VERSION_DECL}\n" |
| "#extension GL_KHR_shader_subgroup_ballot: enable\n" |
| "#extension GL_EXT_tessellation_shader : require\n" |
| "layout(isolines, equal_spacing, ccw ) in;\n" |
| "layout(location = 0) in float in_color[];\n" |
| "layout(location = 0) out float out_color;\n" |
| "\n" |
| "void main (void)\n" |
| "{\n" |
| " gl_Position = mix(gl_in[0].gl_Position, gl_in[1].gl_Position, gl_TessCoord.x);\n" |
| " out_color = in_color[0];\n" |
| "}\n"); |
| } |
| |
| void glc::subgroups::addGeometryShadersFromTemplate (const std::string& glslTemplate, SourceCollections& collection) |
| { |
| tcu::StringTemplate geometryTemplate(glslTemplate); |
| |
| map<string, string> linesParams; |
| linesParams.insert(pair<string, string>("TOPOLOGY", "lines")); |
| |
| map<string, string> pointsParams; |
| pointsParams.insert(pair<string, string>("TOPOLOGY", "points")); |
| |
| collection.add("geometry_lines") << glu::GeometrySource("${VERSION_DECL}\n" + geometryTemplate.specialize(linesParams)); |
| collection.add("geometry_points") << glu::GeometrySource("${VERSION_DECL}\n" + geometryTemplate.specialize(pointsParams)); |
| } |
| |
| void initializeMemory(deqp::Context& context, glw::GLvoid *hostPtr, subgroups::SSBOData& data) |
| { |
| using namespace subgroups; |
| const Format format = data.format; |
| const deUint64 size = data.numElements * |
| (data.isImage ? getFormatSizeInBytes(format) : getElementSizeInBytes(format, data.layout)); |
| if (subgroups::SSBOData::InitializeNonZero == data.initializeType) |
| { |
| de::Random rnd(context.getTestContext().getCommandLine().getBaseSeed()); |
| switch (format) |
| { |
| default: |
| DE_FATAL("Illegal buffer format"); |
| break; |
| case FORMAT_R32_BOOL: |
| case FORMAT_R32G32_BOOL: |
| case FORMAT_R32G32B32_BOOL: |
| case FORMAT_R32G32B32A32_BOOL: |
| { |
| deUint32* ptr = reinterpret_cast<deUint32*>(hostPtr); |
| |
| for (deUint64 k = 0; k < (size / sizeof(deUint32)); k++) |
| { |
| deUint32 r = rnd.getUint32(); |
| ptr[k] = (r & 1) ? r : 0; |
| } |
| } |
| break; |
| case FORMAT_R32_SINT: |
| case FORMAT_R32G32_SINT: |
| case FORMAT_R32G32B32_SINT: |
| case FORMAT_R32G32B32A32_SINT: |
| case FORMAT_R32_UINT: |
| case FORMAT_R32G32_UINT: |
| case FORMAT_R32G32B32_UINT: |
| case FORMAT_R32G32B32A32_UINT: |
| { |
| deUint32* ptr = reinterpret_cast<deUint32*>(hostPtr); |
| |
| for (deUint64 k = 0; k < (size / sizeof(deUint32)); k++) |
| { |
| ptr[k] = rnd.getUint32(); |
| } |
| } |
| break; |
| case FORMAT_R32_SFLOAT: |
| case FORMAT_R32G32_SFLOAT: |
| case FORMAT_R32G32B32_SFLOAT: |
| case FORMAT_R32G32B32A32_SFLOAT: |
| { |
| float* ptr = reinterpret_cast<float*>(hostPtr); |
| |
| for (deUint64 k = 0; k < (size / sizeof(float)); k++) |
| { |
| ptr[k] = rnd.getFloat(); |
| } |
| } |
| break; |
| case FORMAT_R64_SFLOAT: |
| case FORMAT_R64G64_SFLOAT: |
| case FORMAT_R64G64B64_SFLOAT: |
| case FORMAT_R64G64B64A64_SFLOAT: |
| { |
| double* ptr = reinterpret_cast<double*>(hostPtr); |
| |
| for (deUint64 k = 0; k < (size / sizeof(double)); k++) |
| { |
| ptr[k] = rnd.getDouble(); |
| } |
| } |
| break; |
| } |
| } |
| else if (subgroups::SSBOData::InitializeZero == data.initializeType) |
| { |
| deUint32* ptr = reinterpret_cast<deUint32*>(hostPtr); |
| |
| for (deUint64 k = 0; k < size / 4; k++) |
| { |
| ptr[k] = 0; |
| } |
| } |
| |
| if (subgroups::SSBOData::InitializeNone != data.initializeType) |
| { |
| // nothing to do for GL |
| } |
| } |
| |
| deUint32 getResultBinding (const glc::subgroups::ShaderStageFlags shaderStage) |
| { |
| using namespace glc::subgroups; |
| switch(shaderStage) |
| { |
| case SHADER_STAGE_VERTEX_BIT: |
| return 0u; |
| break; |
| case SHADER_STAGE_TESS_CONTROL_BIT: |
| return 1u; |
| break; |
| case SHADER_STAGE_TESS_EVALUATION_BIT: |
| return 2u; |
| break; |
| case SHADER_STAGE_GEOMETRY_BIT: |
| return 3u; |
| break; |
| default: |
| DE_ASSERT(0); |
| return -1; |
| } |
| DE_ASSERT(0); |
| return -1; |
| } |
| |
| tcu::TestStatus glc::subgroups::makeTessellationEvaluationFrameBufferTest( |
| Context& context, Format format, SSBOData* extraData, |
| deUint32 extraDataCount, |
| bool (*checkResult)(std::vector<const void*> datas, deUint32 width, deUint32 subgroupSize), |
| const ShaderStageFlags shaderStage) |
| { |
| tcu::TestLog& log = context.getDeqpContext().getTestContext().getLog(); |
| const glw::Functions& gl = context.getDeqpContext().getRenderContext().getFunctions(); |
| |
| const deUint32 maxWidth = getMaxWidth(); |
| vector<de::SharedPtr<BufferOrImage> > inputBuffers (extraDataCount); |
| |
| const GlslSource& vshader = context.getSourceCollection().get("vert"); |
| const GlslSource& tcshader = context.getSourceCollection().get("tesc"); |
| const GlslSource& teshader = context.getSourceCollection().get("tese"); |
| const GlslSource& fshader = context.getSourceCollection().get("fragment"); |
| |
| for (deUint32 i = 0u; i < extraDataCount; i++) |
| { |
| if (extraData[i].isImage) |
| { |
| inputBuffers[i] = de::SharedPtr<BufferOrImage>(new Image(context, static_cast<deUint32>(extraData[i].numElements), 1u, extraData[i].format)); |
| // haven't implemented init for images yet |
| DE_ASSERT(extraData[i].initializeType == subgroups::SSBOData::InitializeNone); |
| } |
| else |
| { |
| deUint64 size = getElementSizeInBytes(extraData[i].format, extraData[i].layout) * extraData[i].numElements; |
| inputBuffers[i] = de::SharedPtr<BufferOrImage>(new Buffer(context, size, GL_UNIFORM_BUFFER)); |
| |
| glw::GLvoid *ptr = inputBuffers[i]->getAsBuffer()->mapBufferPtr(); |
| initializeMemory(context.getDeqpContext(), ptr, extraData[i]); |
| inputBuffers[i]->getAsBuffer()->unmapBufferPtr(); |
| } |
| } |
| |
| for (deUint32 ndx = 0u; ndx < extraDataCount; ndx++) |
| { |
| log << tcu::TestLog::Message |
| << "binding inputBuffers[" << ndx << "](" << inputBuffers[ndx]->getType() << ", " << inputBuffers[ndx]->getId() << " ), " |
| << "stage = " << shaderStage << " , binding = " << extraData[ndx].binding << "\n" |
| << tcu::TestLog::EndMessage; |
| |
| if (inputBuffers[ndx]->isImage()) |
| { |
| gl.bindImageTexture(extraData[ndx].binding, inputBuffers[ndx]->getId(), |
| 0, GL_FALSE, 0, GL_READ_ONLY, extraData[ndx].format); |
| GLU_EXPECT_NO_ERROR(gl.getError(), "glBindImageTexture()"); |
| } else |
| { |
| gl.bindBufferBase(inputBuffers[ndx]->getType(), extraData[ndx].binding, inputBuffers[ndx]->getId()); |
| GLU_EXPECT_NO_ERROR(gl.getError(), "glBindBufferBase()"); |
| } |
| } |
| |
| de::MovePtr<glu::ShaderProgram> pipeline( |
| makeGraphicsPipeline(context, (ShaderStageFlags)(SHADER_STAGE_VERTEX_BIT | SHADER_STAGE_FRAGMENT_BIT | SHADER_STAGE_TESS_CONTROL_BIT | SHADER_STAGE_TESS_EVALUATION_BIT), |
| &vshader, &fshader, DE_NULL, &tcshader, &teshader)); |
| if (!pipeline->isOk()) |
| { |
| return tcu::TestStatus::fail("tese graphics program build failed"); |
| } |
| |
| const deUint32 subgroupSize = getSubgroupSize(context); |
| const deUint64 vertexBufferSize = 2ull * maxWidth * sizeof(tcu::Vec4); |
| Buffer vertexBuffer (context, vertexBufferSize, GL_ARRAY_BUFFER); |
| unsigned totalIterations = 0u; |
| unsigned failedIterations = 0u; |
| Image discardableImage (context, maxWidth, 1u, format); |
| |
| { |
| glw::GLvoid * bufferPtr = vertexBuffer.mapBufferPtr(); |
| std::vector<tcu::Vec4> data (2u * maxWidth, tcu::Vec4(1.0f, 0.0f, 1.0f, 1.0f)); |
| const float pixelSize = 2.0f / static_cast<float>(maxWidth); |
| float leftHandPosition = -1.0f; |
| |
| for(deUint32 ndx = 0u; ndx < data.size(); ndx+=2u) |
| { |
| data[ndx][0] = leftHandPosition; |
| leftHandPosition += pixelSize; |
| data[ndx+1][0] = leftHandPosition; |
| } |
| |
| deMemcpy(bufferPtr, &data[0], data.size() * sizeof(tcu::Vec4)); |
| vertexBuffer.unmapBufferPtr(); |
| } |
| |
| Vao vao(context); |
| Fbo fbo(context); |
| fbo.bind2D(discardableImage); |
| |
| gl.viewport(0, 0, maxWidth, 1u); |
| GLU_EXPECT_NO_ERROR(gl.getError(), "glViewport"); |
| |
| const deUint64 imageResultSize = getFormatSizeInBytes(format) * maxWidth; |
| vector<glw::GLubyte> imageBufferResult(imageResultSize); |
| const deUint64 vertexBufferOffset = 0u; |
| |
| for (deUint32 width = 1u; width < maxWidth; width = getNextWidth(width)) |
| { |
| totalIterations++; |
| |
| { |
| gl.clearColor(0.0f, 0.0f, 0.0f, 0.0f); |
| GLU_EXPECT_NO_ERROR(gl.getError(), "glClearColor"); |
| gl.clear(GL_COLOR_BUFFER_BIT); |
| GLU_EXPECT_NO_ERROR(gl.getError(), "glClear"); |
| |
| gl.useProgram(pipeline->getProgram()); |
| GLU_EXPECT_NO_ERROR(gl.getError(), "glUseProgram"); |
| |
| gl.enableVertexAttribArray(0); |
| GLU_EXPECT_NO_ERROR(gl.getError(), "glEnableVertexAttribArray"); |
| |
| gl.bindBuffer(GL_ARRAY_BUFFER, vertexBuffer.getId()); |
| GLU_EXPECT_NO_ERROR(gl.getError(), "glBindBuffer"); |
| |
| gl.vertexAttribPointer(0, 4, GL_FLOAT, GL_FALSE, sizeof(tcu::Vec4), glu::BufferOffsetAsPointer(vertexBufferOffset)); |
| GLU_EXPECT_NO_ERROR(gl.getError(), "glVertexAttribPointer"); |
| |
| gl.patchParameteri(GL_PATCH_VERTICES, 2u); |
| GLU_EXPECT_NO_ERROR(gl.getError(), "glPatchParameter(PATCH_VERTICES)"); |
| |
| gl.drawArrays(GL_PATCHES, 0, 2 * width); |
| GLU_EXPECT_NO_ERROR(gl.getError(), "glDrawArrays"); |
| |
| gl.disableVertexAttribArray(0); |
| |
| GLenum readFormat; |
| GLenum readType; |
| getFormatReadInfo(format, readFormat, readType); |
| |
| gl.readPixels(0, 0, width, 1, readFormat, readType, (GLvoid*)&imageBufferResult[0]); |
| GLU_EXPECT_NO_ERROR(gl.getError(), "glReadPixels"); |
| } |
| |
| { |
| std::vector<const void*> datas; |
| datas.push_back(&imageBufferResult[0]); |
| if (!checkResult(datas, width/2u, subgroupSize)) |
| failedIterations++; |
| } |
| } |
| |
| if (0 < failedIterations) |
| { |
| log << tcu::TestLog::Message << (totalIterations - failedIterations) << " / " |
| << totalIterations << " values passed" << tcu::TestLog::EndMessage; |
| return tcu::TestStatus::fail("Failed!"); |
| } else |
| { |
| log << tcu::TestLog::Message << (totalIterations - failedIterations) << " / " |
| <<totalIterations << " values passed" << tcu::TestLog::EndMessage; |
| } |
| |
| return tcu::TestStatus::pass("OK"); |
| } |
| |
| bool glc::subgroups::check(std::vector<const void*> datas, |
| deUint32 width, deUint32 ref) |
| { |
| const deUint32* data = reinterpret_cast<const deUint32*>(datas[0]); |
| |
| for (deUint32 n = 0; n < width; ++n) |
| { |
| if (data[n] != ref) |
| { |
| return false; |
| } |
| } |
| |
| return true; |
| } |
| |
| bool glc::subgroups::checkCompute(std::vector<const void*> datas, |
| const deUint32 numWorkgroups[3], const deUint32 localSize[3], |
| deUint32 ref) |
| { |
| const deUint32 globalSizeX = numWorkgroups[0] * localSize[0]; |
| const deUint32 globalSizeY = numWorkgroups[1] * localSize[1]; |
| const deUint32 globalSizeZ = numWorkgroups[2] * localSize[2]; |
| |
| return check(datas, globalSizeX * globalSizeY * globalSizeZ, ref); |
| } |
| |
| |
| tcu::TestStatus glc::subgroups::makeGeometryFrameBufferTest( |
| Context& context, Format format, SSBOData* extraData, |
| deUint32 extraDataCount, |
| bool (*checkResult)(std::vector<const void*> datas, deUint32 width, deUint32 subgroupSize)) |
| { |
| tcu::TestLog& log = context.getDeqpContext().getTestContext().getLog(); |
| const glw::Functions& gl = context.getDeqpContext().getRenderContext().getFunctions(); |
| |
| const deUint32 maxWidth = getMaxWidth(); |
| vector<de::SharedPtr<BufferOrImage> > inputBuffers (extraDataCount); |
| |
| const GlslSource& vshader = context.getSourceCollection().get("vert"); |
| const GlslSource& gshader = context.getSourceCollection().get("geometry"); |
| const GlslSource& fshader = context.getSourceCollection().get("fragment"); |
| |
| for (deUint32 i = 0u; i < extraDataCount; i++) |
| { |
| if (extraData[i].isImage) |
| { |
| inputBuffers[i] = de::SharedPtr<BufferOrImage>(new Image(context, static_cast<deUint32>(extraData[i].numElements), 1u, extraData[i].format)); |
| // haven't implemented init for images yet |
| DE_ASSERT(extraData[i].initializeType == subgroups::SSBOData::InitializeNone); |
| } |
| else |
| { |
| deUint64 size = getElementSizeInBytes(extraData[i].format, extraData[i].layout) * extraData[i].numElements; |
| inputBuffers[i] = de::SharedPtr<BufferOrImage>(new Buffer(context, size, GL_UNIFORM_BUFFER)); |
| |
| glw::GLvoid *ptr = inputBuffers[i]->getAsBuffer()->mapBufferPtr(); |
| initializeMemory(context.getDeqpContext(), ptr, extraData[i]); |
| inputBuffers[i]->getAsBuffer()->unmapBufferPtr(); |
| } |
| } |
| |
| for (deUint32 ndx = 0u; ndx < extraDataCount; ndx++) |
| { |
| log << tcu::TestLog::Message |
| << "binding inputBuffers[" << ndx << "](" << inputBuffers[ndx]->getType() << ", " << inputBuffers[ndx]->getId() << " ), " |
| << "GEOMETRY, binding = " << extraData[ndx].binding << "\n" |
| << tcu::TestLog::EndMessage; |
| |
| if (inputBuffers[ndx]->isImage()) |
| { |
| gl.bindImageTexture(extraData[ndx].binding, inputBuffers[ndx]->getId(), |
| 0, GL_FALSE, 0, GL_READ_ONLY, extraData[ndx].format); |
| GLU_EXPECT_NO_ERROR(gl.getError(), "glBindImageTexture()"); |
| } else |
| { |
| gl.bindBufferBase(inputBuffers[ndx]->getType(), extraData[ndx].binding, inputBuffers[ndx]->getId()); |
| GLU_EXPECT_NO_ERROR(gl.getError(), "glBindBufferBase()"); |
| } |
| } |
| |
| de::MovePtr<glu::ShaderProgram> pipeline( |
| makeGraphicsPipeline(context, (ShaderStageFlags)(SHADER_STAGE_VERTEX_BIT | SHADER_STAGE_FRAGMENT_BIT | SHADER_STAGE_GEOMETRY_BIT), |
| &vshader, &fshader, &gshader, DE_NULL, DE_NULL)); |
| if (!pipeline->isOk()) |
| { |
| return tcu::TestStatus::fail("geom graphics program build failed"); |
| } |
| |
| const deUint32 subgroupSize = getSubgroupSize(context); |
| const deUint64 vertexBufferSize = maxWidth * sizeof(tcu::Vec4); |
| Buffer vertexBuffer (context, vertexBufferSize, GL_ARRAY_BUFFER); |
| unsigned totalIterations = 0u; |
| unsigned failedIterations = 0u; |
| Image discardableImage (context, maxWidth, 1u, format); |
| |
| { |
| glw::GLvoid * bufferPtr = vertexBuffer.mapBufferPtr(); |
| std::vector<tcu::Vec4> data (maxWidth, tcu::Vec4(1.0f, 1.0f, 1.0f, 1.0f)); |
| const float pixelSize = 2.0f / static_cast<float>(maxWidth); |
| float leftHandPosition = -1.0f; |
| |
| for(deUint32 ndx = 0u; ndx < maxWidth; ++ndx) |
| { |
| data[ndx][0] = leftHandPosition + pixelSize / 2.0f; |
| leftHandPosition += pixelSize; |
| } |
| |
| deMemcpy(bufferPtr, &data[0], maxWidth * sizeof(tcu::Vec4)); |
| vertexBuffer.unmapBufferPtr(); |
| } |
| |
| Vao vao(context); |
| Fbo fbo(context); |
| fbo.bind2D(discardableImage); |
| |
| gl.viewport(0, 0, maxWidth, 1u); |
| GLU_EXPECT_NO_ERROR(gl.getError(), "glViewport"); |
| |
| const deUint64 imageResultSize = getFormatSizeInBytes(format) * maxWidth; |
| vector<glw::GLubyte> imageBufferResult(imageResultSize); |
| const deUint64 vertexBufferOffset = 0u; |
| |
| for (deUint32 width = 1u; width < maxWidth; width = getNextWidth(width)) |
| { |
| totalIterations++; |
| |
| for (deUint32 ndx = 0u; ndx < inputBuffers.size(); ndx++) |
| { |
| if (inputBuffers[ndx]->isImage()) |
| { |
| DE_ASSERT(extraData[ndx].initializeType == subgroups::SSBOData::InitializeNone); |
| } else |
| { |
| glw::GLvoid *ptr = inputBuffers[ndx]->getAsBuffer()->mapBufferPtr(); |
| initializeMemory(context.getDeqpContext(), ptr, extraData[ndx]); |
| inputBuffers[ndx]->getAsBuffer()->unmapBufferPtr(); |
| } |
| } |
| |
| { |
| gl.clearColor(0.0f, 0.0f, 0.0f, 0.0f); |
| GLU_EXPECT_NO_ERROR(gl.getError(), "glClearColor"); |
| gl.clear(GL_COLOR_BUFFER_BIT); |
| GLU_EXPECT_NO_ERROR(gl.getError(), "glClear"); |
| |
| gl.useProgram(pipeline->getProgram()); |
| GLU_EXPECT_NO_ERROR(gl.getError(), "glUseProgram"); |
| |
| gl.enableVertexAttribArray(0); |
| GLU_EXPECT_NO_ERROR(gl.getError(), "glEnableVertexAttribArray"); |
| |
| gl.bindBuffer(GL_ARRAY_BUFFER, vertexBuffer.getId()); |
| GLU_EXPECT_NO_ERROR(gl.getError(), "glBindBuffer"); |
| |
| gl.vertexAttribPointer(0, 4, GL_FLOAT, GL_FALSE, sizeof(tcu::Vec4), glu::BufferOffsetAsPointer(vertexBufferOffset)); |
| GLU_EXPECT_NO_ERROR(gl.getError(), "glVertexAttribPointer"); |
| |
| gl.drawArrays(GL_POINTS, 0, width); |
| GLU_EXPECT_NO_ERROR(gl.getError(), "glDrawArrays"); |
| |
| gl.disableVertexAttribArray(0); |
| GLU_EXPECT_NO_ERROR(gl.getError(), "glDisableVertexAttribArray"); |
| |
| GLenum readFormat; |
| GLenum readType; |
| getFormatReadInfo(format, readFormat, readType); |
| |
| gl.readPixels(0, 0, width, 1, readFormat, readType, (GLvoid*)&imageBufferResult[0]); |
| GLU_EXPECT_NO_ERROR(gl.getError(), "glReadPixels"); |
| } |
| |
| { |
| std::vector<const void*> datas; |
| datas.push_back(&imageBufferResult[0]); |
| if (!checkResult(datas, width, subgroupSize)) |
| failedIterations++; |
| } |
| } |
| |
| if (0 < failedIterations) |
| { |
| log << tcu::TestLog::Message << (totalIterations - failedIterations) << " / " |
| << totalIterations << " values passed" << tcu::TestLog::EndMessage; |
| return tcu::TestStatus::fail("Failed!"); |
| } else |
| { |
| log << tcu::TestLog::Message << (totalIterations - failedIterations) << " / " |
| <<totalIterations << " values passed" << tcu::TestLog::EndMessage; |
| } |
| |
| return tcu::TestStatus::pass("OK"); |
| } |
| |
| tcu::TestStatus glc::subgroups::allStages( |
| Context& context, Format format, SSBOData* extraDatas, |
| deUint32 extraDatasCount, |
| bool (*checkResult)(std::vector<const void*> datas, deUint32 width, deUint32 subgroupSize), |
| const ShaderStageFlags shaderStageTested) |
| { |
| const deUint32 maxWidth = getMaxWidth(); |
| vector<ShaderStageFlags> stagesVector; |
| ShaderStageFlags shaderStageRequired = (ShaderStageFlags)0ull; |
| tcu::TestLog& log = context.getDeqpContext().getTestContext().getLog(); |
| const glw::Functions& gl = context.getDeqpContext().getRenderContext().getFunctions(); |
| |
| if (shaderStageTested & SHADER_STAGE_VERTEX_BIT) |
| { |
| stagesVector.push_back(SHADER_STAGE_VERTEX_BIT); |
| } |
| if (shaderStageTested & SHADER_STAGE_TESS_CONTROL_BIT) |
| { |
| stagesVector.push_back(SHADER_STAGE_TESS_CONTROL_BIT); |
| shaderStageRequired = (ShaderStageFlags)((deUint32)shaderStageRequired | ((deUint32)(shaderStageTested & SHADER_STAGE_TESS_EVALUATION_BIT) ? 0u : (deUint32)SHADER_STAGE_TESS_EVALUATION_BIT)); |
| shaderStageRequired = (ShaderStageFlags)((deUint32)shaderStageRequired | ((deUint32)(shaderStageTested & SHADER_STAGE_VERTEX_BIT) ? 0u : (deUint32)SHADER_STAGE_VERTEX_BIT)); |
| } |
| if (shaderStageTested & SHADER_STAGE_TESS_EVALUATION_BIT) |
| { |
| stagesVector.push_back(SHADER_STAGE_TESS_EVALUATION_BIT); |
| shaderStageRequired = (ShaderStageFlags)((deUint32)shaderStageRequired | ((deUint32)(shaderStageTested & SHADER_STAGE_VERTEX_BIT) ? 0u : (deUint32)SHADER_STAGE_VERTEX_BIT)); |
| shaderStageRequired = (ShaderStageFlags)((deUint32)shaderStageRequired | ((deUint32)(shaderStageTested & SHADER_STAGE_TESS_CONTROL_BIT) ? 0u : (deUint32)SHADER_STAGE_TESS_CONTROL_BIT)); |
| } |
| if (shaderStageTested & SHADER_STAGE_GEOMETRY_BIT) |
| { |
| stagesVector.push_back(SHADER_STAGE_GEOMETRY_BIT); |
| const ShaderStageFlags required = SHADER_STAGE_VERTEX_BIT; |
| shaderStageRequired = (ShaderStageFlags)((deUint32)shaderStageRequired | ((deUint32)(shaderStageTested & required) ? 0u : (deUint32)required)); |
| } |
| if (shaderStageTested & SHADER_STAGE_FRAGMENT_BIT) |
| { |
| const ShaderStageFlags required = SHADER_STAGE_VERTEX_BIT; |
| shaderStageRequired = (ShaderStageFlags)((deUint32)shaderStageRequired | ((deUint32)(shaderStageTested & required) ? 0u : (deUint32)required)); |
| } |
| |
| const deUint32 stagesCount = static_cast<deUint32>(stagesVector.size()); |
| const string vert = (shaderStageRequired & SHADER_STAGE_VERTEX_BIT) ? "vert_noSubgroup" : "vert"; |
| const string tesc = (shaderStageRequired & SHADER_STAGE_TESS_CONTROL_BIT) ? "tesc_noSubgroup" : "tesc"; |
| const string tese = (shaderStageRequired & SHADER_STAGE_TESS_EVALUATION_BIT) ? "tese_noSubgroup" : "tese"; |
| |
| shaderStageRequired = (ShaderStageFlags)(shaderStageTested | shaderStageRequired); |
| |
| const GlslSource *vshader = &context.getSourceCollection().get(vert); |
| const GlslSource *fshader = DE_NULL; |
| const GlslSource *gshader = DE_NULL; |
| const GlslSource *tcshader = DE_NULL; |
| const GlslSource *teshader = DE_NULL; |
| |
| if (shaderStageRequired & SHADER_STAGE_TESS_CONTROL_BIT) |
| { |
| tcshader = &context.getSourceCollection().get(tesc); |
| teshader = &context.getSourceCollection().get(tese); |
| } |
| if (shaderStageRequired & SHADER_STAGE_GEOMETRY_BIT) |
| { |
| if (shaderStageRequired & SHADER_STAGE_TESS_EVALUATION_BIT) |
| { |
| // tessellation shaders output line primitives |
| gshader = &context.getSourceCollection().get("geometry_lines"); |
| } |
| else |
| { |
| // otherwise points are processed by geometry shader |
| gshader = &context.getSourceCollection().get("geometry_points"); |
| } |
| } |
| if (shaderStageRequired & SHADER_STAGE_FRAGMENT_BIT) |
| { |
| fshader = &context.getSourceCollection().get("fragment"); |
| } |
| |
| std::vector< de::SharedPtr<BufferOrImage> > inputBuffers(stagesCount + extraDatasCount); |
| |
| // The implicit result SSBO we use to store our outputs from the shader |
| for (deUint32 ndx = 0u; ndx < stagesCount; ++ndx) |
| { |
| const deUint64 shaderSize = (stagesVector[ndx] == SHADER_STAGE_TESS_EVALUATION_BIT) ? maxWidth * 2 : maxWidth; |
| const deUint64 size = getElementSizeInBytes(format, SSBOData::LayoutStd430) * shaderSize; |
| inputBuffers[ndx] = de::SharedPtr<BufferOrImage>(new Buffer(context, size)); |
| |
| log << tcu::TestLog::Message |
| << "binding inputBuffers[" << ndx << "](" << inputBuffers[ndx]->getType() << ", " |
| << inputBuffers[ndx]->getId() << ", " << size << "), " |
| << "inputstage[" << ndx << "] = " << stagesVector[ndx] << " binding = " << getResultBinding(stagesVector[ndx]) |
| << tcu::TestLog::EndMessage; |
| |
| gl.bindBufferBase(inputBuffers[ndx]->getType(), getResultBinding(stagesVector[ndx]), inputBuffers[ndx]->getId()); |
| GLU_EXPECT_NO_ERROR(gl.getError(), "glBindBufferBase(ndx, inputBuffers[ndx])"); |
| } |
| |
| for (deUint32 ndx = stagesCount; ndx < stagesCount + extraDatasCount; ++ndx) |
| { |
| const deUint32 datasNdx = ndx - stagesCount; |
| if (extraDatas[datasNdx].isImage) |
| { |
| inputBuffers[ndx] = de::SharedPtr<BufferOrImage>(new Image(context, static_cast<deUint32>(extraDatas[datasNdx].numElements), 1, extraDatas[datasNdx].format)); |
| |
| // haven't implemented init for images yet |
| DE_ASSERT(extraDatas[datasNdx].initializeType == subgroups::SSBOData::InitializeNone); |
| } |
| else |
| { |
| const deUint64 size = getElementSizeInBytes(extraDatas[datasNdx].format, extraDatas[datasNdx].layout) * extraDatas[datasNdx].numElements; |
| inputBuffers[ndx] = de::SharedPtr<BufferOrImage>(new Buffer(context, size)); |
| |
| glw::GLvoid *ptr = inputBuffers[ndx]->getAsBuffer()->mapBufferPtr(); |
| initializeMemory(context.getDeqpContext(), ptr, extraDatas[datasNdx]); |
| inputBuffers[ndx]->getAsBuffer()->unmapBufferPtr(); |
| } |
| |
| log << tcu::TestLog::Message |
| << "binding inputBuffers[" << ndx << "](" << inputBuffers[ndx]->getType() << ", " |
| << inputBuffers[ndx]->getId() << ", " << extraDatas[datasNdx].numElements << " els), " |
| << "extrastage[" << datasNdx << "] = " << extraDatas[datasNdx].stages << " binding = " << extraDatas[datasNdx].binding |
| << tcu::TestLog::EndMessage; |
| |
| if (inputBuffers[ndx]->isImage()) |
| { |
| gl.bindImageTexture(extraDatas[datasNdx].binding, inputBuffers[ndx]->getId(), |
| 0, GL_FALSE, 0, GL_READ_WRITE, extraDatas[datasNdx].format); |
| GLU_EXPECT_NO_ERROR(gl.getError(), "glBindImageTexture(extraDatas[datasNdx])"); |
| } else |
| { |
| gl.bindBufferBase(inputBuffers[ndx]->getType(), extraDatas[datasNdx].binding, inputBuffers[ndx]->getId()); |
| GLU_EXPECT_NO_ERROR(gl.getError(), "glBindBufferBase(extraDatas[datasNdx])"); |
| } |
| } |
| |
| de::MovePtr<glu::ShaderProgram> pipeline( |
| makeGraphicsPipeline(context, shaderStageRequired, vshader, fshader, gshader, tcshader, teshader)); |
| |
| if (!pipeline->isOk()) |
| { |
| return tcu::TestStatus::fail("allstages graphics program build failed"); |
| } |
| |
| { |
| const deUint32 subgroupSize = getSubgroupSize(context); |
| unsigned totalIterations = 0u; |
| unsigned failedIterations = 0u; |
| Image resultImage (context, maxWidth, 1, format); |
| const deUint64 imageResultSize = getFormatSizeInBytes(format) * maxWidth; |
| vector<glw::GLubyte> imageBufferResult(imageResultSize); |
| |
| Vao vao(context); |
| Fbo fbo(context); |
| fbo.bind2D(resultImage); |
| |
| gl.viewport(0, 0, maxWidth, 1u); |
| GLU_EXPECT_NO_ERROR(gl.getError(), "viewport"); |
| |
| for (deUint32 width = 1u; width < maxWidth; width = getNextWidth(width)) |
| { |
| for (deUint32 ndx = stagesCount; ndx < stagesCount + extraDatasCount; ++ndx) |
| { |
| // re-init the data |
| if (extraDatas[ndx - stagesCount].isImage) |
| { |
| // haven't implemented init for images yet |
| DE_ASSERT(extraDatas[ndx - stagesCount].initializeType == subgroups::SSBOData::InitializeNone); |
| } else |
| { |
| glw::GLvoid *ptr = inputBuffers[ndx]->getAsBuffer()->mapBufferPtr(); |
| initializeMemory(context.getDeqpContext(), ptr, extraDatas[ndx - stagesCount]); |
| inputBuffers[ndx]->getAsBuffer()->unmapBufferPtr(); |
| } |
| } |
| |
| totalIterations++; |
| |
| gl.clearColor(0.0f, 0.0f, 0.0f, 0.0f); |
| GLU_EXPECT_NO_ERROR(gl.getError(), "glClearColor"); |
| gl.clear(GL_COLOR_BUFFER_BIT); |
| GLU_EXPECT_NO_ERROR(gl.getError(), "glClear"); |
| |
| gl.useProgram(pipeline->getProgram()); |
| GLU_EXPECT_NO_ERROR(gl.getError(), "glUseProgram"); |
| |
| glw::GLenum drawType; |
| if (shaderStageRequired & SHADER_STAGE_TESS_CONTROL_BIT) |
| { |
| drawType = GL_PATCHES; |
| gl.patchParameteri(GL_PATCH_VERTICES, 1u); |
| GLU_EXPECT_NO_ERROR(gl.getError(), "glPatchParameter(PATCH_VERTICES)"); |
| } else |
| { |
| drawType = GL_POINTS; |
| } |
| |
| gl.drawArrays(drawType, 0, width); |
| GLU_EXPECT_NO_ERROR(gl.getError(), "glDrawArrays"); |
| |
| GLenum readFormat; |
| GLenum readType; |
| getFormatReadInfo(format, readFormat, readType); |
| |
| gl.readPixels(0, 0, width, 1, readFormat, readType, (GLvoid*)&imageBufferResult[0]); |
| GLU_EXPECT_NO_ERROR(gl.getError(), "glReadPixels"); |
| |
| for (deUint32 ndx = 0u; ndx < stagesCount; ++ndx) |
| { |
| std::vector<const void*> datas; |
| std::vector<Buffer *> buffersToUnmap; |
| |
| if (!inputBuffers[ndx]->isImage()) |
| { |
| glw::GLvoid * resultData = inputBuffers[ndx]->getAsBuffer()->mapBufferPtr(); |
| buffersToUnmap.push_back(inputBuffers[ndx]->getAsBuffer()); |
| // we always have our result data first |
| datas.push_back(resultData); |
| } |
| |
| for (deUint32 index = stagesCount; index < stagesCount + extraDatasCount; ++index) |
| { |
| const deUint32 datasNdx = index - stagesCount; |
| if ((stagesVector[ndx] & extraDatas[datasNdx].stages) && (!inputBuffers[index]->isImage())) |
| { |
| glw::GLvoid * resultData = inputBuffers[index]->getAsBuffer()->mapBufferPtr(); |
| buffersToUnmap.push_back(inputBuffers[index]->getAsBuffer()); |
| datas.push_back(resultData); |
| } |
| } |
| |
| if (!checkResult(datas, (stagesVector[ndx] == SHADER_STAGE_TESS_EVALUATION_BIT) ? width * 2 : width , subgroupSize)) |
| failedIterations++; |
| |
| while( !buffersToUnmap.empty() ) |
| { |
| Buffer * buf = buffersToUnmap.back(); |
| buf->unmapBufferPtr(); |
| buffersToUnmap.pop_back(); |
| } |
| } |
| if (shaderStageTested & SHADER_STAGE_FRAGMENT_BIT) |
| { |
| std::vector<const void*> datas; |
| std::vector<Buffer *> buffersToUnmap; |
| |
| // we always have our result data first |
| datas.push_back(&imageBufferResult[0]); |
| |
| for (deUint32 index = stagesCount; index < stagesCount + extraDatasCount; ++index) |
| { |
| const deUint32 datasNdx = index - stagesCount; |
| if (SHADER_STAGE_FRAGMENT_BIT & extraDatas[datasNdx].stages && (!inputBuffers[index]->isImage())) |
| { |
| glw::GLvoid * resultData = inputBuffers[index]->getAsBuffer()->mapBufferPtr(); |
| buffersToUnmap.push_back(inputBuffers[index]->getAsBuffer()); |
| // we always have our result data first |
| datas.push_back(resultData); |
| } |
| } |
| |
| if (!checkResult(datas, width, subgroupSize)) |
| failedIterations++; |
| |
| while( !buffersToUnmap.empty() ) |
| { |
| Buffer * buf = buffersToUnmap.back(); |
| buf->unmapBufferPtr(); |
| buffersToUnmap.pop_back(); |
| } |
| } |
| |
| } |
| |
| if (0 < failedIterations) |
| { |
| log << tcu::TestLog::Message << (totalIterations - failedIterations) << " / " |
| << totalIterations << " values passed" << tcu::TestLog::EndMessage; |
| return tcu::TestStatus::fail("Failed!"); |
| } else |
| { |
| log << tcu::TestLog::Message << (totalIterations - failedIterations) << " / " |
| << totalIterations << " values passed" << tcu::TestLog::EndMessage; |
| } |
| } |
| return tcu::TestStatus::pass("OK"); |
| } |
| |
| tcu::TestStatus glc::subgroups::makeVertexFrameBufferTest(Context& context, Format format, |
| SSBOData* extraData, deUint32 extraDataCount, |
| bool (*checkResult)(std::vector<const void*> datas, deUint32 width, deUint32 subgroupSize)) |
| { |
| tcu::TestLog& log = context.getDeqpContext().getTestContext().getLog(); |
| const glw::Functions& gl = context.getDeqpContext().getRenderContext().getFunctions(); |
| |
| const deUint32 maxWidth = getMaxWidth(); |
| vector<de::SharedPtr<BufferOrImage> > inputBuffers (extraDataCount); |
| |
| const GlslSource& vshader = context.getSourceCollection().get("vert"); |
| const GlslSource& fshader = context.getSourceCollection().get("fragment"); |
| |
| for (deUint32 i = 0u; i < extraDataCount; i++) |
| { |
| if (extraData[i].isImage) |
| { |
| inputBuffers[i] = de::SharedPtr<BufferOrImage>(new Image(context, static_cast<deUint32>(extraData[i].numElements), 1u, extraData[i].format)); |
| |
| // haven't implemented init for images yet |
| DE_ASSERT(extraData[i].initializeType == subgroups::SSBOData::InitializeNone); |
| } |
| else |
| { |
| deUint64 size = getElementSizeInBytes(extraData[i].format, extraData[i].layout) * extraData[i].numElements; |
| inputBuffers[i] = de::SharedPtr<BufferOrImage>(new Buffer(context, size, GL_UNIFORM_BUFFER)); |
| |
| glw::GLvoid *ptr = inputBuffers[i]->getAsBuffer()->mapBufferPtr(); |
| initializeMemory(context.getDeqpContext(), ptr, extraData[i]); |
| inputBuffers[i]->getAsBuffer()->unmapBufferPtr(); |
| } |
| } |
| |
| for (deUint32 ndx = 0u; ndx < extraDataCount; ndx++) |
| { |
| log << tcu::TestLog::Message |
| << "binding inputBuffers[" << ndx << "](" << inputBuffers[ndx]->getType() << ", " << inputBuffers[ndx]->getId() << " ), " |
| << "VERTEX, binding = " << extraData[ndx].binding << "\n" |
| << tcu::TestLog::EndMessage; |
| |
| if (inputBuffers[ndx]->isImage()) |
| { |
| gl.bindImageTexture(extraData[ndx].binding, inputBuffers[ndx]->getId(), |
| 0, GL_FALSE, 0, GL_READ_ONLY, extraData[ndx].format); |
| GLU_EXPECT_NO_ERROR(gl.getError(), "glBindImageTexture()"); |
| } else |
| { |
| gl.bindBufferBase(inputBuffers[ndx]->getType(), extraData[ndx].binding, inputBuffers[ndx]->getId()); |
| GLU_EXPECT_NO_ERROR(gl.getError(), "glBindBufferBase()"); |
| } |
| } |
| |
| de::MovePtr<glu::ShaderProgram> pipeline( |
| makeGraphicsPipeline(context, (ShaderStageFlags)(SHADER_STAGE_VERTEX_BIT | SHADER_STAGE_FRAGMENT_BIT), |
| &vshader, &fshader, DE_NULL, DE_NULL, DE_NULL)); |
| |
| if (!pipeline->isOk()) |
| { |
| return tcu::TestStatus::fail("vert graphics program build failed"); |
| } |
| |
| const deUint32 subgroupSize = getSubgroupSize(context); |
| |
| const deUint64 vertexBufferSize = maxWidth * sizeof(tcu::Vec4); |
| Buffer vertexBuffer (context, vertexBufferSize, GL_ARRAY_BUFFER); |
| |
| unsigned totalIterations = 0u; |
| unsigned failedIterations = 0u; |
| |
| Image discardableImage (context, maxWidth, 1u, format); |
| |
| { |
| glw::GLvoid * bufferPtr = vertexBuffer.mapBufferPtr(); |
| std::vector<tcu::Vec4> data (maxWidth, tcu::Vec4(1.0f, 1.0f, 1.0f, 1.0f)); |
| const float pixelSize = 2.0f / static_cast<float>(maxWidth); |
| float leftHandPosition = -1.0f; |
| |
| for(deUint32 ndx = 0u; ndx < maxWidth; ++ndx) |
| { |
| data[ndx][0] = leftHandPosition + pixelSize / 2.0f; |
| leftHandPosition += pixelSize; |
| } |
| |
| deMemcpy(bufferPtr, &data[0], maxWidth * sizeof(tcu::Vec4)); |
| vertexBuffer.unmapBufferPtr(); |
| } |
| |
| Vao vao(context); |
| Fbo fbo(context); |
| fbo.bind2D(discardableImage); |
| |
| gl.viewport(0, 0, maxWidth, 1u); |
| GLU_EXPECT_NO_ERROR(gl.getError(), "glViewport"); |
| |
| const deUint64 imageResultSize = getFormatSizeInBytes(format) * maxWidth; |
| vector<glw::GLubyte> imageBufferResult(imageResultSize); |
| const deUint64 vertexBufferOffset = 0u; |
| |
| for (deUint32 width = 1u; width < maxWidth; width = getNextWidth(width)) |
| { |
| totalIterations++; |
| |
| for (deUint32 ndx = 0u; ndx < inputBuffers.size(); ndx++) |
| { |
| if (inputBuffers[ndx]->isImage()) |
| { |
| DE_ASSERT(extraData[ndx].initializeType == subgroups::SSBOData::InitializeNone); |
| } else |
| { |
| glw::GLvoid *ptr = inputBuffers[ndx]->getAsBuffer()->mapBufferPtr(); |
| initializeMemory(context.getDeqpContext(), ptr, extraData[ndx]); |
| inputBuffers[ndx]->getAsBuffer()->unmapBufferPtr(); |
| } |
| } |
| |
| { |
| gl.clearColor(0.0f, 0.0f, 0.0f, 0.0f); |
| GLU_EXPECT_NO_ERROR(gl.getError(), "glClearColor"); |
| gl.clear(GL_COLOR_BUFFER_BIT); |
| GLU_EXPECT_NO_ERROR(gl.getError(), "glClear"); |
| |
| gl.useProgram(pipeline->getProgram()); |
| GLU_EXPECT_NO_ERROR(gl.getError(), "glUseProgram"); |
| |
| gl.enableVertexAttribArray(0); |
| GLU_EXPECT_NO_ERROR(gl.getError(), "glEnableVertexAttribArray"); |
| |
| gl.bindBuffer(GL_ARRAY_BUFFER, vertexBuffer.getId()); |
| GLU_EXPECT_NO_ERROR(gl.getError(), "glBindBuffer"); |
| |
| gl.vertexAttribPointer(0, 4, GL_FLOAT, GL_FALSE, sizeof(tcu::Vec4), glu::BufferOffsetAsPointer(vertexBufferOffset)); |
| GLU_EXPECT_NO_ERROR(gl.getError(), "glVertexAttribPointer"); |
| |
| gl.drawArrays(GL_POINTS, 0, width); |
| GLU_EXPECT_NO_ERROR(gl.getError(), "glDrawArrays"); |
| |
| gl.disableVertexAttribArray(0); |
| GLU_EXPECT_NO_ERROR(gl.getError(), "glDisableVertexAttribArray"); |
| |
| GLenum readFormat; |
| GLenum readType; |
| getFormatReadInfo(format, readFormat, readType); |
| |
| gl.readPixels(0, 0, width, 1, readFormat, readType, (GLvoid*)&imageBufferResult[0]); |
| GLU_EXPECT_NO_ERROR(gl.getError(), "glReadPixels"); |
| } |
| |
| { |
| std::vector<const void*> datas; |
| datas.push_back(&imageBufferResult[0]); |
| if (!checkResult(datas, width, subgroupSize)) |
| failedIterations++; |
| } |
| } |
| |
| if (0 < failedIterations) |
| { |
| log << tcu::TestLog::Message << (totalIterations - failedIterations) << " / " |
| << totalIterations << " values passed" << tcu::TestLog::EndMessage; |
| return tcu::TestStatus::fail("Failed!"); |
| } else |
| { |
| log << tcu::TestLog::Message << (totalIterations - failedIterations) << " / " |
| <<totalIterations << " values passed" << tcu::TestLog::EndMessage; |
| } |
| |
| return tcu::TestStatus::pass("OK"); |
| } |
| |
| |
| tcu::TestStatus glc::subgroups::makeFragmentFrameBufferTest (Context& context, Format format, SSBOData* extraDatas, |
| deUint32 extraDatasCount, |
| bool (*checkResult)(std::vector<const void*> datas, deUint32 width, |
| deUint32 height, deUint32 subgroupSize)) |
| { |
| tcu::TestLog& log = context.getDeqpContext().getTestContext().getLog(); |
| const glw::Functions& gl = context.getDeqpContext().getRenderContext().getFunctions(); |
| |
| const GlslSource& vshader = context.getSourceCollection().get("vert"); |
| const GlslSource& fshader = context.getSourceCollection().get("fragment"); |
| |
| std::vector< de::SharedPtr<BufferOrImage> > inputBuffers(extraDatasCount); |
| |
| for (deUint32 i = 0; i < extraDatasCount; i++) |
| { |
| if (extraDatas[i].isImage) |
| { |
| inputBuffers[i] = de::SharedPtr<BufferOrImage>(new Image(context, |
| static_cast<deUint32>(extraDatas[i].numElements), 1, extraDatas[i].format)); |
| |
| // haven't implemented init for images yet |
| DE_ASSERT(extraDatas[i].initializeType == subgroups::SSBOData::InitializeNone); |
| } |
| else |
| { |
| deUint64 size = |
| getElementSizeInBytes(extraDatas[i].format, extraDatas[i].layout) * extraDatas[i].numElements; |
| inputBuffers[i] = de::SharedPtr<BufferOrImage>(new Buffer(context, size, GL_UNIFORM_BUFFER)); |
| |
| glw::GLvoid *ptr = inputBuffers[i]->getAsBuffer()->mapBufferPtr(); |
| initializeMemory(context.getDeqpContext(), ptr, extraDatas[i]); |
| inputBuffers[i]->getAsBuffer()->unmapBufferPtr(); |
| } |
| } |
| |
| for (deUint32 i = 0; i < extraDatasCount; i++) |
| { |
| log << tcu::TestLog::Message |
| << "binding inputBuffers[" << i << "](" << inputBuffers[i]->getType() << ", " << inputBuffers[i]->getId() << " ), " |
| << "FRAGMENT, binding = " << extraDatas[i].binding << "\n" |
| << tcu::TestLog::EndMessage; |
| |
| if (inputBuffers[i]->isImage()) |
| { |
| gl.bindImageTexture(extraDatas[i].binding, inputBuffers[i]->getId(), |
| 0, GL_FALSE, 0, GL_READ_ONLY, extraDatas[i].format); |
| GLU_EXPECT_NO_ERROR(gl.getError(), "glBindImageTexture()"); |
| } else |
| { |
| gl.bindBufferBase(inputBuffers[i]->getType(), extraDatas[i].binding, inputBuffers[i]->getId()); |
| GLU_EXPECT_NO_ERROR(gl.getError(), "glBindBufferBase()"); |
| } |
| } |
| |
| de::MovePtr<glu::ShaderProgram> pipeline( |
| makeGraphicsPipeline(context, (ShaderStageFlags)(SHADER_STAGE_VERTEX_BIT | SHADER_STAGE_FRAGMENT_BIT), |
| &vshader, &fshader, DE_NULL, DE_NULL, DE_NULL)); |
| |
| if (!pipeline->isOk()) |
| { |
| return tcu::TestStatus::fail("frag graphics program build failed"); |
| } |
| |
| const deUint32 subgroupSize = getSubgroupSize(context); |
| |
| unsigned totalIterations = 0; |
| unsigned failedIterations = 0; |
| |
| Vao vao(context); |
| Fbo fbo(context); |
| |
| for (deUint32 width = 8; width <= subgroupSize; width *= 2) |
| { |
| for (deUint32 height = 8; height <= subgroupSize; height *= 2) |
| { |
| totalIterations++; |
| |
| // re-init the data |
| for (deUint32 i = 0; i < extraDatasCount; i++) |
| { |
| if (inputBuffers[i]->isImage()) |
| { |
| DE_ASSERT(extraDatas[i].initializeType == subgroups::SSBOData::InitializeNone); |
| } else |
| { |
| glw::GLvoid *ptr = inputBuffers[i]->getAsBuffer()->mapBufferPtr(); |
| initializeMemory(context.getDeqpContext(), ptr, extraDatas[i]); |
| inputBuffers[i]->getAsBuffer()->unmapBufferPtr(); |
| } |
| } |
| |
| deUint64 formatSize = getFormatSizeInBytes(format); |
| const deUint64 resultImageSizeInBytes = |
| width * height * formatSize; |
| |
| Image resultImage(context, width, height, format); |
| |
| vector<glw::GLubyte> resultBuffer(resultImageSizeInBytes); |
| |
| fbo.bind2D(resultImage); |
| |
| gl.viewport(0, 0, width, height); |
| GLU_EXPECT_NO_ERROR(gl.getError(), "glViewport"); |
| |
| gl.clearColor(0.0f, 0.0f, 0.0f, 0.0f); |
| GLU_EXPECT_NO_ERROR(gl.getError(), "glClearColor"); |
| gl.clear(GL_COLOR_BUFFER_BIT); |
| GLU_EXPECT_NO_ERROR(gl.getError(), "glClear"); |
| |
| gl.useProgram(pipeline->getProgram()); |
| GLU_EXPECT_NO_ERROR(gl.getError(), "glUseProgram"); |
| |
| gl.drawArrays(GL_TRIANGLE_STRIP, 0, 4); |
| GLU_EXPECT_NO_ERROR(gl.getError(), "glDrawArrays"); |
| |
| GLenum readFormat; |
| GLenum readType; |
| getFormatReadInfo(format, readFormat, readType); |
| |
| gl.readPixels(0, 0, width, height, readFormat, readType, (GLvoid*)&resultBuffer[0]); |
| GLU_EXPECT_NO_ERROR(gl.getError(), "glReadPixels"); |
| |
| std::vector<const void*> datas; |
| { |
| // we always have our result data first |
| datas.push_back(&resultBuffer[0]); |
| } |
| |
| if (!checkResult(datas, width, height, subgroupSize)) |
| { |
| failedIterations++; |
| } |
| } |
| } |
| |
| if (0 < failedIterations) |
| { |
| log << tcu::TestLog::Message << (totalIterations - failedIterations) << " / " |
| << totalIterations << " values passed" << tcu::TestLog::EndMessage; |
| return tcu::TestStatus::fail("Failed!"); |
| } else |
| { |
| log << tcu::TestLog::Message << (totalIterations - failedIterations) << " / " |
| <<totalIterations << " values passed" << tcu::TestLog::EndMessage; |
| } |
| return tcu::TestStatus::pass("OK"); |
| } |
| |
| tcu::TestStatus glc::subgroups::makeComputeTest( |
| Context& context, Format format, SSBOData* inputs, deUint32 inputsCount, |
| bool (*checkResult)(std::vector<const void*> datas, |
| const deUint32 numWorkgroups[3], const deUint32 localSize[3], |
| deUint32 subgroupSize)) |
| { |
| const glw::Functions& gl = context.getDeqpContext().getRenderContext().getFunctions(); |
| deUint64 elementSize = getFormatSizeInBytes(format); |
| |
| const deUint64 resultBufferSize = maxSupportedSubgroupSize() * |
| maxSupportedSubgroupSize() * |
| maxSupportedSubgroupSize(); |
| const deUint64 resultBufferSizeInBytes = resultBufferSize * elementSize; |
| |
| Buffer resultBuffer( |
| context, resultBufferSizeInBytes); |
| |
| std::vector< de::SharedPtr<BufferOrImage> > inputBuffers(inputsCount); |
| |
| for (deUint32 i = 0; i < inputsCount; i++) |
| { |
| if (inputs[i].isImage) |
| { |
| inputBuffers[i] = de::SharedPtr<BufferOrImage>(new Image(context, |
| static_cast<deUint32>(inputs[i].numElements), 1, inputs[i].format)); |
| // haven't implemented init for images yet |
| DE_ASSERT(inputs[i].initializeType == subgroups::SSBOData::InitializeNone); |
| } |
| else |
| { |
| deUint64 size = |
| getElementSizeInBytes(inputs[i].format, inputs[i].layout) * inputs[i].numElements; |
| inputBuffers[i] = de::SharedPtr<BufferOrImage>(new Buffer(context, size)); |
| |
| glw::GLvoid *ptr = inputBuffers[i]->getAsBuffer()->mapBufferPtr(); |
| initializeMemory(context.getDeqpContext(), ptr, inputs[i]); |
| inputBuffers[i]->getAsBuffer()->unmapBufferPtr(); |
| } |
| |
| } |
| |
| tcu::TestLog& log = context.getDeqpContext().getTestContext().getLog(); |
| log << tcu::TestLog::Message |
| << "binding resultbuffer(type=" << resultBuffer.getType() |
| << ", id=" << resultBuffer.getId() << ", binding=0), COMPUTE" |
| << tcu::TestLog::EndMessage; |
| |
| gl.bindBufferBase(resultBuffer.getType(), 0, resultBuffer.getId()); |
| GLU_EXPECT_NO_ERROR(gl.getError(), "glBindBufferBase(0, resultBuffer)"); |
| |
| for (deUint32 i = 0; i < inputsCount; i++) |
| { |
| log << tcu::TestLog::Message |
| << "binding inputBuffers[" << i << "](type=" << inputBuffers[i]->getType() |
| << ", id=" << inputBuffers[i]->getId() << ", binding=" |
| << inputs[i].binding << "), 1, COMPUTE" |
| << tcu::TestLog::EndMessage; |
| |
| if (inputBuffers[i]->isImage()) |
| { |
| gl.bindImageTexture(inputs[i].binding, inputBuffers[i]->getId(), |
| 0, GL_FALSE, 0, GL_READ_WRITE, inputs[i].format); |
| GLU_EXPECT_NO_ERROR(gl.getError(), "glBindImageTexture(inputBuffer[i]"); |
| } else |
| { |
| gl.bindBufferBase(inputBuffers[i]->getType(), inputs[i].binding, inputBuffers[i]->getId()); |
| GLU_EXPECT_NO_ERROR(gl.getError(), "glBindBufferBase(inputBuffer[i])"); |
| } |
| } |
| |
| const GlslSource &cshader = context.getSourceCollection().get("comp"); |
| |
| unsigned totalIterations = 0; |
| unsigned failedIterations = 0; |
| |
| const deUint32 subgroupSize = getSubgroupSize(context); |
| |
| const deUint32 numWorkgroups[3] = {4, 2, 2}; |
| |
| const deUint32 localSizesToTestCount = 15; |
| deUint32 localSizesToTest[localSizesToTestCount][3] = |
| { |
| {1, 1, 1}, |
| {32, 4, 1}, |
| {32, 1, 4}, |
| {1, 32, 4}, |
| {1, 4, 32}, |
| {4, 1, 32}, |
| {4, 32, 1}, |
| {subgroupSize, 1, 1}, |
| {1, subgroupSize, 1}, |
| {1, 1, subgroupSize}, |
| {3, 5, 7}, |
| {128, 1, 1}, |
| {1, 128, 1}, |
| {1, 1, 64}, |
| {1, 1, 1} // Isn't used, just here to make double buffering checks easier |
| }; |
| |
| |
| de::MovePtr<glu::ShaderProgram> lastPipeline( |
| makeComputePipeline(context, cshader, |
| localSizesToTest[0][0], localSizesToTest[0][1], localSizesToTest[0][2])); |
| |
| for (deUint32 index = 0; index < (localSizesToTestCount - 1); index++) |
| { |
| const deUint32 nextX = localSizesToTest[index + 1][0]; |
| const deUint32 nextY = localSizesToTest[index + 1][1]; |
| const deUint32 nextZ = localSizesToTest[index + 1][2]; |
| |
| // we are running one test |
| totalIterations++; |
| |
| if (!lastPipeline->isOk()) |
| { |
| return tcu::TestStatus::fail("compute shaders build failed"); |
| } |
| |
| gl.useProgram(lastPipeline->getProgram()); |
| GLU_EXPECT_NO_ERROR(gl.getError(), "glUseProgram"); |
| |
| gl.dispatchCompute(numWorkgroups[0], numWorkgroups[1], numWorkgroups[2]); |
| GLU_EXPECT_NO_ERROR(gl.getError(), "glDispatchCompute"); |
| |
| de::MovePtr<glu::ShaderProgram> nextPipeline( |
| makeComputePipeline(context, cshader, nextX, nextY, nextZ)); |
| |
| std::vector<const void*> datas; |
| |
| { |
| glw::GLvoid * resultData = resultBuffer.mapBufferPtr(); |
| |
| // we always have our result data first |
| datas.push_back(resultData); |
| } |
| |
| for (deUint32 i = 0; i < inputsCount; i++) |
| { |
| if (!inputBuffers[i]->isImage()) |
| { |
| glw::GLvoid *resultData = inputBuffers[i]->getAsBuffer()->mapBufferPtr(); |
| |
| // we always have our result data first |
| datas.push_back(resultData); |
| } |
| } |
| |
| if (!checkResult(datas, numWorkgroups, localSizesToTest[index], subgroupSize)) |
| { |
| failedIterations++; |
| } |
| |
| resultBuffer.unmapBufferPtr(); |
| for (deUint32 i = 0; i < inputsCount; i++) |
| { |
| if (!inputBuffers[i]->isImage()) |
| { |
| inputBuffers[i]->getAsBuffer()->unmapBufferPtr(); |
| } |
| } |
| |
| lastPipeline = nextPipeline; |
| } |
| |
| if (0 < failedIterations) |
| { |
| log << tcu::TestLog::Message << (totalIterations - failedIterations) << " / " |
| << totalIterations << " values passed" << tcu::TestLog::EndMessage; |
| return tcu::TestStatus::fail("Failed!"); |
| } else |
| { |
| log << tcu::TestLog::Message << (totalIterations - failedIterations) << " / " |
| << totalIterations << " values passed" << tcu::TestLog::EndMessage; |
| } |
| |
| return tcu::TestStatus::pass("OK"); |
| } |