| /*------------------------------------------------------------------------- |
| * drawElements Quality Program OpenGL ES Utilities |
| * ------------------------------------------------ |
| * |
| * 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 Wrapper for GL program object. |
| *//*--------------------------------------------------------------------*/ |
| |
| #include "gluShaderProgram.hpp" |
| #include "gluRenderContext.hpp" |
| #include "glwFunctions.hpp" |
| #include "glwEnums.hpp" |
| #include "tcuTestLog.hpp" |
| #include "deClock.h" |
| |
| #include <cstring> |
| |
| using std::string; |
| |
| namespace glu |
| { |
| |
| // Shader |
| |
| Shader::Shader(const RenderContext &renderCtx, ShaderType shaderType) : m_gl(renderCtx.getFunctions()), m_shader(0) |
| { |
| m_info.type = shaderType; |
| m_shader = m_gl.createShader(getGLShaderType(shaderType)); |
| GLU_EXPECT_NO_ERROR(m_gl.getError(), "glCreateShader()"); |
| TCU_CHECK(m_shader); |
| } |
| |
| Shader::Shader(const glw::Functions &gl, ShaderType shaderType) : m_gl(gl), m_shader(0) |
| { |
| m_info.type = shaderType; |
| m_shader = m_gl.createShader(getGLShaderType(shaderType)); |
| GLU_EXPECT_NO_ERROR(m_gl.getError(), "glCreateShader()"); |
| TCU_CHECK(m_shader); |
| } |
| |
| Shader::~Shader(void) |
| { |
| m_gl.deleteShader(m_shader); |
| } |
| |
| void Shader::setSources(int numSourceStrings, const char *const *sourceStrings, const int *lengths) |
| { |
| m_gl.shaderSource(m_shader, numSourceStrings, sourceStrings, lengths); |
| GLU_EXPECT_NO_ERROR(m_gl.getError(), "glShaderSource()"); |
| |
| m_info.source.clear(); |
| for (int ndx = 0; ndx < numSourceStrings; ndx++) |
| { |
| const size_t length = lengths && lengths[ndx] >= 0 ? lengths[ndx] : strlen(sourceStrings[ndx]); |
| m_info.source += std::string(sourceStrings[ndx], length); |
| } |
| } |
| |
| void Shader::compile(void) |
| { |
| m_info.compileOk = false; |
| m_info.compileTimeUs = 0; |
| m_info.infoLog.clear(); |
| |
| { |
| uint64_t compileStart = deGetMicroseconds(); |
| m_gl.compileShader(m_shader); |
| m_info.compileTimeUs = deGetMicroseconds() - compileStart; |
| } |
| |
| GLU_EXPECT_NO_ERROR(m_gl.getError(), "glCompileShader()"); |
| |
| // Query status |
| { |
| int compileStatus = 0; |
| |
| m_gl.getShaderiv(m_shader, GL_COMPILE_STATUS, &compileStatus); |
| GLU_EXPECT_NO_ERROR(m_gl.getError(), "glGetShaderiv()"); |
| |
| m_info.compileOk = compileStatus != GL_FALSE; |
| } |
| |
| // Query log |
| { |
| int infoLogLen = 0; |
| int unusedLen; |
| |
| m_gl.getShaderiv(m_shader, GL_INFO_LOG_LENGTH, &infoLogLen); |
| GLU_EXPECT_NO_ERROR(m_gl.getError(), "glGetShaderiv()"); |
| |
| if (infoLogLen > 0) |
| { |
| // The INFO_LOG_LENGTH query and the buffer query implementations have |
| // very commonly off-by-one errors. Try to work around these issues. |
| |
| // add tolerance for off-by-one in log length, buffer write, and for terminator |
| std::vector<char> infoLog(infoLogLen + 3, '\0'); |
| |
| // claim buf size is one smaller to protect from off-by-one writing over buffer bounds |
| m_gl.getShaderInfoLog(m_shader, (int)infoLog.size() - 1, &unusedLen, &infoLog[0]); |
| |
| if (infoLog[(int)(infoLog.size()) - 1] != '\0') |
| { |
| // return whole buffer if null terminator was overwritten |
| m_info.infoLog = std::string(&infoLog[0], infoLog.size()); |
| } |
| else |
| { |
| // read as C string. infoLog is guaranteed to be 0-terminated |
| m_info.infoLog = std::string(&infoLog[0]); |
| } |
| } |
| } |
| } |
| |
| void Shader::specialize(const char *entryPoint, glw::GLuint numSpecializationConstants, |
| const glw::GLuint *constantIndex, const glw::GLuint *constantValue) |
| { |
| m_info.compileOk = false; |
| m_info.compileTimeUs = 0; |
| m_info.infoLog.clear(); |
| |
| { |
| uint64_t compileStart = deGetMicroseconds(); |
| m_gl.specializeShader(m_shader, entryPoint, numSpecializationConstants, constantIndex, constantValue); |
| m_info.compileTimeUs = deGetMicroseconds() - compileStart; |
| } |
| |
| GLU_EXPECT_NO_ERROR(m_gl.getError(), "glSpecializeShader()"); |
| |
| // Query status |
| { |
| int compileStatus = 0; |
| |
| m_gl.getShaderiv(m_shader, GL_COMPILE_STATUS, &compileStatus); |
| GLU_EXPECT_NO_ERROR(m_gl.getError(), "glGetShaderiv()"); |
| |
| m_info.compileOk = compileStatus != GL_FALSE; |
| } |
| |
| // Query log |
| { |
| int infoLogLen = 0; |
| int unusedLen; |
| |
| m_gl.getShaderiv(m_shader, GL_INFO_LOG_LENGTH, &infoLogLen); |
| GLU_EXPECT_NO_ERROR(m_gl.getError(), "glGetShaderiv()"); |
| |
| if (infoLogLen > 0) |
| { |
| // The INFO_LOG_LENGTH query and the buffer query implementations have |
| // very commonly off-by-one errors. Try to work around these issues. |
| |
| // add tolerance for off-by-one in log length, buffer write, and for terminator |
| std::vector<char> infoLog(infoLogLen + 3, '\0'); |
| |
| // claim buf size is one smaller to protect from off-by-one writing over buffer bounds |
| m_gl.getShaderInfoLog(m_shader, (int)infoLog.size() - 1, &unusedLen, &infoLog[0]); |
| GLU_EXPECT_NO_ERROR(m_gl.getError(), "glGetShaderInfoLog()"); |
| |
| if (infoLog[(int)(infoLog.size()) - 1] != '\0') |
| { |
| // return whole buffer if null terminator was overwritten |
| m_info.infoLog = std::string(&infoLog[0], infoLog.size()); |
| } |
| else |
| { |
| // read as C string. infoLog is guaranteed to be 0-terminated |
| m_info.infoLog = std::string(&infoLog[0]); |
| } |
| } |
| } |
| } |
| |
| // Program |
| |
| static bool getProgramLinkStatus(const glw::Functions &gl, uint32_t program) |
| { |
| int linkStatus = 0; |
| |
| gl.getProgramiv(program, GL_LINK_STATUS, &linkStatus); |
| GLU_EXPECT_NO_ERROR(gl.getError(), "glGetProgramiv()"); |
| return (linkStatus != GL_FALSE); |
| } |
| |
| static std::string getProgramInfoLog(const glw::Functions &gl, uint32_t program) |
| { |
| int infoLogLen = 0; |
| int unusedLen; |
| |
| gl.getProgramiv(program, GL_INFO_LOG_LENGTH, &infoLogLen); |
| GLU_EXPECT_NO_ERROR(gl.getError(), "glGetProgramiv()"); |
| |
| if (infoLogLen > 0) |
| { |
| // The INFO_LOG_LENGTH query and the buffer query implementations have |
| // very commonly off-by-one errors. Try to work around these issues. |
| |
| // add tolerance for off-by-one in log length, buffer write, and for terminator |
| std::vector<char> infoLog(infoLogLen + 3, '\0'); |
| |
| // claim buf size is one smaller to protect from off-by-one writing over buffer bounds |
| gl.getProgramInfoLog(program, (int)infoLog.size() - 1, &unusedLen, &infoLog[0]); |
| |
| // return whole buffer if null terminator was overwritten |
| if (infoLog[(int)(infoLog.size()) - 1] != '\0') |
| return std::string(&infoLog[0], infoLog.size()); |
| |
| // read as C string. infoLog is guaranteed to be 0-terminated |
| return std::string(&infoLog[0]); |
| } |
| return std::string(); |
| } |
| |
| Program::Program(const RenderContext &renderCtx) : m_gl(renderCtx.getFunctions()), m_program(0) |
| { |
| m_program = m_gl.createProgram(); |
| GLU_EXPECT_NO_ERROR(m_gl.getError(), "glCreateProgram()"); |
| } |
| |
| Program::Program(const glw::Functions &gl) : m_gl(gl), m_program(0) |
| { |
| m_program = m_gl.createProgram(); |
| GLU_EXPECT_NO_ERROR(m_gl.getError(), "glCreateProgram()"); |
| } |
| |
| Program::Program(const RenderContext &renderCtx, uint32_t program) : m_gl(renderCtx.getFunctions()), m_program(program) |
| { |
| m_info.linkOk = getProgramLinkStatus(m_gl, program); |
| m_info.infoLog = getProgramInfoLog(m_gl, program); |
| } |
| |
| Program::~Program(void) |
| { |
| m_gl.deleteProgram(m_program); |
| } |
| |
| void Program::attachShader(uint32_t shader) |
| { |
| m_gl.attachShader(m_program, shader); |
| GLU_EXPECT_NO_ERROR(m_gl.getError(), "glAttachShader()"); |
| } |
| |
| void Program::detachShader(uint32_t shader) |
| { |
| m_gl.detachShader(m_program, shader); |
| GLU_EXPECT_NO_ERROR(m_gl.getError(), "glDetachShader()"); |
| } |
| |
| void Program::bindAttribLocation(uint32_t location, const char *name) |
| { |
| m_gl.bindAttribLocation(m_program, location, name); |
| GLU_EXPECT_NO_ERROR(m_gl.getError(), "glBindAttribLocation()"); |
| } |
| |
| void Program::transformFeedbackVaryings(int count, const char *const *varyings, uint32_t bufferMode) |
| { |
| m_gl.transformFeedbackVaryings(m_program, count, varyings, bufferMode); |
| GLU_EXPECT_NO_ERROR(m_gl.getError(), "glTransformFeedbackVaryings()"); |
| } |
| |
| void Program::link(void) |
| { |
| m_info.linkOk = false; |
| m_info.linkTimeUs = 0; |
| m_info.infoLog.clear(); |
| |
| { |
| uint64_t linkStart = deGetMicroseconds(); |
| m_gl.linkProgram(m_program); |
| m_info.linkTimeUs = deGetMicroseconds() - linkStart; |
| } |
| GLU_EXPECT_NO_ERROR(m_gl.getError(), "glLinkProgram()"); |
| |
| m_info.linkOk = getProgramLinkStatus(m_gl, m_program); |
| m_info.infoLog = getProgramInfoLog(m_gl, m_program); |
| } |
| |
| bool Program::isSeparable(void) const |
| { |
| int separable = GL_FALSE; |
| |
| m_gl.getProgramiv(m_program, GL_PROGRAM_SEPARABLE, &separable); |
| GLU_EXPECT_NO_ERROR(m_gl.getError(), "glGetProgramiv()"); |
| |
| return (separable != GL_FALSE); |
| } |
| |
| void Program::setSeparable(bool separable) |
| { |
| m_gl.programParameteri(m_program, GL_PROGRAM_SEPARABLE, separable); |
| GLU_EXPECT_NO_ERROR(m_gl.getError(), "glProgramParameteri()"); |
| } |
| |
| // ProgramPipeline |
| |
| ProgramPipeline::ProgramPipeline(const RenderContext &renderCtx) : m_gl(renderCtx.getFunctions()), m_pipeline(0) |
| { |
| m_gl.genProgramPipelines(1, &m_pipeline); |
| GLU_EXPECT_NO_ERROR(m_gl.getError(), "glGenProgramPipelines()"); |
| } |
| |
| ProgramPipeline::ProgramPipeline(const glw::Functions &gl) : m_gl(gl), m_pipeline(0) |
| { |
| m_gl.genProgramPipelines(1, &m_pipeline); |
| GLU_EXPECT_NO_ERROR(m_gl.getError(), "glGenProgramPipelines()"); |
| } |
| |
| ProgramPipeline::~ProgramPipeline(void) |
| { |
| m_gl.deleteProgramPipelines(1, &m_pipeline); |
| } |
| |
| void ProgramPipeline::useProgramStages(uint32_t stages, uint32_t program) |
| { |
| m_gl.useProgramStages(m_pipeline, stages, program); |
| GLU_EXPECT_NO_ERROR(m_gl.getError(), "glUseProgramStages()"); |
| } |
| |
| void ProgramPipeline::activeShaderProgram(uint32_t program) |
| { |
| m_gl.activeShaderProgram(m_pipeline, program); |
| GLU_EXPECT_NO_ERROR(m_gl.getError(), "glActiveShaderProgram()"); |
| } |
| |
| bool ProgramPipeline::isValid(void) |
| { |
| glw::GLint status = GL_FALSE; |
| m_gl.validateProgramPipeline(m_pipeline); |
| GLU_EXPECT_NO_ERROR(m_gl.getError(), "glValidateProgramPipeline()"); |
| |
| m_gl.getProgramPipelineiv(m_pipeline, GL_VALIDATE_STATUS, &status); |
| |
| return (status != GL_FALSE); |
| } |
| |
| // ShaderProgram |
| |
| ShaderProgram::ShaderProgram(const RenderContext &renderCtx, const ProgramSources &sources) |
| : m_program(renderCtx.getFunctions()) |
| { |
| init(renderCtx.getFunctions(), sources); |
| } |
| |
| ShaderProgram::ShaderProgram(const RenderContext &renderCtx, const ProgramBinaries &binaries) |
| : m_program(renderCtx.getFunctions()) |
| { |
| init(renderCtx.getFunctions(), binaries); |
| } |
| |
| ShaderProgram::ShaderProgram(const glw::Functions &gl, const ProgramSources &sources) : m_program(gl) |
| { |
| init(gl, sources); |
| } |
| |
| ShaderProgram::ShaderProgram(const glw::Functions &gl, const ProgramBinaries &binaries) : m_program(gl) |
| { |
| init(gl, binaries); |
| } |
| |
| void ShaderProgram::init(const glw::Functions &gl, const ProgramSources &sources) |
| { |
| try |
| { |
| bool shadersOk = true; |
| |
| for (int shaderType = 0; shaderType < SHADERTYPE_LAST; shaderType++) |
| { |
| for (int shaderNdx = 0; shaderNdx < (int)sources.sources[shaderType].size(); ++shaderNdx) |
| { |
| const char *source = sources.sources[shaderType][shaderNdx].c_str(); |
| const int length = (int)sources.sources[shaderType][shaderNdx].size(); |
| |
| m_shaders[shaderType].reserve(m_shaders[shaderType].size() + 1); |
| |
| m_shaders[shaderType].push_back(new Shader(gl, ShaderType(shaderType))); |
| m_shaders[shaderType].back()->setSources(1, &source, &length); |
| m_shaders[shaderType].back()->compile(); |
| |
| shadersOk = shadersOk && m_shaders[shaderType].back()->getCompileStatus(); |
| } |
| } |
| |
| if (shadersOk) |
| { |
| for (int shaderType = 0; shaderType < SHADERTYPE_LAST; shaderType++) |
| for (int shaderNdx = 0; shaderNdx < (int)m_shaders[shaderType].size(); ++shaderNdx) |
| m_program.attachShader(m_shaders[shaderType][shaderNdx]->getShader()); |
| |
| for (std::vector<AttribLocationBinding>::const_iterator binding = sources.attribLocationBindings.begin(); |
| binding != sources.attribLocationBindings.end(); ++binding) |
| m_program.bindAttribLocation(binding->location, binding->name.c_str()); |
| |
| DE_ASSERT((sources.transformFeedbackBufferMode == GL_NONE) == sources.transformFeedbackVaryings.empty()); |
| if (sources.transformFeedbackBufferMode != GL_NONE) |
| { |
| std::vector<const char *> tfVaryings(sources.transformFeedbackVaryings.size()); |
| for (int ndx = 0; ndx < (int)tfVaryings.size(); ndx++) |
| tfVaryings[ndx] = sources.transformFeedbackVaryings[ndx].c_str(); |
| |
| m_program.transformFeedbackVaryings((int)tfVaryings.size(), &tfVaryings[0], |
| sources.transformFeedbackBufferMode); |
| } |
| |
| if (sources.separable) |
| m_program.setSeparable(true); |
| |
| m_program.link(); |
| } |
| } |
| catch (...) |
| { |
| for (int shaderType = 0; shaderType < SHADERTYPE_LAST; shaderType++) |
| for (int shaderNdx = 0; shaderNdx < (int)m_shaders[shaderType].size(); ++shaderNdx) |
| delete m_shaders[shaderType][shaderNdx]; |
| throw; |
| } |
| } |
| |
| void ShaderProgram::init(const glw::Functions &gl, const ProgramBinaries &binaries) |
| { |
| try |
| { |
| bool shadersOk = true; |
| |
| for (uint32_t binaryNdx = 0; binaryNdx < binaries.binaries.size(); ++binaryNdx) |
| { |
| ShaderBinary shaderBinary = binaries.binaries[binaryNdx]; |
| if (!shaderBinary.binary.empty()) |
| { |
| const char *binary = (const char *)shaderBinary.binary.data(); |
| const int length = (int)(shaderBinary.binary.size() * sizeof(uint32_t)); |
| |
| DE_ASSERT(shaderBinary.shaderEntryPoints.size() == shaderBinary.shaderTypes.size()); |
| |
| std::vector<Shader *> shaders; |
| for (uint32_t shaderTypeNdx = 0; shaderTypeNdx < shaderBinary.shaderTypes.size(); ++shaderTypeNdx) |
| { |
| ShaderType shaderType = shaderBinary.shaderTypes[shaderTypeNdx]; |
| |
| Shader *shader = new Shader(gl, ShaderType(shaderType)); |
| |
| m_shaders[shaderType].reserve(m_shaders[shaderType].size() + 1); |
| m_shaders[shaderType].push_back(shader); |
| shaders.push_back(shader); |
| } |
| |
| setBinary(gl, shaders, binaries.binaryFormat, binary, length); |
| |
| for (uint32_t shaderNdx = 0; shaderNdx < shaders.size(); ++shaderNdx) |
| { |
| shaders[shaderNdx]->specialize(shaderBinary.shaderEntryPoints[shaderNdx].c_str(), |
| (uint32_t)shaderBinary.specializationIndices.size(), |
| shaderBinary.specializationIndices.data(), |
| shaderBinary.specializationValues.data()); |
| |
| shadersOk = shadersOk && shaders[shaderNdx]->getCompileStatus(); |
| } |
| } |
| } |
| |
| if (shadersOk) |
| { |
| for (int shaderType = 0; shaderType < SHADERTYPE_LAST; shaderType++) |
| for (int shaderNdx = 0; shaderNdx < (int)m_shaders[shaderType].size(); ++shaderNdx) |
| m_program.attachShader(m_shaders[shaderType][shaderNdx]->getShader()); |
| |
| m_program.link(); |
| } |
| } |
| catch (...) |
| { |
| for (int shaderType = 0; shaderType < SHADERTYPE_LAST; shaderType++) |
| for (int shaderNdx = 0; shaderNdx < (int)m_shaders[shaderType].size(); ++shaderNdx) |
| delete m_shaders[shaderType][shaderNdx]; |
| throw; |
| } |
| } |
| |
| void ShaderProgram::setBinary(const glw::Functions &gl, std::vector<Shader *> &shaders, glw::GLenum binaryFormat, |
| const void *binaryData, const int length) |
| { |
| std::vector<glw::GLuint> shaderVec; |
| for (uint32_t shaderNdx = 0; shaderNdx < shaders.size(); ++shaderNdx) |
| shaderVec.push_back(shaders[shaderNdx]->getShader()); |
| |
| gl.shaderBinary((glw::GLsizei)shaderVec.size(), shaderVec.data(), binaryFormat, binaryData, length); |
| GLU_EXPECT_NO_ERROR(gl.getError(), "glShaderBinary"); |
| |
| for (uint32_t shaderNdx = 0; shaderNdx < shaders.size(); ++shaderNdx) |
| { |
| glw::GLint shaderState; |
| gl.getShaderiv(shaders[shaderNdx]->getShader(), GL_SPIR_V_BINARY_ARB, &shaderState); |
| GLU_EXPECT_NO_ERROR(gl.getError(), "getShaderiv"); |
| |
| DE_ASSERT(shaderState == GL_TRUE); |
| } |
| } |
| |
| ShaderProgram::~ShaderProgram(void) |
| { |
| for (int shaderType = 0; shaderType < SHADERTYPE_LAST; shaderType++) |
| for (int shaderNdx = 0; shaderNdx < (int)m_shaders[shaderType].size(); ++shaderNdx) |
| delete m_shaders[shaderType][shaderNdx]; |
| } |
| |
| // Utilities |
| |
| uint32_t getGLShaderType(ShaderType shaderType) |
| { |
| static const uint32_t s_typeMap[] = { |
| GL_VERTEX_SHADER, |
| GL_FRAGMENT_SHADER, |
| GL_GEOMETRY_SHADER, |
| GL_TESS_CONTROL_SHADER, |
| GL_TESS_EVALUATION_SHADER, |
| GL_COMPUTE_SHADER, |
| 0, |
| 0, |
| 0, |
| 0, |
| 0, |
| 0, |
| 0, |
| 0, |
| }; |
| DE_STATIC_ASSERT(DE_LENGTH_OF_ARRAY(s_typeMap) == SHADERTYPE_LAST); |
| DE_ASSERT(de::inBounds<int>(shaderType, 0, DE_LENGTH_OF_ARRAY(s_typeMap))); |
| return s_typeMap[shaderType]; |
| } |
| |
| uint32_t getGLShaderTypeBit(ShaderType shaderType) |
| { |
| static const uint32_t s_typebitMap[] = { |
| GL_VERTEX_SHADER_BIT, |
| GL_FRAGMENT_SHADER_BIT, |
| GL_GEOMETRY_SHADER_BIT, |
| GL_TESS_CONTROL_SHADER_BIT, |
| GL_TESS_EVALUATION_SHADER_BIT, |
| GL_COMPUTE_SHADER_BIT, |
| 0, |
| 0, |
| 0, |
| 0, |
| 0, |
| 0, |
| 0, |
| 0, |
| }; |
| DE_STATIC_ASSERT(DE_LENGTH_OF_ARRAY(s_typebitMap) == SHADERTYPE_LAST); |
| DE_ASSERT(de::inBounds<int>(shaderType, 0, DE_LENGTH_OF_ARRAY(s_typebitMap))); |
| return s_typebitMap[shaderType]; |
| } |
| |
| qpShaderType getLogShaderType(ShaderType shaderType) |
| { |
| static const qpShaderType s_typeMap[] = { |
| QP_SHADER_TYPE_VERTEX, |
| QP_SHADER_TYPE_FRAGMENT, |
| QP_SHADER_TYPE_GEOMETRY, |
| QP_SHADER_TYPE_TESS_CONTROL, |
| QP_SHADER_TYPE_TESS_EVALUATION, |
| QP_SHADER_TYPE_COMPUTE, |
| QP_SHADER_TYPE_RAYGEN, |
| QP_SHADER_TYPE_ANY_HIT, |
| QP_SHADER_TYPE_CLOSEST_HIT, |
| QP_SHADER_TYPE_MISS, |
| QP_SHADER_TYPE_INTERSECTION, |
| QP_SHADER_TYPE_CALLABLE, |
| QP_SHADER_TYPE_TASK, |
| QP_SHADER_TYPE_MESH, |
| }; |
| DE_STATIC_ASSERT(DE_LENGTH_OF_ARRAY(s_typeMap) == SHADERTYPE_LAST); |
| DE_ASSERT(de::inBounds<int>(shaderType, 0, DE_LENGTH_OF_ARRAY(s_typeMap))); |
| return s_typeMap[shaderType]; |
| } |
| |
| tcu::TestLog &operator<<(tcu::TestLog &log, const ShaderInfo &shaderInfo) |
| { |
| return log << tcu::TestLog::Shader(getLogShaderType(shaderInfo.type), shaderInfo.source, shaderInfo.compileOk, |
| shaderInfo.infoLog); |
| } |
| |
| tcu::TestLog &operator<<(tcu::TestLog &log, const Shader &shader) |
| { |
| return log << tcu::TestLog::ShaderProgram(false, "Plain shader") << shader.getInfo() |
| << tcu::TestLog::EndShaderProgram; |
| } |
| |
| static void logShaderProgram(tcu::TestLog &log, const ProgramInfo &programInfo, size_t numShaders, |
| const ShaderInfo *const *shaderInfos) |
| { |
| log << tcu::TestLog::ShaderProgram(programInfo.linkOk, programInfo.infoLog); |
| try |
| { |
| for (size_t shaderNdx = 0; shaderNdx < numShaders; ++shaderNdx) |
| log << *shaderInfos[shaderNdx]; |
| } |
| catch (...) |
| { |
| log << tcu::TestLog::EndShaderProgram; |
| throw; |
| } |
| log << tcu::TestLog::EndShaderProgram; |
| |
| // Write statistics. |
| { |
| static const struct |
| { |
| const char *name; |
| const char *description; |
| } s_compileTimeDesc[] = { |
| {"VertexCompileTime", "Vertex shader compile time"}, |
| {"FragmentCompileTime", "Fragment shader compile time"}, |
| {"GeometryCompileTime", "Geometry shader compile time"}, |
| {"TessControlCompileTime", "Tesselation control shader compile time"}, |
| {"TessEvaluationCompileTime", "Tesselation evaluation shader compile time"}, |
| {"ComputeCompileTime", "Compute shader compile time"}, |
| {"RaygenCompileTime", "Raygen shader compile time"}, |
| {"AnyHitCompileTime", "Any hit shader compile time"}, |
| {"ClosestHitCompileTime", "Closest hit shader compile time"}, |
| {"MissCompileTime", "Miss shader compile time"}, |
| {"IntersectionCompileTime", "Intersection shader compile time"}, |
| {"CallableCompileTime", "Callable shader compile time"}, |
| {"TaskCompileTime", "Task shader compile time"}, |
| {"MeshCompileTime", "Mesh shader compile time"}, |
| }; |
| DE_STATIC_ASSERT(DE_LENGTH_OF_ARRAY(s_compileTimeDesc) == SHADERTYPE_LAST); |
| |
| bool allShadersOk = true; |
| |
| for (size_t shaderNdx = 0; shaderNdx < numShaders; ++shaderNdx) |
| { |
| const ShaderInfo &shaderInfo = *shaderInfos[shaderNdx]; |
| |
| log << tcu::TestLog::Float(s_compileTimeDesc[shaderInfo.type].name, |
| s_compileTimeDesc[shaderInfo.type].description, "ms", QP_KEY_TAG_TIME, |
| (float)shaderInfo.compileTimeUs / 1000.0f); |
| |
| allShadersOk = allShadersOk && shaderInfo.compileOk; |
| } |
| |
| if (allShadersOk) |
| log << tcu::TestLog::Float("LinkTime", "Link time", "ms", QP_KEY_TAG_TIME, |
| (float)programInfo.linkTimeUs / 1000.0f); |
| } |
| } |
| |
| tcu::TestLog &operator<<(tcu::TestLog &log, const ShaderProgramInfo &shaderProgramInfo) |
| { |
| std::vector<const ShaderInfo *> shaderPtrs(shaderProgramInfo.shaders.size()); |
| |
| for (size_t ndx = 0; ndx < shaderPtrs.size(); ndx++) |
| shaderPtrs[ndx] = &shaderProgramInfo.shaders[ndx]; |
| |
| logShaderProgram(log, shaderProgramInfo.program, shaderPtrs.size(), shaderPtrs.empty() ? DE_NULL : &shaderPtrs[0]); |
| |
| return log; |
| } |
| |
| tcu::TestLog &operator<<(tcu::TestLog &log, const ShaderProgram &shaderProgram) |
| { |
| std::vector<const ShaderInfo *> shaderPtrs; |
| |
| for (int shaderType = 0; shaderType < SHADERTYPE_LAST; shaderType++) |
| { |
| for (int shaderNdx = 0; shaderNdx < shaderProgram.getNumShaders((ShaderType)shaderType); shaderNdx++) |
| shaderPtrs.push_back(&shaderProgram.getShaderInfo((ShaderType)shaderType, shaderNdx)); |
| } |
| |
| logShaderProgram(log, shaderProgram.getProgramInfo(), shaderPtrs.size(), |
| shaderPtrs.empty() ? DE_NULL : &shaderPtrs[0]); |
| |
| return log; |
| } |
| |
| tcu::TestLog &operator<<(tcu::TestLog &log, const ProgramSources &sources) |
| { |
| log << tcu::TestLog::ShaderProgram(false, "(Source only)"); |
| |
| try |
| { |
| for (int shaderType = 0; shaderType < SHADERTYPE_LAST; shaderType++) |
| { |
| for (size_t shaderNdx = 0; shaderNdx < sources.sources[shaderType].size(); shaderNdx++) |
| { |
| log << tcu::TestLog::Shader(getLogShaderType((ShaderType)shaderType), |
| sources.sources[shaderType][shaderNdx], false, ""); |
| } |
| } |
| } |
| catch (...) |
| { |
| log << tcu::TestLog::EndShaderProgram; |
| throw; |
| } |
| |
| log << tcu::TestLog::EndShaderProgram; |
| |
| return log; |
| } |
| |
| } // namespace glu |