blob: a2c745f0935dfb8bf2eea1cd096cb2bacfc897ce [file] [log] [blame]
//
// Copyright 2020 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.
//
// ProgramExecutable.cpp: Collects the interfaces common to both Programs and
// ProgramPipelines in order to execute/draw with either.
#include "libANGLE/ProgramExecutable.h"
#include "libANGLE/Context.h"
#include "libANGLE/Program.h"
#include "libANGLE/ProgramPipeline.h"
#include "libANGLE/Shader.h"
namespace gl
{
ProgramExecutable::ProgramExecutable()
: mProgramState(nullptr),
mProgramPipelineState(nullptr),
mMaxActiveAttribLocation(0),
mAttributesTypeMask(0),
mAttributesMask(0),
mActiveSamplersMask(0),
mActiveSamplerRefCounts{},
mActiveImagesMask(0),
mCanDrawWith(false),
mTransformFeedbackBufferMode(GL_INTERLEAVED_ATTRIBS),
mSamplerUniformRange(0, 0),
mImageUniformRange(0, 0)
{
reset();
}
ProgramExecutable::ProgramExecutable(const ProgramExecutable &other)
: mProgramState(other.mProgramState),
mProgramPipelineState(other.mProgramPipelineState),
mLinkedGraphicsShaderStages(other.mLinkedGraphicsShaderStages),
mLinkedComputeShaderStages(other.mLinkedComputeShaderStages),
mActiveAttribLocationsMask(other.mActiveAttribLocationsMask),
mMaxActiveAttribLocation(other.mMaxActiveAttribLocation),
mAttributesTypeMask(other.mAttributesTypeMask),
mAttributesMask(other.mAttributesMask),
mActiveSamplersMask(other.mActiveSamplersMask),
mActiveSamplerRefCounts(other.mActiveSamplerRefCounts),
mActiveSamplerTypes(other.mActiveSamplerTypes),
mActiveSamplerFormats(other.mActiveSamplerFormats),
mActiveSamplerShaderBits(other.mActiveSamplerShaderBits),
mActiveImagesMask(other.mActiveImagesMask),
mActiveImageShaderBits(other.mActiveImageShaderBits),
mCanDrawWith(other.mCanDrawWith),
mOutputVariables(other.mOutputVariables),
mOutputLocations(other.mOutputLocations),
mProgramInputs(other.mProgramInputs),
mLinkedTransformFeedbackVaryings(other.mLinkedTransformFeedbackVaryings),
mTransformFeedbackStrides(other.mTransformFeedbackStrides),
mTransformFeedbackBufferMode(other.mTransformFeedbackBufferMode),
mUniforms(other.mUniforms),
mSamplerUniformRange(other.mSamplerUniformRange),
mUniformBlocks(other.mUniformBlocks),
mAtomicCounterBuffers(other.mAtomicCounterBuffers),
mImageUniformRange(other.mImageUniformRange),
mShaderStorageBlocks(other.mShaderStorageBlocks)
{
reset();
}
ProgramExecutable::~ProgramExecutable() = default;
void ProgramExecutable::reset()
{
resetInfoLog();
mActiveAttribLocationsMask.reset();
mAttributesTypeMask.reset();
mAttributesMask.reset();
mMaxActiveAttribLocation = 0;
mActiveSamplersMask.reset();
mActiveSamplerRefCounts = {};
mActiveSamplerTypes.fill(TextureType::InvalidEnum);
mActiveSamplerFormats.fill(SamplerFormat::InvalidEnum);
mActiveImagesMask.reset();
mProgramInputs.clear();
mLinkedTransformFeedbackVaryings.clear();
mUniforms.clear();
mUniformBlocks.clear();
mShaderStorageBlocks.clear();
mAtomicCounterBuffers.clear();
mOutputVariables.clear();
mOutputLocations.clear();
}
void ProgramExecutable::load(gl::BinaryInputStream *stream)
{
static_assert(MAX_VERTEX_ATTRIBS * 2 <= sizeof(uint32_t) * 8,
"Too many vertex attribs for mask: All bits of mAttributesTypeMask types and "
"mask fit into 32 bits each");
mAttributesTypeMask = gl::ComponentTypeMask(stream->readInt<uint32_t>());
mAttributesMask = stream->readInt<gl::AttributesMask>();
mActiveAttribLocationsMask = stream->readInt<gl::AttributesMask>();
mMaxActiveAttribLocation = stream->readInt<unsigned int>();
mLinkedGraphicsShaderStages = ShaderBitSet(stream->readInt<uint8_t>());
mLinkedComputeShaderStages = ShaderBitSet(stream->readInt<uint8_t>());
}
void ProgramExecutable::save(gl::BinaryOutputStream *stream) const
{
static_assert(MAX_VERTEX_ATTRIBS * 2 <= sizeof(uint32_t) * 8,
"All bits of mAttributesTypeMask types and mask fit into 32 bits each");
stream->writeInt(static_cast<int>(mAttributesTypeMask.to_ulong()));
stream->writeInt(static_cast<int>(mAttributesMask.to_ulong()));
stream->writeInt(mActiveAttribLocationsMask.to_ulong());
stream->writeInt(mMaxActiveAttribLocation);
stream->writeInt(mLinkedGraphicsShaderStages.bits());
stream->writeInt(mLinkedComputeShaderStages.bits());
}
const ProgramState *ProgramExecutable::getProgramState(ShaderType shaderType) const
{
if (mProgramState &&
(hasLinkedShaderStage(shaderType) || mProgramState->getAttachedShader(shaderType)))
{
return mProgramState;
}
else if (mProgramPipelineState && (hasLinkedShaderStage(shaderType) ||
mProgramPipelineState->getShaderProgram(shaderType)))
{
return &mProgramPipelineState->getShaderProgram(shaderType)->getState();
}
return nullptr;
}
bool ProgramExecutable::isCompute() const
{
ASSERT(mProgramState || mProgramPipelineState);
if (mProgramState)
{
return mProgramState->isCompute();
}
return mProgramPipelineState->isCompute();
}
void ProgramExecutable::setIsCompute(bool isComputeIn)
{
// A Program can only either be graphics or compute, but never both, so it can answer
// isCompute() based on which shaders it has. However, a PPO can have both graphics and compute
// programs attached, so we don't know if the PPO is a 'graphics' or 'compute' PPO until the
// actual draw/dispatch call, which is why only PPOs need to record the type of call here.
if (mProgramPipelineState)
{
mProgramPipelineState->setIsCompute(isComputeIn);
}
}
int ProgramExecutable::getInfoLogLength() const
{
return static_cast<int>(mInfoLog.getLength());
}
void ProgramExecutable::getInfoLog(GLsizei bufSize, GLsizei *length, char *infoLog) const
{
return mInfoLog.getLog(bufSize, length, infoLog);
}
std::string ProgramExecutable::getInfoLogString() const
{
return mInfoLog.str();
}
bool ProgramExecutable::isAttribLocationActive(size_t attribLocation) const
{
// TODO(timvp): http://anglebug.com/3570: Enable this assert here somehow.
// ASSERT(!mLinkingState);
ASSERT(attribLocation < mActiveAttribLocationsMask.size());
return mActiveAttribLocationsMask[attribLocation];
}
AttributesMask ProgramExecutable::getAttributesMask() const
{
// TODO(timvp): http://anglebug.com/3570: Enable this assert here somehow.
// ASSERT(!mLinkingState);
return mAttributesMask;
}
bool ProgramExecutable::hasDefaultUniforms() const
{
ASSERT(mProgramState || mProgramPipelineState);
if (mProgramState)
{
return mProgramState->hasDefaultUniforms();
}
return mProgramPipelineState->hasDefaultUniforms();
}
bool ProgramExecutable::hasTextures() const
{
ASSERT(mProgramState || mProgramPipelineState);
if (mProgramState)
{
return mProgramState->hasTextures();
}
return mProgramPipelineState->hasTextures();
}
bool ProgramExecutable::hasUniformBuffers() const
{
ASSERT(mProgramState || mProgramPipelineState);
if (mProgramState)
{
return mProgramState->hasUniformBuffers();
}
return mProgramPipelineState->hasUniformBuffers();
}
bool ProgramExecutable::hasStorageBuffers() const
{
ASSERT(mProgramState || mProgramPipelineState);
if (mProgramState)
{
return mProgramState->hasStorageBuffers();
}
return mProgramPipelineState->hasStorageBuffers();
}
bool ProgramExecutable::hasAtomicCounterBuffers() const
{
ASSERT(mProgramState || mProgramPipelineState);
if (mProgramState)
{
return mProgramState->hasAtomicCounterBuffers();
}
return mProgramPipelineState->hasAtomicCounterBuffers();
}
bool ProgramExecutable::hasImages() const
{
ASSERT(mProgramState || mProgramPipelineState);
if (mProgramState)
{
return mProgramState->hasImages();
}
return mProgramPipelineState->hasImages();
}
bool ProgramExecutable::hasTransformFeedbackOutput() const
{
ASSERT(mProgramState || mProgramPipelineState);
if (mProgramState)
{
return mProgramState->hasTransformFeedbackOutput();
}
return mProgramPipelineState->hasTransformFeedbackOutput();
}
size_t ProgramExecutable::getTransformFeedbackBufferCount(const gl::State &glState) const
{
ASSERT(mProgramState || mProgramPipelineState);
if (mProgramState)
{
return mProgramState->getTransformFeedbackBufferCount();
}
// TODO(timvp): http://anglebug.com/3570: Support program pipelines
return 0;
}
void ProgramExecutable::updateActiveSamplers(const ProgramState &programState)
{
const std::vector<SamplerBinding> &samplerBindings = programState.getSamplerBindings();
for (uint32_t samplerIndex = 0; samplerIndex < samplerBindings.size(); ++samplerIndex)
{
const SamplerBinding &samplerBinding = samplerBindings[samplerIndex];
if (samplerBinding.unreferenced)
continue;
uint32_t uniformIndex = programState.getUniformIndexFromSamplerIndex(samplerIndex);
const gl::LinkedUniform &samplerUniform = programState.getUniforms()[uniformIndex];
for (GLint textureUnit : samplerBinding.boundTextureUnits)
{
if (++mActiveSamplerRefCounts[textureUnit] == 1)
{
mActiveSamplerTypes[textureUnit] = samplerBinding.textureType;
mActiveSamplerFormats[textureUnit] = samplerBinding.format;
mActiveSamplerShaderBits[textureUnit] = samplerUniform.activeShaders();
}
else
{
if (mActiveSamplerTypes[textureUnit] != samplerBinding.textureType)
{
mActiveSamplerTypes[textureUnit] = TextureType::InvalidEnum;
}
if (mActiveSamplerFormats[textureUnit] != samplerBinding.format)
{
mActiveSamplerFormats[textureUnit] = SamplerFormat::InvalidEnum;
}
}
mActiveSamplersMask.set(textureUnit);
}
}
}
void ProgramExecutable::updateActiveImages(std::vector<ImageBinding> &imageBindings)
{
const bool compute = isCompute() ? true : false;
for (uint32_t imageIndex = 0; imageIndex < imageBindings.size(); ++imageIndex)
{
const gl::ImageBinding &imageBinding = imageBindings[imageIndex];
if (imageBinding.unreferenced)
continue;
uint32_t uniformIndex = mProgramState->getUniformIndexFromImageIndex(imageIndex);
const gl::LinkedUniform &imageUniform = mProgramState->getUniforms()[uniformIndex];
const ShaderBitSet shaderBits = imageUniform.activeShaders();
for (GLint imageUnit : imageBinding.boundImageUnits)
{
mActiveImagesMask.set(imageUnit);
if (compute)
mActiveImageShaderBits[imageUnit].set(gl::ShaderType::Compute);
else
mActiveImageShaderBits[imageUnit] = shaderBits;
}
}
}
void ProgramExecutable::setSamplerUniformTextureTypeAndFormat(
size_t textureUnitIndex,
std::vector<SamplerBinding> &samplerBindings)
{
bool foundBinding = false;
TextureType foundType = TextureType::InvalidEnum;
SamplerFormat foundFormat = SamplerFormat::InvalidEnum;
for (const SamplerBinding &binding : samplerBindings)
{
if (binding.unreferenced)
continue;
// A conflict exists if samplers of different types are sourced by the same texture unit.
// We need to check all bound textures to detect this error case.
for (GLuint textureUnit : binding.boundTextureUnits)
{
if (textureUnit == textureUnitIndex)
{
if (!foundBinding)
{
foundBinding = true;
foundType = binding.textureType;
foundFormat = binding.format;
}
else
{
if (foundType != binding.textureType)
{
foundType = TextureType::InvalidEnum;
}
if (foundFormat != binding.format)
{
foundFormat = SamplerFormat::InvalidEnum;
}
}
}
}
}
mActiveSamplerTypes[textureUnitIndex] = foundType;
mActiveSamplerFormats[textureUnitIndex] = foundFormat;
}
bool ProgramExecutable::linkValidateGlobalNames(InfoLog &infoLog) const
{
std::unordered_map<std::string, const sh::ShaderVariable *> uniformMap;
using BlockAndFieldPair = std::pair<const sh::InterfaceBlock *, const sh::ShaderVariable *>;
std::unordered_map<std::string, std::vector<BlockAndFieldPair>> uniformBlockFieldMap;
for (ShaderType shaderType : kAllGraphicsShaderTypes)
{
const ProgramState *programState = getProgramState(shaderType);
if (!programState)
{
continue;
}
Shader *shader = programState->getAttachedShader(shaderType);
if (!shader)
{
continue;
}
// Build a map of Uniforms
const std::vector<sh::ShaderVariable> uniforms = shader->getUniforms();
for (const auto &uniform : uniforms)
{
uniformMap[uniform.name] = &uniform;
}
// Build a map of Uniform Blocks
// This will also detect any field name conflicts between Uniform Blocks without instance
// names
const std::vector<sh::InterfaceBlock> &uniformBlocks = shader->getUniformBlocks();
for (const auto &uniformBlock : uniformBlocks)
{
// Only uniform blocks without an instance name can create a conflict with their field
// names
if (!uniformBlock.instanceName.empty())
{
continue;
}
for (const auto &field : uniformBlock.fields)
{
if (!uniformBlockFieldMap.count(field.name))
{
// First time we've seen this uniform block field name, so add the
// (Uniform Block, Field) pair immediately since there can't be a conflict yet
BlockAndFieldPair blockAndFieldPair(&uniformBlock, &field);
std::vector<BlockAndFieldPair> newUniformBlockList;
newUniformBlockList.push_back(blockAndFieldPair);
uniformBlockFieldMap[field.name] = newUniformBlockList;
continue;
}
// We've seen this name before.
// We need to check each of the uniform blocks that contain a field with this name
// to see if there's a conflict or not.
std::vector<BlockAndFieldPair> prevBlockFieldPairs =
uniformBlockFieldMap[field.name];
for (const auto &prevBlockFieldPair : prevBlockFieldPairs)
{
const sh::InterfaceBlock *prevUniformBlock = prevBlockFieldPair.first;
const sh::ShaderVariable *prevUniformBlockField = prevBlockFieldPair.second;
if (uniformBlock.isSameInterfaceBlockAtLinkTime(*prevUniformBlock))
{
// The same uniform block should, by definition, contain the same field name
continue;
}
// The uniform blocks don't match, so check if the necessary field properties
// also match
if ((field.name == prevUniformBlockField->name) &&
(field.type == prevUniformBlockField->type) &&
(field.precision == prevUniformBlockField->precision))
{
infoLog << "Name conflicts between uniform block field names: "
<< field.name;
return false;
}
}
// No conflict, so record this pair
BlockAndFieldPair blockAndFieldPair(&uniformBlock, &field);
uniformBlockFieldMap[field.name].push_back(blockAndFieldPair);
}
}
}
// Validate no uniform names conflict with attribute names
const ProgramState *programState = getProgramState(ShaderType::Vertex);
if (programState)
{
Shader *vertexShader = programState->getAttachedShader(ShaderType::Vertex);
if (vertexShader)
{
for (const auto &attrib : vertexShader->getActiveAttributes())
{
if (uniformMap.count(attrib.name))
{
infoLog << "Name conflicts between a uniform and an attribute: " << attrib.name;
return false;
}
}
}
}
// Validate no Uniform Block fields conflict with other Uniforms
for (const auto &uniformBlockField : uniformBlockFieldMap)
{
const std::string &fieldName = uniformBlockField.first;
if (uniformMap.count(fieldName))
{
infoLog << "Name conflicts between a uniform and a uniform block field: " << fieldName;
return false;
}
}
return true;
}
void ProgramExecutable::updateCanDrawWith()
{
mCanDrawWith =
(hasLinkedShaderStage(ShaderType::Vertex) && hasLinkedShaderStage(ShaderType::Fragment));
}
void ProgramExecutable::saveLinkedStateInfo()
{
// Only a Program's linked data needs to be saved, not a ProgramPipeline's
ASSERT(mProgramState);
for (ShaderType shaderType : getLinkedShaderStages())
{
Shader *shader = mProgramState->getAttachedShader(shaderType);
ASSERT(shader);
mLinkedOutputVaryings[shaderType] = shader->getOutputVaryings();
mLinkedInputVaryings[shaderType] = shader->getInputVaryings();
mLinkedShaderVersions[shaderType] = shader->getShaderVersion();
}
}
} // namespace gl