| // |
| // Copyright 2017 The ANGLE Project Authors. All rights reserved. |
| // Use of this source code is governed by a BSD-style license that can be |
| // found in the LICENSE file. |
| // |
| |
| // ProgramPipeline.cpp: Implements the gl::ProgramPipeline class. |
| // Implements GL program pipeline objects and related functionality. |
| // [OpenGL ES 3.1] section 7.4 page 105. |
| |
| #include "libANGLE/ProgramPipeline.h" |
| |
| #include <algorithm> |
| |
| #include "libANGLE/Context.h" |
| #include "libANGLE/Program.h" |
| #include "libANGLE/angletypes.h" |
| #include "libANGLE/renderer/GLImplFactory.h" |
| #include "libANGLE/renderer/ProgramPipelineImpl.h" |
| |
| namespace gl |
| { |
| |
| enum SubjectIndexes : angle::SubjectIndex |
| { |
| kExecutableSubjectIndex = 0 |
| }; |
| |
| ProgramPipelineState::ProgramPipelineState() |
| : mLabel(), |
| mActiveShaderProgram(nullptr), |
| mValid(false), |
| mExecutable(new ProgramExecutable()), |
| mIsLinked(false) |
| { |
| for (const ShaderType shaderType : gl::AllShaderTypes()) |
| { |
| mPrograms[shaderType] = nullptr; |
| } |
| } |
| |
| ProgramPipelineState::~ProgramPipelineState() |
| { |
| SafeDelete(mExecutable); |
| } |
| |
| const std::string &ProgramPipelineState::getLabel() const |
| { |
| return mLabel; |
| } |
| |
| void ProgramPipelineState::activeShaderProgram(Program *shaderProgram) |
| { |
| mActiveShaderProgram = shaderProgram; |
| } |
| |
| void ProgramPipelineState::useProgramStage(const Context *context, |
| const ShaderType shaderType, |
| Program *shaderProgram, |
| angle::ObserverBinding *programObserverBindings) |
| { |
| Program *oldProgram = mPrograms[shaderType]; |
| if (oldProgram) |
| { |
| oldProgram->release(context); |
| } |
| |
| // If program refers to a program object with a valid shader attached for the indicated shader |
| // stage, glUseProgramStages installs the executable code for that stage in the indicated |
| // program pipeline object pipeline. |
| if (shaderProgram && (shaderProgram->id().value != 0) && |
| shaderProgram->getExecutable().hasLinkedShaderStage(shaderType)) |
| { |
| mPrograms[shaderType] = shaderProgram; |
| shaderProgram->addRef(); |
| } |
| else |
| { |
| // If program is zero, or refers to a program object with no valid shader executable for the |
| // given stage, it is as if the pipeline object has no programmable stage configured for the |
| // indicated shader stage. |
| mPrograms[shaderType] = nullptr; |
| } |
| |
| Program *program = mPrograms[shaderType]; |
| programObserverBindings->bind(program); |
| } |
| |
| void ProgramPipelineState::useProgramStages( |
| const Context *context, |
| GLbitfield stages, |
| Program *shaderProgram, |
| std::vector<angle::ObserverBinding> *programObserverBindings) |
| { |
| if (stages == GL_ALL_SHADER_BITS) |
| { |
| for (const ShaderType shaderType : gl::AllShaderTypes()) |
| { |
| size_t index = static_cast<size_t>(shaderType); |
| ASSERT(index < programObserverBindings->size()); |
| useProgramStage(context, shaderType, shaderProgram, |
| &programObserverBindings->at(index)); |
| } |
| } |
| else |
| { |
| if (stages & GL_VERTEX_SHADER_BIT) |
| { |
| size_t index = static_cast<size_t>(ShaderType::Vertex); |
| ASSERT(index < programObserverBindings->size()); |
| useProgramStage(context, ShaderType::Vertex, shaderProgram, |
| &programObserverBindings->at(index)); |
| } |
| |
| if (stages & GL_FRAGMENT_SHADER_BIT) |
| { |
| size_t index = static_cast<size_t>(ShaderType::Fragment); |
| ASSERT(index < programObserverBindings->size()); |
| useProgramStage(context, ShaderType::Fragment, shaderProgram, |
| &programObserverBindings->at(index)); |
| } |
| |
| if (stages & GL_COMPUTE_SHADER_BIT) |
| { |
| size_t index = static_cast<size_t>(ShaderType::Compute); |
| ASSERT(index < programObserverBindings->size()); |
| useProgramStage(context, ShaderType::Compute, shaderProgram, |
| &programObserverBindings->at(index)); |
| } |
| } |
| } |
| |
| bool ProgramPipelineState::usesShaderProgram(ShaderProgramID programId) const |
| { |
| for (const Program *program : mPrograms) |
| { |
| if (program && (program->id() == programId)) |
| { |
| return true; |
| } |
| } |
| |
| return false; |
| } |
| |
| void ProgramPipelineState::updateExecutableTextures() |
| { |
| for (const ShaderType shaderType : mExecutable->getLinkedShaderStages()) |
| { |
| const Program *program = getShaderProgram(shaderType); |
| ASSERT(program); |
| mExecutable->setActiveTextureMask(program->getExecutable().getActiveSamplersMask()); |
| mExecutable->setActiveImagesMask(program->getExecutable().getActiveImagesMask()); |
| // Updates mActiveSamplerRefCounts, mActiveSamplerTypes, and mActiveSamplerFormats |
| mExecutable->updateActiveSamplers(program->getState()); |
| } |
| } |
| |
| ProgramPipeline::ProgramPipeline(rx::GLImplFactory *factory, ProgramPipelineID handle) |
| : RefCountObject(factory->generateSerial(), handle), |
| mProgramPipelineImpl(factory->createProgramPipeline(mState)), |
| mExecutableObserverBinding(this, kExecutableSubjectIndex) |
| { |
| ASSERT(mProgramPipelineImpl); |
| |
| for (const ShaderType shaderType : gl::AllShaderTypes()) |
| { |
| mProgramObserverBindings.emplace_back(this, static_cast<angle::SubjectIndex>(shaderType)); |
| } |
| mExecutableObserverBinding.bind(mState.mExecutable); |
| } |
| |
| ProgramPipeline::~ProgramPipeline() |
| { |
| mProgramPipelineImpl.release(); |
| } |
| |
| void ProgramPipeline::onDestroy(const Context *context) |
| { |
| for (Program *program : mState.mPrograms) |
| { |
| if (program) |
| { |
| ASSERT(program->getRefCount()); |
| program->release(context); |
| } |
| } |
| |
| getImplementation()->destroy(context); |
| } |
| |
| void ProgramPipeline::setLabel(const Context *context, const std::string &label) |
| { |
| mState.mLabel = label; |
| } |
| |
| const std::string &ProgramPipeline::getLabel() const |
| { |
| return mState.mLabel; |
| } |
| |
| rx::ProgramPipelineImpl *ProgramPipeline::getImplementation() const |
| { |
| return mProgramPipelineImpl.get(); |
| } |
| |
| void ProgramPipeline::activeShaderProgram(Program *shaderProgram) |
| { |
| mState.activeShaderProgram(shaderProgram); |
| } |
| |
| void ProgramPipeline::useProgramStages(const Context *context, |
| GLbitfield stages, |
| Program *shaderProgram) |
| { |
| mState.useProgramStages(context, stages, shaderProgram, &mProgramObserverBindings); |
| updateLinkedShaderStages(); |
| updateExecutable(); |
| |
| mState.mIsLinked = false; |
| } |
| |
| void ProgramPipeline::updateLinkedShaderStages() |
| { |
| mState.mExecutable->resetLinkedShaderStages(); |
| |
| for (const ShaderType shaderType : gl::AllShaderTypes()) |
| { |
| Program *program = mState.mPrograms[shaderType]; |
| if (program) |
| { |
| mState.mExecutable->setLinkedShaderStages(shaderType); |
| } |
| } |
| |
| mState.mExecutable->updateCanDrawWith(); |
| } |
| |
| void ProgramPipeline::updateExecutableAttributes() |
| { |
| Program *vertexProgram = getShaderProgram(gl::ShaderType::Vertex); |
| |
| if (!vertexProgram) |
| { |
| return; |
| } |
| |
| const ProgramExecutable &vertexExecutable = vertexProgram->getExecutable(); |
| mState.mExecutable->mActiveAttribLocationsMask = vertexExecutable.mActiveAttribLocationsMask; |
| mState.mExecutable->mMaxActiveAttribLocation = vertexExecutable.mMaxActiveAttribLocation; |
| mState.mExecutable->mAttributesTypeMask = vertexExecutable.mAttributesTypeMask; |
| mState.mExecutable->mAttributesMask = vertexExecutable.mAttributesMask; |
| } |
| |
| void ProgramPipeline::updateTransformFeedbackMembers() |
| { |
| Program *vertexProgram = getShaderProgram(gl::ShaderType::Vertex); |
| |
| if (!vertexProgram) |
| { |
| return; |
| } |
| |
| const ProgramExecutable &vertexExecutable = vertexProgram->getExecutable(); |
| mState.mExecutable->mTransformFeedbackStrides = vertexExecutable.mTransformFeedbackStrides; |
| mState.mExecutable->mLinkedTransformFeedbackVaryings = |
| vertexExecutable.mLinkedTransformFeedbackVaryings; |
| } |
| |
| void ProgramPipeline::updateShaderStorageBlocks() |
| { |
| mState.mExecutable->mComputeShaderStorageBlocks.clear(); |
| mState.mExecutable->mGraphicsShaderStorageBlocks.clear(); |
| |
| // Only copy the storage blocks from each Program in the PPO once, since each Program could |
| // contain multiple shader stages. |
| ShaderBitSet handledStages; |
| |
| for (const gl::ShaderType shaderType : kAllGraphicsShaderTypes) |
| { |
| const Program *shaderProgram = getShaderProgram(shaderType); |
| if (shaderProgram && !handledStages.test(shaderType)) |
| { |
| // Only add each Program's blocks once. |
| handledStages |= shaderProgram->getExecutable().getLinkedShaderStages(); |
| |
| for (const InterfaceBlock &block : |
| shaderProgram->getExecutable().getShaderStorageBlocks()) |
| { |
| mState.mExecutable->mGraphicsShaderStorageBlocks.emplace_back(block); |
| } |
| } |
| } |
| |
| const Program *computeProgram = getShaderProgram(ShaderType::Compute); |
| if (computeProgram) |
| { |
| for (const InterfaceBlock &block : computeProgram->getExecutable().getShaderStorageBlocks()) |
| { |
| mState.mExecutable->mComputeShaderStorageBlocks.emplace_back(block); |
| } |
| } |
| } |
| |
| void ProgramPipeline::updateImageBindings() |
| { |
| mState.mExecutable->mComputeImageBindings.clear(); |
| mState.mExecutable->mGraphicsImageBindings.clear(); |
| |
| // Only copy the storage blocks from each Program in the PPO once, since each Program could |
| // contain multiple shader stages. |
| ShaderBitSet handledStages; |
| |
| for (const gl::ShaderType shaderType : kAllGraphicsShaderTypes) |
| { |
| const Program *shaderProgram = getShaderProgram(shaderType); |
| if (shaderProgram && !handledStages.test(shaderType)) |
| { |
| // Only add each Program's blocks once. |
| handledStages |= shaderProgram->getExecutable().getLinkedShaderStages(); |
| |
| for (const ImageBinding &imageBinding : shaderProgram->getState().getImageBindings()) |
| { |
| mState.mExecutable->mGraphicsImageBindings.emplace_back(imageBinding); |
| } |
| } |
| } |
| |
| const Program *computeProgram = getShaderProgram(ShaderType::Compute); |
| if (computeProgram) |
| { |
| for (const ImageBinding &imageBinding : computeProgram->getState().getImageBindings()) |
| { |
| mState.mExecutable->mComputeImageBindings.emplace_back(imageBinding); |
| } |
| } |
| } |
| |
| void ProgramPipeline::updateHasBooleans() |
| { |
| // Need to check all of the shader stages, not just linked, so we handle Compute correctly. |
| for (const gl::ShaderType shaderType : kAllGraphicsShaderTypes) |
| { |
| const Program *shaderProgram = getShaderProgram(shaderType); |
| if (shaderProgram) |
| { |
| const ProgramExecutable &executable = shaderProgram->getExecutable(); |
| |
| if (executable.hasUniformBuffers()) |
| { |
| mState.mExecutable->mPipelineHasGraphicsUniformBuffers = true; |
| } |
| if (executable.hasGraphicsStorageBuffers()) |
| { |
| mState.mExecutable->mPipelineHasGraphicsStorageBuffers = true; |
| } |
| if (executable.hasAtomicCounterBuffers()) |
| { |
| mState.mExecutable->mPipelineHasGraphicsAtomicCounterBuffers = true; |
| } |
| if (executable.hasDefaultUniforms()) |
| { |
| mState.mExecutable->mPipelineHasGraphicsDefaultUniforms = true; |
| } |
| if (executable.hasTextures()) |
| { |
| mState.mExecutable->mPipelineHasGraphicsTextures = true; |
| } |
| if (executable.hasGraphicsImages()) |
| { |
| mState.mExecutable->mPipelineHasGraphicsImages = true; |
| } |
| } |
| } |
| |
| const Program *computeProgram = getShaderProgram(ShaderType::Compute); |
| if (computeProgram) |
| { |
| const ProgramExecutable &executable = computeProgram->getExecutable(); |
| |
| if (executable.hasUniformBuffers()) |
| { |
| mState.mExecutable->mPipelineHasComputeUniformBuffers = true; |
| } |
| if (executable.hasComputeStorageBuffers()) |
| { |
| mState.mExecutable->mPipelineHasComputeStorageBuffers = true; |
| } |
| if (executable.hasAtomicCounterBuffers()) |
| { |
| mState.mExecutable->mPipelineHasComputeAtomicCounterBuffers = true; |
| } |
| if (executable.hasDefaultUniforms()) |
| { |
| mState.mExecutable->mPipelineHasComputeDefaultUniforms = true; |
| } |
| if (executable.hasTextures()) |
| { |
| mState.mExecutable->mPipelineHasComputeTextures = true; |
| } |
| if (executable.hasComputeImages()) |
| { |
| mState.mExecutable->mPipelineHasComputeImages = true; |
| } |
| } |
| } |
| |
| void ProgramPipeline::updateExecutable() |
| { |
| mState.mExecutable->reset(); |
| |
| // Vertex Shader ProgramExecutable properties |
| updateExecutableAttributes(); |
| updateTransformFeedbackMembers(); |
| updateShaderStorageBlocks(); |
| updateImageBindings(); |
| |
| // All Shader ProgramExecutable properties |
| mState.updateExecutableTextures(); |
| |
| // Must be last, since it queries things updated by earlier functions |
| updateHasBooleans(); |
| } |
| |
| ProgramMergedVaryings ProgramPipeline::getMergedVaryings() const |
| { |
| ASSERT(!mState.mExecutable->isCompute()); |
| |
| // Varyings are matched between pairs of consecutive stages, by location if assigned or |
| // by name otherwise. Note that it's possible for one stage to specify location and the other |
| // not: https://cvs.khronos.org/bugzilla/show_bug.cgi?id=16261 |
| |
| // Map stages to the previous active stage in the rendering pipeline. When looking at input |
| // varyings of a stage, this is used to find the stage whose output varyings are being linked |
| // with them. |
| ShaderMap<ShaderType> previousActiveStage; |
| |
| // Note that kAllGraphicsShaderTypes is sorted according to the rendering pipeline. |
| ShaderType lastActiveStage = ShaderType::InvalidEnum; |
| for (ShaderType shaderType : getExecutable().getLinkedShaderStages()) |
| { |
| previousActiveStage[shaderType] = lastActiveStage; |
| |
| const Program *program = getShaderProgram(shaderType); |
| ASSERT(program); |
| lastActiveStage = shaderType; |
| } |
| |
| // First, go through output varyings and create two maps (one by name, one by location) for |
| // faster lookup when matching input varyings. |
| |
| ShaderMap<std::map<std::string, size_t>> outputVaryingNameToIndexShaderMap; |
| ShaderMap<std::map<int, size_t>> outputVaryingLocationToIndexShaderMap; |
| |
| ProgramMergedVaryings merged; |
| |
| // Gather output varyings. |
| for (ShaderType shaderType : getExecutable().getLinkedShaderStages()) |
| { |
| const Program *program = getShaderProgram(shaderType); |
| ASSERT(program); |
| Shader *shader = program->getState().getAttachedShader(shaderType); |
| ASSERT(shader); |
| |
| for (const sh::ShaderVariable &varying : shader->getOutputVaryings()) |
| { |
| merged.push_back({}); |
| ProgramVaryingRef *ref = &merged.back(); |
| |
| ref->frontShader = &varying; |
| ref->frontShaderStage = shaderType; |
| |
| // Always map by name. Even if location is provided in this stage, it may not be in the |
| // paired stage. |
| outputVaryingNameToIndexShaderMap[shaderType][varying.name] = merged.size() - 1; |
| |
| // If location is provided, also keep it in a map by location. |
| if (varying.location != -1) |
| { |
| outputVaryingLocationToIndexShaderMap[shaderType][varying.location] = |
| merged.size() - 1; |
| } |
| } |
| } |
| |
| // Gather input varyings, and match them with output varyings of the previous stage. |
| for (ShaderType shaderType : getExecutable().getLinkedShaderStages()) |
| { |
| const Program *program = getShaderProgram(shaderType); |
| ASSERT(program); |
| Shader *shader = program->getState().getAttachedShader(shaderType); |
| ASSERT(shader); |
| ShaderType previousStage = previousActiveStage[shaderType]; |
| |
| for (const sh::ShaderVariable &varying : shader->getInputVaryings()) |
| { |
| size_t mergedIndex = merged.size(); |
| if (previousStage != ShaderType::InvalidEnum) |
| { |
| // If location is provided, see if we can match by location. |
| if (varying.location != -1) |
| { |
| std::map<int, size_t> outputVaryingLocationToIndex = |
| outputVaryingLocationToIndexShaderMap[previousStage]; |
| auto byLocationIter = outputVaryingLocationToIndex.find(varying.location); |
| if (byLocationIter != outputVaryingLocationToIndex.end()) |
| { |
| mergedIndex = byLocationIter->second; |
| } |
| } |
| |
| // If not found, try to match by name. |
| if (mergedIndex == merged.size()) |
| { |
| std::map<std::string, size_t> outputVaryingNameToIndex = |
| outputVaryingNameToIndexShaderMap[previousStage]; |
| auto byNameIter = outputVaryingNameToIndex.find(varying.name); |
| if (byNameIter != outputVaryingNameToIndex.end()) |
| { |
| mergedIndex = byNameIter->second; |
| } |
| } |
| } |
| |
| // If no previous stage, or not matched by location or name, create a new entry for it. |
| if (mergedIndex == merged.size()) |
| { |
| merged.push_back({}); |
| mergedIndex = merged.size() - 1; |
| } |
| |
| ProgramVaryingRef *ref = &merged[mergedIndex]; |
| |
| ref->backShader = &varying; |
| ref->backShaderStage = shaderType; |
| } |
| } |
| |
| return merged; |
| } |
| |
| // The attached shaders are checked for linking errors by matching up their variables. |
| // Uniform, input and output variables get collected. |
| // The code gets compiled into binaries. |
| angle::Result ProgramPipeline::link(const Context *context) |
| { |
| if (mState.mIsLinked) |
| { |
| return angle::Result::Continue; |
| } |
| |
| ProgramMergedVaryings mergedVaryings; |
| |
| if (!getExecutable().isCompute()) |
| { |
| InfoLog &infoLog = mState.mExecutable->getInfoLog(); |
| infoLog.reset(); |
| const State &state = context->getState(); |
| |
| // Map the varyings to the register file |
| gl::PackMode packMode = PackMode::ANGLE_RELAXED; |
| if (state.getLimitations().noFlexibleVaryingPacking) |
| { |
| // D3D9 pack mode is strictly more strict than WebGL, so takes priority. |
| packMode = PackMode::ANGLE_NON_CONFORMANT_D3D9; |
| } |
| else if (state.getExtensions().webglCompatibility) |
| { |
| // In WebGL, we use a slightly different handling for packing variables. |
| packMode = PackMode::WEBGL_STRICT; |
| } |
| |
| if (!linkVaryings(infoLog)) |
| { |
| return angle::Result::Stop; |
| } |
| |
| gl::ShaderMap<const gl::ProgramState *> programStates; |
| fillProgramStateMap(&programStates); |
| if (!mState.mExecutable->linkValidateGlobalNames(infoLog, programStates)) |
| { |
| return angle::Result::Stop; |
| } |
| |
| GLuint maxVaryingVectors = |
| static_cast<GLuint>(context->getState().getCaps().maxVaryingVectors); |
| VaryingPacking varyingPacking(maxVaryingVectors, packMode); |
| |
| mergedVaryings = getMergedVaryings(); |
| for (ShaderType shaderType : getExecutable().getLinkedShaderStages()) |
| { |
| Program *program = mState.mPrograms[shaderType]; |
| ASSERT(program); |
| program->getExecutable().getResources().varyingPacking.reset(); |
| ANGLE_TRY( |
| program->linkMergedVaryings(context, program->getExecutable(), mergedVaryings)); |
| } |
| } |
| |
| ANGLE_TRY(getImplementation()->link(context, mergedVaryings)); |
| |
| mState.mIsLinked = true; |
| |
| return angle::Result::Continue; |
| } |
| |
| bool ProgramPipeline::linkVaryings(InfoLog &infoLog) const |
| { |
| ShaderType previousShaderType = ShaderType::InvalidEnum; |
| for (ShaderType shaderType : getExecutable().getLinkedShaderStages()) |
| { |
| Program *program = getShaderProgram(shaderType); |
| ASSERT(program); |
| ProgramExecutable &executable = program->getExecutable(); |
| |
| if (previousShaderType != ShaderType::InvalidEnum) |
| { |
| Program *previousProgram = getShaderProgram(previousShaderType); |
| ASSERT(previousProgram); |
| ProgramExecutable &previousExecutable = previousProgram->getExecutable(); |
| |
| if (!Program::linkValidateShaderInterfaceMatching( |
| previousExecutable.getLinkedOutputVaryings(previousShaderType), |
| executable.getLinkedInputVaryings(shaderType), previousShaderType, shaderType, |
| previousExecutable.getLinkedShaderVersion(previousShaderType), |
| executable.getLinkedShaderVersion(shaderType), true, infoLog)) |
| { |
| return false; |
| } |
| } |
| previousShaderType = shaderType; |
| } |
| |
| Program *vertexProgram = mState.mPrograms[ShaderType::Vertex]; |
| Program *fragmentProgram = mState.mPrograms[ShaderType::Fragment]; |
| if (!vertexProgram || !fragmentProgram) |
| { |
| return false; |
| } |
| ProgramExecutable &vertexExecutable = vertexProgram->getExecutable(); |
| ProgramExecutable &fragmentExecutable = fragmentProgram->getExecutable(); |
| return Program::linkValidateBuiltInVaryings( |
| vertexExecutable.getLinkedOutputVaryings(ShaderType::Vertex), |
| fragmentExecutable.getLinkedInputVaryings(ShaderType::Fragment), |
| vertexExecutable.getLinkedShaderVersion(ShaderType::Vertex), infoLog); |
| } |
| |
| void ProgramPipeline::validate(const gl::Context *context) |
| { |
| const Caps &caps = context->getCaps(); |
| mState.mValid = true; |
| InfoLog &infoLog = mState.mExecutable->getInfoLog(); |
| infoLog.reset(); |
| |
| for (const ShaderType shaderType : mState.mExecutable->getLinkedShaderStages()) |
| { |
| Program *shaderProgram = mState.mPrograms[shaderType]; |
| if (shaderProgram) |
| { |
| shaderProgram->resolveLink(context); |
| shaderProgram->validate(caps); |
| std::string shaderInfoString = shaderProgram->getExecutable().getInfoLogString(); |
| if (shaderInfoString.length()) |
| { |
| mState.mValid = false; |
| infoLog << shaderInfoString << "\n"; |
| return; |
| } |
| if (!shaderProgram->isSeparable()) |
| { |
| mState.mValid = false; |
| infoLog << GetShaderTypeString(shaderType) << " is not marked separable." |
| << "\n"; |
| return; |
| } |
| } |
| } |
| |
| if (!linkVaryings(infoLog)) |
| { |
| mState.mValid = false; |
| |
| for (const ShaderType shaderType : mState.mExecutable->getLinkedShaderStages()) |
| { |
| Program *shaderProgram = mState.mPrograms[shaderType]; |
| ASSERT(shaderProgram); |
| shaderProgram->validate(caps); |
| std::string shaderInfoString = shaderProgram->getExecutable().getInfoLogString(); |
| if (shaderInfoString.length()) |
| { |
| infoLog << shaderInfoString << "\n"; |
| } |
| } |
| } |
| } |
| |
| bool ProgramPipeline::validateSamplers(InfoLog *infoLog, const Caps &caps) |
| { |
| for (const ShaderType shaderType : gl::AllShaderTypes()) |
| { |
| Program *shaderProgram = mState.mPrograms[shaderType]; |
| if (shaderProgram && !shaderProgram->validateSamplers(infoLog, caps)) |
| { |
| return false; |
| } |
| } |
| |
| return true; |
| } |
| |
| void ProgramPipeline::onSubjectStateChange(angle::SubjectIndex index, angle::SubjectMessage message) |
| { |
| switch (message) |
| { |
| case angle::SubjectMessage::SubjectChanged: |
| mState.mIsLinked = false; |
| mState.updateExecutableTextures(); |
| break; |
| default: |
| UNREACHABLE(); |
| break; |
| } |
| } |
| |
| void ProgramPipeline::fillProgramStateMap(ShaderMap<const ProgramState *> *programStatesOut) |
| { |
| for (ShaderType shaderType : AllShaderTypes()) |
| { |
| (*programStatesOut)[shaderType] = nullptr; |
| |
| Program *program = getShaderProgram(shaderType); |
| if (program) |
| { |
| (*programStatesOut)[shaderType] = &program->getState(); |
| } |
| } |
| } |
| |
| } // namespace gl |