blob: 65ff25a0318a5c063ae711752a7c483e05577c0d [file] [log] [blame]
//
// 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.
//
// ProgramPipelineVk.cpp:
// Implements the class methods for ProgramPipelineVk.
//
#include "libANGLE/renderer/vulkan/ProgramPipelineVk.h"
#include "libANGLE/renderer/glslang_wrapper_utils.h"
#include "libANGLE/renderer/vulkan/GlslangWrapperVk.h"
namespace rx
{
ProgramPipelineVk::ProgramPipelineVk(const gl::ProgramPipelineState &state)
: ProgramPipelineImpl(state)
{
mExecutable.setProgramPipeline(this);
}
ProgramPipelineVk::~ProgramPipelineVk() {}
void ProgramPipelineVk::destroy(const gl::Context *context)
{
ContextVk *contextVk = vk::GetImpl(context);
reset(contextVk);
}
void ProgramPipelineVk::reset(ContextVk *contextVk)
{
mExecutable.reset(contextVk);
}
// TODO: http://anglebug.com/3570: Move/Copy all of the necessary information into
// the ProgramExecutable, so this function can be removed.
void ProgramPipelineVk::fillProgramStateMap(
gl::ShaderMap<const gl::ProgramState *> *programStatesOut)
{
for (gl::ShaderType shaderType : gl::AllShaderTypes())
{
(*programStatesOut)[shaderType] = nullptr;
ProgramVk *programVk = getShaderProgram(shaderType);
if (programVk)
{
(*programStatesOut)[shaderType] = &programVk->getState();
}
}
}
angle::Result ProgramPipelineVk::link(const gl::Context *glContext,
const gl::ProgramMergedVaryings &mergedVaryings,
const gl::ProgramVaryingPacking &varyingPacking)
{
ContextVk *contextVk = vk::GetImpl(glContext);
const gl::ProgramExecutable &glExecutable = mState.getExecutable();
GlslangSourceOptions options =
GlslangWrapperVk::CreateSourceOptions(contextVk->getRenderer()->getFeatures());
GlslangProgramInterfaceInfo glslangProgramInterfaceInfo;
GlslangWrapperVk::ResetGlslangProgramInterfaceInfo(&glslangProgramInterfaceInfo);
mExecutable.clearVariableInfoMap();
// Now that the program pipeline has all of the programs attached, the various descriptor
// set/binding locations need to be re-assigned to their correct values.
const gl::ShaderType linkedTransformFeedbackStage =
glExecutable.getLinkedTransformFeedbackStage();
// This should be done before assigning varying locations. Otherwise, we can encounter shader
// interface mismatching problems when the transform feedback stage is not the vertex stage.
if (options.supportsTransformFeedbackExtension)
{
for (const gl::ShaderType shaderType : glExecutable.getLinkedShaderStages())
{
const gl::Program *glProgram = mState.getShaderProgram(shaderType);
if (glProgram && gl::ShaderTypeSupportsTransformFeedback(shaderType))
{
const bool isTransformFeedbackStage =
shaderType == linkedTransformFeedbackStage &&
!glProgram->getState().getLinkedTransformFeedbackVaryings().empty();
GlslangAssignTransformFeedbackLocations(
shaderType, glProgram->getState(), isTransformFeedbackStage,
&glslangProgramInterfaceInfo, &mExecutable.mVariableInfoMap);
}
}
}
gl::ShaderType frontShaderType = gl::ShaderType::InvalidEnum;
UniformBindingIndexMap uniformBindingIndexMap;
for (const gl::ShaderType shaderType : glExecutable.getLinkedShaderStages())
{
const gl::Program *glProgram = mState.getShaderProgram(shaderType);
if (glProgram)
{
const bool isTransformFeedbackStage =
shaderType == linkedTransformFeedbackStage &&
!glProgram->getState().getLinkedTransformFeedbackVaryings().empty();
GlslangAssignLocations(options, glProgram->getState(), varyingPacking, shaderType,
frontShaderType, isTransformFeedbackStage,
&glslangProgramInterfaceInfo, &uniformBindingIndexMap,
&mExecutable.mVariableInfoMap);
frontShaderType = shaderType;
}
}
if (contextVk->getFeatures().enablePrecisionQualifiers.enabled)
{
mExecutable.resolvePrecisionMismatch(mergedVaryings);
}
return mExecutable.createPipelineLayout(contextVk, mState.getExecutable(), nullptr);
}
size_t ProgramPipelineVk::calcUniformUpdateRequiredSpace(
ContextVk *contextVk,
gl::ShaderMap<VkDeviceSize> *uniformOffsets) const
{
size_t requiredSpace = 0;
for (const gl::ShaderType shaderType : mState.getExecutable().getLinkedShaderStages())
{
ProgramVk *programVk = getShaderProgram(shaderType);
ASSERT(programVk);
if (programVk->isShaderUniformDirty(shaderType))
{
(*uniformOffsets)[shaderType] = requiredSpace;
requiredSpace += programVk->getDefaultUniformAlignedSize(contextVk, shaderType);
}
}
return requiredSpace;
}
angle::Result ProgramPipelineVk::updateUniforms(ContextVk *contextVk)
{
const gl::ProgramExecutable &glExecutable = mState.getExecutable();
vk::DynamicBuffer *defaultUniformStorage = contextVk->getDefaultUniformStorage();
uint8_t *bufferData = nullptr;
VkDeviceSize bufferOffset = 0;
uint32_t offsetIndex = 0;
bool anyNewBufferAllocated = false;
gl::ShaderMap<VkDeviceSize> offsets; // offset to the beginning of bufferData
size_t requiredSpace;
// We usually only update uniform data for shader stages that are actually dirty. But when the
// buffer for uniform data have switched, because all shader stages are using the same buffer,
// we then must update uniform data for all shader stages to keep all shader stages' uniform
// data in the same buffer.
requiredSpace = calcUniformUpdateRequiredSpace(contextVk, &offsets);
ASSERT(requiredSpace > 0);
// Allocate space from dynamicBuffer. Always try to allocate from the current buffer first.
// If that failed, we deal with fall out and try again.
if (!defaultUniformStorage->allocateFromCurrentBuffer(requiredSpace, &bufferData,
&bufferOffset))
{
setAllDefaultUniformsDirty();
requiredSpace = calcUniformUpdateRequiredSpace(contextVk, &offsets);
ANGLE_TRY(defaultUniformStorage->allocate(contextVk, requiredSpace, &bufferData, nullptr,
&bufferOffset, &anyNewBufferAllocated));
}
for (const gl::ShaderType shaderType : glExecutable.getLinkedShaderStages())
{
ProgramVk *programVk = getShaderProgram(shaderType);
ASSERT(programVk);
if (programVk->isShaderUniformDirty(shaderType))
{
const angle::MemoryBuffer &uniformData =
programVk->getDefaultUniformBlocks()[shaderType].uniformData;
memcpy(&bufferData[offsets[shaderType]], uniformData.data(), uniformData.size());
mExecutable.mDynamicUniformDescriptorOffsets[offsetIndex] =
static_cast<uint32_t>(bufferOffset + offsets[shaderType]);
programVk->clearShaderUniformDirtyBit(shaderType);
}
++offsetIndex;
}
ANGLE_TRY(defaultUniformStorage->flush(contextVk));
// Because the uniform buffers are per context, we can't rely on dynamicBuffer's allocate
// function to tell us if you have got a new buffer or not. Other program's use of the buffer
// might already pushed dynamicBuffer to a new buffer. We record which buffer (represented by
// the unique BufferSerial number) we were using with the current descriptor set and then we
// use that recorded BufferSerial compare to the current uniform buffer to quickly detect if
// there is a buffer switch or not. We need to retrieve from the descriptor set cache or
// allocate a new descriptor set whenever there is uniform buffer switch.
vk::BufferHelper *defaultUniformBuffer = defaultUniformStorage->getCurrentBuffer();
if (mExecutable.getCurrentDefaultUniformBufferSerial() !=
defaultUniformBuffer->getBufferSerial())
{
// We need to reinitialize the descriptor sets if we newly allocated buffers since we can't
// modify the descriptor sets once initialized.
vk::UniformsAndXfbDescriptorDesc defaultUniformsDesc;
vk::UniformsAndXfbDescriptorDesc *uniformsAndXfbBufferDesc;
if (glExecutable.hasTransformFeedbackOutput())
{
TransformFeedbackVk *transformFeedbackVk =
vk::GetImpl(contextVk->getState().getCurrentTransformFeedback());
uniformsAndXfbBufferDesc = &transformFeedbackVk->getTransformFeedbackDesc();
uniformsAndXfbBufferDesc->updateDefaultUniformBuffer(
defaultUniformBuffer->getBufferSerial());
}
else
{
defaultUniformsDesc.updateDefaultUniformBuffer(defaultUniformBuffer->getBufferSerial());
uniformsAndXfbBufferDesc = &defaultUniformsDesc;
}
bool newDescriptorSetAllocated;
ANGLE_TRY(mExecutable.allocUniformAndXfbDescriptorSet(contextVk, *uniformsAndXfbBufferDesc,
&newDescriptorSetAllocated));
if (newDescriptorSetAllocated)
{
for (const gl::ShaderType shaderType : glExecutable.getLinkedShaderStages())
{
ProgramVk *programVk = getShaderProgram(shaderType);
mExecutable.updateDefaultUniformsDescriptorSet(
shaderType, programVk->getDefaultUniformBlocks()[shaderType],
defaultUniformBuffer, contextVk);
mExecutable.updateTransformFeedbackDescriptorSetImpl(programVk->getState(),
contextVk);
}
}
}
return angle::Result::Continue;
}
bool ProgramPipelineVk::hasDirtyUniforms() const
{
for (const gl::ShaderType shaderType : gl::AllShaderTypes())
{
const ProgramVk *program = getShaderProgram(shaderType);
if (program && program->hasDirtyUniforms())
{
return true;
}
}
return false;
}
void ProgramPipelineVk::setAllDefaultUniformsDirty()
{
const gl::ProgramExecutable &glExecutable = mState.getExecutable();
for (const gl::ShaderType shaderType : glExecutable.getLinkedShaderStages())
{
ProgramVk *programVk = getShaderProgram(shaderType);
ASSERT(programVk);
programVk->setShaderUniformDirtyBit(shaderType);
}
}
void ProgramPipelineVk::onProgramBind()
{
setAllDefaultUniformsDirty();
}
} // namespace rx