Commit 10ade024 by Tim Van Patten Committed by Commit Bot

Create the ProgramExecutableVk Class

The ProgramExecutableVk class is being created to collect data structures that are common to both ProgramVks and ProgramPipelineVks, as well as any shared functions. This allows callers to make Program-/ProgramPipeline- specific queries without needing to know exactly which responded by querying the current ProgramExecutableVk. This will also allow the necessary data structures to only be populated and stored within the ProgramExecutableVk when necessary and reused as often as necessary. A few things are being moved into this class: - mVariableInfoMap This information will be required to defer translating the SPIR-V until when the vulkan pipeline layout is determined and the actual locations are known.   This will also allow removing determining these locations twice (during GLSL->SPIR-V compilation and pipeline layout). - createPipelineLayout() - update*DescriptorSet() - Descriptor Set Layout lists - Pipeline Layout - Various other functions/members related to pipeplines, descriptor sets, etc. Bug: angleproject:3570 Change-Id: I4b5ababeafec865148783c8ffd4c15f659f4856d Reviewed-on: https://chromium-review.googlesource.com/c/angle/angle/+/2055656 Commit-Queue: Tim Van Patten <timvp@google.com> Reviewed-by: 's avatarJamie Madill <jmadill@chromium.org>
parent b19000f3
......@@ -43,6 +43,8 @@ _vulkan_backend_sources = [
"OverlayVk.h",
"PersistentCommandPool.cpp",
"PersistentCommandPool.h",
"ProgramExecutableVk.cpp",
"ProgramExecutableVk.h",
"ProgramPipelineVk.cpp",
"ProgramPipelineVk.h",
"ProgramVk.cpp",
......
......@@ -1267,7 +1267,8 @@ ANGLE_INLINE angle::Result ContextVk::handleDirtyTexturesImpl(
if (executable->hasTextures(mState))
{
ANGLE_TRY(mProgram->updateTexturesDescriptorSet(this));
ANGLE_TRY(
mProgram->getExecutable().updateTexturesDescriptorSet(mProgram->getState(), this));
}
return angle::Result::Continue;
......@@ -1345,8 +1346,8 @@ ANGLE_INLINE angle::Result ContextVk::handleDirtyShaderResourcesImpl(
if (executable->hasUniformBuffers(mState) || executable->hasStorageBuffers(mState) ||
executable->hasAtomicCounterBuffers(mState) || executable->hasImages(mState))
{
ANGLE_TRY(mProgram->updateShaderResourcesDescriptorSet(this, &mResourceUseList,
commandBufferHelper));
ANGLE_TRY(mProgram->getExecutable().updateShaderResourcesDescriptorSet(
mProgram->getState(), this, &mResourceUseList, commandBufferHelper));
}
return angle::Result::Continue;
}
......@@ -1388,7 +1389,8 @@ angle::Result ContextVk::handleDirtyGraphicsTransformFeedbackBuffersEmulation(
&mResourceUseList, VK_ACCESS_TRANSFORM_FEEDBACK_WRITE_BIT_EXT, &bufferHelper);
}
ANGLE_TRY(mProgram->updateTransformFeedbackDescriptorSet(this));
ANGLE_TRY(mProgram->getExecutable().updateTransformFeedbackDescriptorSet(
mProgram->getState(), mProgram->getDefaultUniformBlocks(), this));
}
return angle::Result::Continue;
}
......@@ -1466,7 +1468,7 @@ angle::Result ContextVk::handleDirtyGraphicsTransformFeedbackState(const gl::Con
angle::Result ContextVk::handleDirtyDescriptorSets(const gl::Context *context,
vk::CommandBuffer *commandBuffer)
{
ANGLE_TRY(mProgram->updateDescriptorSets(this, commandBuffer));
ANGLE_TRY(mProgram->getExecutable().updateDescriptorSets(this, commandBuffer));
return angle::Result::Continue;
}
......
//
// 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.
//
// ProgramExecutableVk.cpp: Collects the information and interfaces common to both ProgramVks and
// ProgramPipelineVks in order to execute/draw with either.
#include "libANGLE/renderer/vulkan/ProgramExecutableVk.h"
#include "libANGLE/renderer/vulkan/BufferVk.h"
#include "libANGLE/renderer/vulkan/TextureVk.h"
#include "libANGLE/renderer/vulkan/TransformFeedbackVk.h"
#include "libANGLE/renderer/vulkan/vk_helpers.h"
namespace rx
{
DefaultUniformBlock::DefaultUniformBlock() = default;
DefaultUniformBlock::~DefaultUniformBlock() = default;
ProgramExecutableVk::ProgramExecutableVk()
: mStorageBlockBindingsOffset(0),
mAtomicCounterBufferBindingsOffset(0),
mImageBindingsOffset(0),
mEmptyDescriptorSets{},
mDynamicBufferOffsets{}
{}
ProgramExecutableVk::~ProgramExecutableVk() = default;
void ProgramExecutableVk::reset(ContextVk *contextVk)
{
RendererVk *renderer = contextVk->getRenderer();
clearVariableInfoMap();
for (auto &descriptorSetLayout : mDescriptorSetLayouts)
{
descriptorSetLayout.reset();
}
mPipelineLayout.reset();
mEmptyBuffer.release(renderer);
mDescriptorSets.clear();
mEmptyDescriptorSets.fill(VK_NULL_HANDLE);
for (vk::RefCountedDescriptorPoolBinding &binding : mDescriptorPoolBindings)
{
binding.reset();
}
for (vk::DynamicDescriptorPool &descriptorPool : mDynamicDescriptorPools)
{
descriptorPool.release(contextVk);
}
mTextureDescriptorsCache.clear();
mDescriptorBuffersCache.clear();
}
void ProgramExecutableVk::clearVariableInfoMap()
{
mVariableInfoMap.clear();
}
void ProgramExecutableVk::updateBindingOffsets(const gl::ProgramState &programState)
{
mStorageBlockBindingsOffset = static_cast<uint32_t>(programState.getUniqueUniformBlockCount());
mAtomicCounterBufferBindingsOffset = static_cast<uint32_t>(
mStorageBlockBindingsOffset + programState.getUniqueStorageBlockCount());
uint32_t atomicCounterBindingCount = programState.getAtomicCounterBuffers().empty() ? 0 : 1;
mImageBindingsOffset = mAtomicCounterBufferBindingsOffset + atomicCounterBindingCount;
}
uint32_t GetInterfaceBlockArraySize(const std::vector<gl::InterfaceBlock> &blocks,
uint32_t bufferIndex)
{
const gl::InterfaceBlock &block = blocks[bufferIndex];
if (!block.isArray)
{
return 1;
}
ASSERT(block.arrayElement == 0);
// Search consecutively until all array indices of this block are visited.
uint32_t arraySize;
for (arraySize = 1; bufferIndex + arraySize < blocks.size(); ++arraySize)
{
const gl::InterfaceBlock &nextBlock = blocks[bufferIndex + arraySize];
if (nextBlock.arrayElement != arraySize)
{
break;
}
// It's unexpected for an array to start at a non-zero array size, so we can always rely on
// the sequential `arrayElement`s to belong to the same block.
ASSERT(nextBlock.name == block.name);
ASSERT(nextBlock.isArray);
}
return arraySize;
}
angle::Result ProgramExecutableVk::allocateDescriptorSet(ContextVk *contextVk,
uint32_t descriptorSetIndex)
{
bool ignoreNewPoolAllocated;
return allocateDescriptorSetAndGetInfo(contextVk, descriptorSetIndex, &ignoreNewPoolAllocated);
}
angle::Result ProgramExecutableVk::allocateDescriptorSetAndGetInfo(ContextVk *contextVk,
uint32_t descriptorSetIndex,
bool *newPoolAllocatedOut)
{
vk::DynamicDescriptorPool &dynamicDescriptorPool = mDynamicDescriptorPools[descriptorSetIndex];
uint32_t potentialNewCount = descriptorSetIndex + 1;
if (potentialNewCount > mDescriptorSets.size())
{
mDescriptorSets.resize(potentialNewCount, VK_NULL_HANDLE);
}
const vk::DescriptorSetLayout &descriptorSetLayout =
mDescriptorSetLayouts[descriptorSetIndex].get();
ANGLE_TRY(dynamicDescriptorPool.allocateSetsAndGetInfo(
contextVk, descriptorSetLayout.ptr(), 1, &mDescriptorPoolBindings[descriptorSetIndex],
&mDescriptorSets[descriptorSetIndex], newPoolAllocatedOut));
mEmptyDescriptorSets[descriptorSetIndex] = VK_NULL_HANDLE;
return angle::Result::Continue;
}
void AddInterfaceBlockDescriptorSetDesc(const std::vector<gl::InterfaceBlock> &blocks,
uint32_t bindingStart,
VkDescriptorType descType,
vk::DescriptorSetLayoutDesc *descOut)
{
uint32_t bindingIndex = 0;
for (uint32_t bufferIndex = 0; bufferIndex < blocks.size();)
{
const uint32_t arraySize = GetInterfaceBlockArraySize(blocks, bufferIndex);
VkShaderStageFlags activeStages =
gl_vk::GetShaderStageFlags(blocks[bufferIndex].activeShaders());
descOut->update(bindingStart + bindingIndex, descType, arraySize, activeStages);
bufferIndex += arraySize;
++bindingIndex;
}
}
void AddAtomicCounterBufferDescriptorSetDesc(
const std::vector<gl::AtomicCounterBuffer> &atomicCounterBuffers,
uint32_t bindingStart,
vk::DescriptorSetLayoutDesc *descOut)
{
if (atomicCounterBuffers.empty())
{
return;
}
VkShaderStageFlags activeStages = 0;
for (const gl::AtomicCounterBuffer &buffer : atomicCounterBuffers)
{
activeStages |= gl_vk::GetShaderStageFlags(buffer.activeShaders());
}
// A single storage buffer array is used for all stages for simplicity.
descOut->update(bindingStart, VK_DESCRIPTOR_TYPE_STORAGE_BUFFER,
gl::IMPLEMENTATION_MAX_ATOMIC_COUNTER_BUFFERS, activeStages);
}
void AddImageDescriptorSetDesc(const gl::ProgramState &programState,
uint32_t bindingStart,
vk::DescriptorSetLayoutDesc *descOut)
{
const std::vector<gl::ImageBinding> &imageBindings = programState.getImageBindings();
const std::vector<gl::LinkedUniform> &uniforms = programState.getUniforms();
for (uint32_t imageIndex = 0; imageIndex < imageBindings.size(); ++imageIndex)
{
const gl::ImageBinding &imageBinding = imageBindings[imageIndex];
uint32_t uniformIndex = programState.getUniformIndexFromImageIndex(imageIndex);
const gl::LinkedUniform &imageUniform = uniforms[uniformIndex];
// The front-end always binds array image units sequentially.
uint32_t arraySize = static_cast<uint32_t>(imageBinding.boundImageUnits.size());
VkShaderStageFlags activeStages = gl_vk::GetShaderStageFlags(imageUniform.activeShaders());
uint32_t bindingIndex = bindingStart + imageIndex;
descOut->update(bindingIndex, VK_DESCRIPTOR_TYPE_STORAGE_IMAGE, arraySize, activeStages);
}
}
void AddTextureDescriptorSetDesc(const gl::ProgramState &programState,
bool useOldRewriteStructSamplers,
vk::DescriptorSetLayoutDesc *descOut)
{
uint32_t bindingIndex = 0;
const std::vector<gl::SamplerBinding> &samplerBindings = programState.getSamplerBindings();
const std::vector<gl::LinkedUniform> &uniforms = programState.getUniforms();
for (uint32_t textureIndex = 0; textureIndex < samplerBindings.size(); ++textureIndex)
{
const gl::SamplerBinding &samplerBinding = samplerBindings[textureIndex];
uint32_t uniformIndex = programState.getUniformIndexFromSamplerIndex(textureIndex);
const gl::LinkedUniform &samplerUniform = uniforms[uniformIndex];
// The front-end always binds array sampler units sequentially.
uint32_t arraySize = static_cast<uint32_t>(samplerBinding.boundTextureUnits.size());
VkShaderStageFlags activeStages =
gl_vk::GetShaderStageFlags(samplerUniform.activeShaders());
if (!useOldRewriteStructSamplers)
{
// 2D arrays are split into multiple 1D arrays when generating
// LinkedUniforms. Since they are flattened into one array, ignore the
// nonzero elements and expand the array to the total array size.
if (gl::SamplerNameContainsNonZeroArrayElement(samplerUniform.name))
{
continue;
}
for (unsigned int outerArraySize : samplerUniform.outerArraySizes)
{
arraySize *= outerArraySize;
}
}
descOut->update(bindingIndex++, VK_DESCRIPTOR_TYPE_COMBINED_IMAGE_SAMPLER, arraySize,
activeStages);
}
}
void WriteBufferDescriptorSetBinding(const gl::OffsetBindingPointer<gl::Buffer> &bufferBinding,
VkDeviceSize maxSize,
VkDescriptorSet descSet,
VkDescriptorType descType,
uint32_t bindingIndex,
uint32_t arrayElement,
VkDeviceSize requiredOffsetAlignment,
VkDescriptorBufferInfo *bufferInfoOut,
VkWriteDescriptorSet *writeInfoOut)
{
gl::Buffer *buffer = bufferBinding.get();
ASSERT(buffer != nullptr);
// Make sure there's no possible under/overflow with binding size.
static_assert(sizeof(VkDeviceSize) >= sizeof(bufferBinding.getSize()),
"VkDeviceSize too small");
ASSERT(bufferBinding.getSize() >= 0);
BufferVk *bufferVk = vk::GetImpl(buffer);
VkDeviceSize offset = bufferBinding.getOffset();
VkDeviceSize size = bufferBinding.getSize();
vk::BufferHelper &bufferHelper = bufferVk->getBuffer();
// If size is 0, we can't always use VK_WHOLE_SIZE (or bufferHelper.getSize()), as the
// backing buffer may be larger than max*BufferRange. In that case, we use the minimum of
// the backing buffer size (what's left after offset) and the buffer size as defined by the
// shader. That latter is only valid for UBOs, as SSBOs may have variable length arrays.
size = size > 0 ? size : (bufferHelper.getSize() - offset);
if (maxSize > 0)
{
size = std::min(size, maxSize);
}
// If requiredOffsetAlignment is 0, the buffer offset is guaranteed to have the necessary
// alignment through other means (the backend specifying the alignment through a GLES limit that
// the frontend then enforces). If it's not 0, we need to bind the buffer at an offset that's
// aligned. The difference in offsets is communicated to the shader via driver uniforms.
if (requiredOffsetAlignment)
{
VkDeviceSize alignedOffset = (offset / requiredOffsetAlignment) * requiredOffsetAlignment;
VkDeviceSize offsetDiff = offset - alignedOffset;
offset = alignedOffset;
size += offsetDiff;
}
bufferInfoOut->buffer = bufferHelper.getBuffer().getHandle();
bufferInfoOut->offset = offset;
bufferInfoOut->range = size;
writeInfoOut->sType = VK_STRUCTURE_TYPE_WRITE_DESCRIPTOR_SET;
writeInfoOut->pNext = nullptr;
writeInfoOut->dstSet = descSet;
writeInfoOut->dstBinding = bindingIndex;
writeInfoOut->dstArrayElement = arrayElement;
writeInfoOut->descriptorCount = 1;
writeInfoOut->descriptorType = descType;
writeInfoOut->pImageInfo = nullptr;
writeInfoOut->pBufferInfo = bufferInfoOut;
writeInfoOut->pTexelBufferView = nullptr;
ASSERT(writeInfoOut->pBufferInfo[0].buffer != VK_NULL_HANDLE);
}
angle::Result ProgramExecutableVk::createPipelineLayout(const gl::Context *glContext,
const gl::ProgramExecutable &glExecutable,
const gl::ProgramState &programState)
{
const gl::State &glState = glContext->getState();
ContextVk *contextVk = vk::GetImpl(glContext);
RendererVk *renderer = contextVk->getRenderer();
gl::TransformFeedback *transformFeedback = glState.getCurrentTransformFeedback();
const gl::ShaderBitSet &linkedShaderStages = glExecutable.getLinkedShaderStages();
reset(contextVk);
updateBindingOffsets(programState);
// Store a reference to the pipeline and descriptor set layouts. This will create them if they
// don't already exist in the cache.
// Default uniforms and transform feedback:
vk::DescriptorSetLayoutDesc uniformsAndXfbSetDesc;
uint32_t uniformBindingIndex = 0;
for (const gl::ShaderType shaderType : linkedShaderStages)
{
uniformsAndXfbSetDesc.update(uniformBindingIndex++,
VK_DESCRIPTOR_TYPE_UNIFORM_BUFFER_DYNAMIC, 1,
gl_vk::kShaderStageMap[shaderType]);
}
bool hasVertexShader = glExecutable.hasLinkedShaderStage(gl::ShaderType::Vertex);
bool hasXfbVaryings = !programState.getLinkedTransformFeedbackVaryings().empty();
if (hasVertexShader && transformFeedback && hasXfbVaryings)
{
size_t xfbBufferCount = programState.getTransformFeedbackBufferCount();
TransformFeedbackVk *transformFeedbackVk = vk::GetImpl(transformFeedback);
transformFeedbackVk->updateDescriptorSetLayout(contextVk, xfbBufferCount,
&uniformsAndXfbSetDesc);
}
ANGLE_TRY(renderer->getDescriptorSetLayout(
contextVk, uniformsAndXfbSetDesc,
&mDescriptorSetLayouts[kUniformsAndXfbDescriptorSetIndex]));
// Uniform and storage buffers, atomic counter buffers and images:
vk::DescriptorSetLayoutDesc resourcesSetDesc;
AddInterfaceBlockDescriptorSetDesc(programState.getUniformBlocks(),
getUniformBlockBindingsOffset(),
VK_DESCRIPTOR_TYPE_UNIFORM_BUFFER, &resourcesSetDesc);
AddInterfaceBlockDescriptorSetDesc(programState.getShaderStorageBlocks(),
getStorageBlockBindingsOffset(),
VK_DESCRIPTOR_TYPE_STORAGE_BUFFER, &resourcesSetDesc);
AddAtomicCounterBufferDescriptorSetDesc(programState.getAtomicCounterBuffers(),
getAtomicCounterBufferBindingsOffset(),
&resourcesSetDesc);
AddImageDescriptorSetDesc(programState, getImageBindingsOffset(), &resourcesSetDesc);
ANGLE_TRY(renderer->getDescriptorSetLayout(
contextVk, resourcesSetDesc, &mDescriptorSetLayouts[kShaderResourceDescriptorSetIndex]));
// Textures:
vk::DescriptorSetLayoutDesc texturesSetDesc;
AddTextureDescriptorSetDesc(programState, contextVk->useOldRewriteStructSamplers(),
&texturesSetDesc);
ANGLE_TRY(renderer->getDescriptorSetLayout(contextVk, texturesSetDesc,
&mDescriptorSetLayouts[kTextureDescriptorSetIndex]));
// Driver uniforms:
VkShaderStageFlags driverUniformsStages = programState.getProgramExecutable().isCompute()
? VK_SHADER_STAGE_COMPUTE_BIT
: VK_SHADER_STAGE_ALL_GRAPHICS;
vk::DescriptorSetLayoutDesc driverUniformsSetDesc =
contextVk->getDriverUniformsDescriptorSetDesc(driverUniformsStages);
ANGLE_TRY(renderer->getDescriptorSetLayout(
contextVk, driverUniformsSetDesc,
&mDescriptorSetLayouts[kDriverUniformsDescriptorSetIndex]));
// Create pipeline layout with these 4 descriptor sets.
vk::PipelineLayoutDesc pipelineLayoutDesc;
pipelineLayoutDesc.updateDescriptorSetLayout(kUniformsAndXfbDescriptorSetIndex,
uniformsAndXfbSetDesc);
pipelineLayoutDesc.updateDescriptorSetLayout(kShaderResourceDescriptorSetIndex,
resourcesSetDesc);
pipelineLayoutDesc.updateDescriptorSetLayout(kTextureDescriptorSetIndex, texturesSetDesc);
pipelineLayoutDesc.updateDescriptorSetLayout(kDriverUniformsDescriptorSetIndex,
driverUniformsSetDesc);
ANGLE_TRY(renderer->getPipelineLayout(contextVk, pipelineLayoutDesc, mDescriptorSetLayouts,
&mPipelineLayout));
// Initialize descriptor pools.
std::array<VkDescriptorPoolSize, 2> uniformAndXfbSetSize = {
{{VK_DESCRIPTOR_TYPE_UNIFORM_BUFFER_DYNAMIC,
static_cast<uint32_t>(programState.getProgramExecutable().getLinkedShaderStageCount())},
{VK_DESCRIPTOR_TYPE_STORAGE_BUFFER, gl::IMPLEMENTATION_MAX_TRANSFORM_FEEDBACK_BUFFERS}}};
uint32_t uniformBlockCount = static_cast<uint32_t>(programState.getUniformBlocks().size());
uint32_t storageBlockCount =
static_cast<uint32_t>(programState.getShaderStorageBlocks().size());
uint32_t atomicCounterBufferCount =
static_cast<uint32_t>(programState.getAtomicCounterBuffers().size());
uint32_t imageCount = static_cast<uint32_t>(programState.getImageBindings().size());
uint32_t textureCount = static_cast<uint32_t>(programState.getSamplerBindings().size());
if (renderer->getFeatures().bindEmptyForUnusedDescriptorSets.enabled)
{
// For this workaround, we have to create an empty descriptor set for each descriptor set
// index, so make sure their pools are initialized.
uniformBlockCount = std::max(uniformBlockCount, 1u);
textureCount = std::max(textureCount, 1u);
}
constexpr size_t kResourceTypesInResourcesSet = 3;
angle::FixedVector<VkDescriptorPoolSize, kResourceTypesInResourcesSet> resourceSetSize;
if (uniformBlockCount > 0)
{
resourceSetSize.emplace_back(VK_DESCRIPTOR_TYPE_UNIFORM_BUFFER, uniformBlockCount);
}
if (storageBlockCount > 0 || atomicCounterBufferCount > 0)
{
// Note that we always use an array of IMPLEMENTATION_MAX_ATOMIC_COUNTER_BUFFERS storage
// buffers for emulating atomic counters, so if there are any atomic counter buffers, we
// need to allocate IMPLEMENTATION_MAX_ATOMIC_COUNTER_BUFFERS descriptors.
const uint32_t atomicCounterStorageBufferCount =
atomicCounterBufferCount > 0 ? gl::IMPLEMENTATION_MAX_ATOMIC_COUNTER_BUFFERS : 0;
const uint32_t storageBufferDescCount = storageBlockCount + atomicCounterStorageBufferCount;
resourceSetSize.emplace_back(VK_DESCRIPTOR_TYPE_STORAGE_BUFFER, storageBufferDescCount);
}
if (imageCount > 0)
{
resourceSetSize.emplace_back(VK_DESCRIPTOR_TYPE_STORAGE_IMAGE, imageCount);
}
VkDescriptorPoolSize textureSetSize = {VK_DESCRIPTOR_TYPE_COMBINED_IMAGE_SAMPLER, textureCount};
ANGLE_TRY(mDynamicDescriptorPools[kUniformsAndXfbDescriptorSetIndex].init(
contextVk, uniformAndXfbSetSize.data(), uniformAndXfbSetSize.size()));
if (resourceSetSize.size() > 0)
{
ANGLE_TRY(mDynamicDescriptorPools[kShaderResourceDescriptorSetIndex].init(
contextVk, resourceSetSize.data(), static_cast<uint32_t>(resourceSetSize.size())));
}
if (textureCount > 0)
{
ANGLE_TRY(mDynamicDescriptorPools[kTextureDescriptorSetIndex].init(contextVk,
&textureSetSize, 1));
}
mDynamicBufferOffsets.resize(programState.getProgramExecutable().getLinkedShaderStageCount());
// Initialize an "empty" buffer for use with default uniform blocks where there are no uniforms,
// or atomic counter buffer array indices that are unused.
constexpr VkBufferUsageFlags kEmptyBufferUsage =
VK_BUFFER_USAGE_UNIFORM_BUFFER_BIT | VK_BUFFER_USAGE_STORAGE_BUFFER_BIT;
VkBufferCreateInfo emptyBufferInfo = {};
emptyBufferInfo.sType = VK_STRUCTURE_TYPE_BUFFER_CREATE_INFO;
emptyBufferInfo.flags = 0;
emptyBufferInfo.size = 4;
emptyBufferInfo.usage = kEmptyBufferUsage;
emptyBufferInfo.sharingMode = VK_SHARING_MODE_EXCLUSIVE;
emptyBufferInfo.queueFamilyIndexCount = 0;
emptyBufferInfo.pQueueFamilyIndices = nullptr;
constexpr VkMemoryPropertyFlags kMemoryType = VK_MEMORY_PROPERTY_DEVICE_LOCAL_BIT;
return mEmptyBuffer.init(contextVk, emptyBufferInfo, kMemoryType);
}
void ProgramExecutableVk::updateDefaultUniformsDescriptorSet(
const gl::ProgramState &programState,
gl::ShaderMap<DefaultUniformBlock> &defaultUniformBlocks,
ContextVk *contextVk)
{
uint32_t shaderStageCount =
static_cast<uint32_t>(programState.getProgramExecutable().getLinkedShaderStageCount());
gl::ShaderVector<VkDescriptorBufferInfo> descriptorBufferInfo(shaderStageCount);
gl::ShaderVector<VkWriteDescriptorSet> writeDescriptorInfo(shaderStageCount);
uint32_t bindingIndex = 0;
mDescriptorBuffersCache.clear();
// Write default uniforms for each shader type.
for (const gl::ShaderType shaderType :
programState.getProgramExecutable().getLinkedShaderStages())
{
DefaultUniformBlock &uniformBlock = defaultUniformBlocks[shaderType];
VkDescriptorBufferInfo &bufferInfo = descriptorBufferInfo[bindingIndex];
VkWriteDescriptorSet &writeInfo = writeDescriptorInfo[bindingIndex];
if (!uniformBlock.uniformData.empty())
{
vk::BufferHelper *bufferHelper = uniformBlock.storage.getCurrentBuffer();
bufferInfo.buffer = bufferHelper->getBuffer().getHandle();
mDescriptorBuffersCache.emplace_back(bufferHelper);
}
else
{
mEmptyBuffer.retain(&contextVk->getResourceUseList());
bufferInfo.buffer = mEmptyBuffer.getBuffer().getHandle();
mDescriptorBuffersCache.emplace_back(&mEmptyBuffer);
}
bufferInfo.offset = 0;
bufferInfo.range = VK_WHOLE_SIZE;
writeInfo.sType = VK_STRUCTURE_TYPE_WRITE_DESCRIPTOR_SET;
writeInfo.pNext = nullptr;
writeInfo.dstSet = mDescriptorSets[kUniformsAndXfbDescriptorSetIndex];
writeInfo.dstBinding = bindingIndex;
writeInfo.dstArrayElement = 0;
writeInfo.descriptorCount = 1;
writeInfo.descriptorType = VK_DESCRIPTOR_TYPE_UNIFORM_BUFFER_DYNAMIC;
writeInfo.pImageInfo = nullptr;
writeInfo.pBufferInfo = &bufferInfo;
writeInfo.pTexelBufferView = nullptr;
++bindingIndex;
}
VkDevice device = contextVk->getDevice();
ASSERT(bindingIndex == shaderStageCount);
ASSERT(shaderStageCount <= kReservedDefaultUniformBindingCount);
vkUpdateDescriptorSets(device, shaderStageCount, writeDescriptorInfo.data(), 0, nullptr);
}
void ProgramExecutableVk::updateBuffersDescriptorSet(ContextVk *contextVk,
vk::ResourceUseList *resourceUseList,
CommandBufferHelper *commandBufferHelper,
const std::vector<gl::InterfaceBlock> &blocks,
VkDescriptorType descriptorType)
{
if (blocks.empty())
{
return;
}
VkDescriptorSet descriptorSet = mDescriptorSets[kShaderResourceDescriptorSetIndex];
ASSERT(descriptorType == VK_DESCRIPTOR_TYPE_UNIFORM_BUFFER ||
descriptorType == VK_DESCRIPTOR_TYPE_STORAGE_BUFFER);
const bool isStorageBuffer = descriptorType == VK_DESCRIPTOR_TYPE_STORAGE_BUFFER;
const uint32_t bindingStart =
isStorageBuffer ? getStorageBlockBindingsOffset() : getUniformBlockBindingsOffset();
static_assert(
gl::IMPLEMENTATION_MAX_SHADER_STORAGE_BUFFER_BINDINGS >=
gl::IMPLEMENTATION_MAX_UNIFORM_BUFFER_BINDINGS,
"The descriptor arrays here would have inadequate size for uniform buffer objects");
gl::StorageBuffersArray<VkDescriptorBufferInfo> descriptorBufferInfo;
gl::StorageBuffersArray<VkWriteDescriptorSet> writeDescriptorInfo;
uint32_t writeCount = 0;
// The binding is incremented every time arrayElement 0 is encountered, which means there will
// be an increment right at the start. Start from -1 to get 0 as the first binding.
int32_t currentBinding = -1;
// Write uniform or storage buffers.
const gl::State &glState = contextVk->getState();
for (uint32_t bufferIndex = 0; bufferIndex < blocks.size(); ++bufferIndex)
{
const gl::InterfaceBlock &block = blocks[bufferIndex];
const gl::OffsetBindingPointer<gl::Buffer> &bufferBinding =
isStorageBuffer ? glState.getIndexedShaderStorageBuffer(block.binding)
: glState.getIndexedUniformBuffer(block.binding);
if (!block.isArray || block.arrayElement == 0)
{
// Array indices of the same buffer binding are placed sequentially in `blocks`.
// Thus, the block binding is updated only when array index 0 is encountered.
++currentBinding;
}
if (bufferBinding.get() == nullptr)
{
continue;
}
uint32_t binding = bindingStart + currentBinding;
uint32_t arrayElement = block.isArray ? block.arrayElement : 0;
VkDeviceSize maxBlockSize = isStorageBuffer ? 0 : block.dataSize;
VkDescriptorBufferInfo &bufferInfo = descriptorBufferInfo[writeCount];
VkWriteDescriptorSet &writeInfo = writeDescriptorInfo[writeCount];
WriteBufferDescriptorSetBinding(bufferBinding, maxBlockSize, descriptorSet, descriptorType,
binding, arrayElement, 0, &bufferInfo, &writeInfo);
BufferVk *bufferVk = vk::GetImpl(bufferBinding.get());
vk::BufferHelper &bufferHelper = bufferVk->getBuffer();
if (isStorageBuffer)
{
// We set the SHADER_READ_BIT to be conservative.
VkAccessFlags accessFlags = VK_ACCESS_SHADER_READ_BIT | VK_ACCESS_SHADER_WRITE_BIT;
commandBufferHelper->bufferWrite(resourceUseList, accessFlags, &bufferHelper);
}
else
{
commandBufferHelper->bufferRead(resourceUseList, VK_ACCESS_UNIFORM_READ_BIT,
&bufferHelper);
}
++writeCount;
}
VkDevice device = contextVk->getDevice();
vkUpdateDescriptorSets(device, writeCount, writeDescriptorInfo.data(), 0, nullptr);
}
void ProgramExecutableVk::updateAtomicCounterBuffersDescriptorSet(
const gl::ProgramState &programState,
ContextVk *contextVk,
vk::ResourceUseList *resourceUseList,
CommandBufferHelper *commandBufferHelper)
{
const gl::State &glState = contextVk->getState();
const std::vector<gl::AtomicCounterBuffer> &atomicCounterBuffers =
programState.getAtomicCounterBuffers();
if (atomicCounterBuffers.empty())
{
return;
}
VkDescriptorSet descriptorSet = mDescriptorSets[kShaderResourceDescriptorSetIndex];
const uint32_t bindingStart = getAtomicCounterBufferBindingsOffset();
gl::AtomicCounterBuffersArray<VkDescriptorBufferInfo> descriptorBufferInfo;
gl::AtomicCounterBuffersArray<VkWriteDescriptorSet> writeDescriptorInfo;
gl::AtomicCounterBufferMask writtenBindings;
RendererVk *rendererVk = contextVk->getRenderer();
const VkDeviceSize requiredOffsetAlignment =
rendererVk->getPhysicalDeviceProperties().limits.minStorageBufferOffsetAlignment;
// Write atomic counter buffers.
for (uint32_t bufferIndex = 0; bufferIndex < atomicCounterBuffers.size(); ++bufferIndex)
{
const gl::AtomicCounterBuffer &atomicCounterBuffer = atomicCounterBuffers[bufferIndex];
uint32_t binding = atomicCounterBuffer.binding;
const gl::OffsetBindingPointer<gl::Buffer> &bufferBinding =
glState.getIndexedAtomicCounterBuffer(binding);
if (bufferBinding.get() == nullptr)
{
continue;
}
VkDescriptorBufferInfo &bufferInfo = descriptorBufferInfo[binding];
VkWriteDescriptorSet &writeInfo = writeDescriptorInfo[binding];
WriteBufferDescriptorSetBinding(bufferBinding, 0, descriptorSet,
VK_DESCRIPTOR_TYPE_STORAGE_BUFFER, bindingStart, binding,
requiredOffsetAlignment, &bufferInfo, &writeInfo);
BufferVk *bufferVk = vk::GetImpl(bufferBinding.get());
vk::BufferHelper &bufferHelper = bufferVk->getBuffer();
// We set SHADER_READ_BIT to be conservative.
commandBufferHelper->bufferWrite(
resourceUseList, VK_ACCESS_SHADER_READ_BIT | VK_ACCESS_SHADER_WRITE_BIT, &bufferHelper);
writtenBindings.set(binding);
}
// Bind the empty buffer to every array slot that's unused.
mEmptyBuffer.retain(&contextVk->getResourceUseList());
for (size_t binding : ~writtenBindings)
{
VkDescriptorBufferInfo &bufferInfo = descriptorBufferInfo[binding];
VkWriteDescriptorSet &writeInfo = writeDescriptorInfo[binding];
bufferInfo.buffer = mEmptyBuffer.getBuffer().getHandle();
bufferInfo.offset = 0;
bufferInfo.range = VK_WHOLE_SIZE;
writeInfo.sType = VK_STRUCTURE_TYPE_WRITE_DESCRIPTOR_SET;
writeInfo.pNext = nullptr;
writeInfo.dstSet = descriptorSet;
writeInfo.dstBinding = bindingStart;
writeInfo.dstArrayElement = static_cast<uint32_t>(binding);
writeInfo.descriptorCount = 1;
writeInfo.descriptorType = VK_DESCRIPTOR_TYPE_STORAGE_BUFFER;
writeInfo.pImageInfo = nullptr;
writeInfo.pBufferInfo = &bufferInfo;
writeInfo.pTexelBufferView = nullptr;
}
VkDevice device = contextVk->getDevice();
vkUpdateDescriptorSets(device, gl::IMPLEMENTATION_MAX_ATOMIC_COUNTER_BUFFERS,
writeDescriptorInfo.data(), 0, nullptr);
}
angle::Result ProgramExecutableVk::updateImagesDescriptorSet(const gl::ProgramState &programState,
ContextVk *contextVk)
{
const gl::State &glState = contextVk->getState();
const std::vector<gl::ImageBinding> &imageBindings = programState.getImageBindings();
if (imageBindings.empty())
{
return angle::Result::Continue;
}
VkDescriptorSet descriptorSet = mDescriptorSets[kShaderResourceDescriptorSetIndex];
const gl::ActiveTextureArray<TextureVk *> &activeImages = contextVk->getActiveImages();
const uint32_t bindingStart = getImageBindingsOffset();
gl::ImagesArray<VkDescriptorImageInfo> descriptorImageInfo;
gl::ImagesArray<VkWriteDescriptorSet> writeDescriptorInfo;
uint32_t writeCount = 0;
// Write images.
for (uint32_t imageIndex = 0; imageIndex < imageBindings.size(); ++imageIndex)
{
const gl::ImageBinding &imageBinding = imageBindings[imageIndex];
ASSERT(!imageBinding.unreferenced);
for (uint32_t arrayElement = 0; arrayElement < imageBinding.boundImageUnits.size();
++arrayElement)
{
GLuint imageUnit = imageBinding.boundImageUnits[arrayElement];
const gl::ImageUnit &binding = glState.getImageUnit(imageUnit);
TextureVk *textureVk = activeImages[imageUnit];
vk::ImageHelper *image = &textureVk->getImage();
const vk::ImageView *imageView = nullptr;
ANGLE_TRY(textureVk->getStorageImageView(contextVk, (binding.layered == GL_TRUE),
binding.level, binding.layer, &imageView));
// Note: binding.access is unused because it is implied by the shader.
// TODO(syoussefi): Support image data reinterpretation by using binding.format.
// http://anglebug.com/3563
VkDescriptorImageInfo &imageInfo = descriptorImageInfo[writeCount];
VkWriteDescriptorSet &writeInfo = writeDescriptorInfo[writeCount];
imageInfo.sampler = VK_NULL_HANDLE;
imageInfo.imageView = imageView->getHandle();
imageInfo.imageLayout = image->getCurrentLayout();
writeInfo.sType = VK_STRUCTURE_TYPE_WRITE_DESCRIPTOR_SET;
writeInfo.pNext = nullptr;
writeInfo.dstSet = descriptorSet;
writeInfo.dstBinding = bindingStart + imageIndex;
writeInfo.dstArrayElement = arrayElement;
writeInfo.descriptorCount = 1;
writeInfo.descriptorType = VK_DESCRIPTOR_TYPE_STORAGE_IMAGE;
writeInfo.pImageInfo = &imageInfo;
writeInfo.pBufferInfo = nullptr;
writeInfo.pTexelBufferView = nullptr;
++writeCount;
}
}
VkDevice device = contextVk->getDevice();
vkUpdateDescriptorSets(device, writeCount, writeDescriptorInfo.data(), 0, nullptr);
return angle::Result::Continue;
}
angle::Result ProgramExecutableVk::updateShaderResourcesDescriptorSet(
const gl::ProgramState &programState,
ContextVk *contextVk,
vk::ResourceUseList *resourceUseList,
CommandBufferHelper *commandBufferHelper)
{
ANGLE_TRY(allocateDescriptorSet(contextVk, kShaderResourceDescriptorSetIndex));
updateBuffersDescriptorSet(contextVk, resourceUseList, commandBufferHelper,
programState.getUniformBlocks(), VK_DESCRIPTOR_TYPE_UNIFORM_BUFFER);
updateBuffersDescriptorSet(contextVk, resourceUseList, commandBufferHelper,
programState.getShaderStorageBlocks(),
VK_DESCRIPTOR_TYPE_STORAGE_BUFFER);
updateAtomicCounterBuffersDescriptorSet(programState, contextVk, resourceUseList,
commandBufferHelper);
return updateImagesDescriptorSet(programState, contextVk);
}
angle::Result ProgramExecutableVk::updateTransformFeedbackDescriptorSet(
const gl::ProgramState &programState,
gl::ShaderMap<DefaultUniformBlock> &defaultUniformBlocks,
ContextVk *contextVk)
{
const gl::State &glState = contextVk->getState();
const gl::ProgramExecutable &executable = programState.getProgramExecutable();
ASSERT(executable.hasTransformFeedbackOutput(glState));
ANGLE_TRY(allocateDescriptorSet(contextVk, kUniformsAndXfbDescriptorSetIndex));
updateDefaultUniformsDescriptorSet(programState, defaultUniformBlocks, contextVk);
updateTransformFeedbackDescriptorSetImpl(programState, contextVk);
return angle::Result::Continue;
}
void ProgramExecutableVk::updateTransformFeedbackDescriptorSetImpl(
const gl::ProgramState &programState,
ContextVk *contextVk)
{
const gl::State &glState = contextVk->getState();
gl::TransformFeedback *transformFeedback = glState.getCurrentTransformFeedback();
const gl::ProgramExecutable &executable = programState.getProgramExecutable();
if (!executable.hasTransformFeedbackOutput(glState))
{
// If xfb has no output there is no need to update descriptor set.
return;
}
if (!glState.isTransformFeedbackActive())
{
// We set empty Buffer to xfb descriptor set because xfb descriptor set
// requires valid buffer bindings, even if they are empty buffer,
// otherwise Vulkan validation layer generates errors.
if (transformFeedback)
{
TransformFeedbackVk *transformFeedbackVk = vk::GetImpl(transformFeedback);
transformFeedbackVk->initDescriptorSet(
contextVk, programState.getTransformFeedbackBufferCount(), &mEmptyBuffer,
mDescriptorSets[kUniformsAndXfbDescriptorSetIndex]);
}
return;
}
TransformFeedbackVk *transformFeedbackVk = vk::GetImpl(glState.getCurrentTransformFeedback());
transformFeedbackVk->updateDescriptorSet(contextVk, programState,
mDescriptorSets[kUniformsAndXfbDescriptorSetIndex]);
}
angle::Result ProgramExecutableVk::updateTexturesDescriptorSet(const gl::ProgramState &programState,
ContextVk *contextVk)
{
const gl::State &glState = contextVk->getState();
const vk::TextureDescriptorDesc &texturesDesc = contextVk->getActiveTexturesDesc();
const gl::ProgramExecutable &executable = programState.getProgramExecutable();
auto iter = mTextureDescriptorsCache.find(texturesDesc);
if (iter != mTextureDescriptorsCache.end())
{
mDescriptorSets[kTextureDescriptorSetIndex] = iter->second;
return angle::Result::Continue;
}
ASSERT(executable.hasTextures(glState));
bool newPoolAllocated;
ANGLE_TRY(
allocateDescriptorSetAndGetInfo(contextVk, kTextureDescriptorSetIndex, &newPoolAllocated));
// Clear descriptor set cache. It may no longer be valid.
if (newPoolAllocated)
{
mTextureDescriptorsCache.clear();
}
VkDescriptorSet descriptorSet = mDescriptorSets[kTextureDescriptorSetIndex];
gl::ActiveTextureArray<VkDescriptorImageInfo> descriptorImageInfo;
gl::ActiveTextureArray<VkWriteDescriptorSet> writeDescriptorInfo;
uint32_t writeCount = 0;
const gl::ActiveTextureArray<vk::TextureUnit> &activeTextures = contextVk->getActiveTextures();
bool emulateSeamfulCubeMapSampling = contextVk->emulateSeamfulCubeMapSampling();
bool useOldRewriteStructSamplers = contextVk->useOldRewriteStructSamplers();
std::unordered_map<std::string, uint32_t> mappedSamplerNameToBindingIndex;
std::unordered_map<std::string, uint32_t> mappedSamplerNameToArrayOffset;
uint32_t currentBindingIndex = 0;
for (uint32_t textureIndex = 0; textureIndex < programState.getSamplerBindings().size();
++textureIndex)
{
const gl::SamplerBinding &samplerBinding = programState.getSamplerBindings()[textureIndex];
ASSERT(!samplerBinding.unreferenced);
uint32_t uniformIndex = programState.getUniformIndexFromSamplerIndex(textureIndex);
const gl::LinkedUniform &samplerUniform = programState.getUniforms()[uniformIndex];
std::string mappedSamplerName = GlslangGetMappedSamplerName(samplerUniform.name);
if (useOldRewriteStructSamplers ||
mappedSamplerNameToBindingIndex.emplace(mappedSamplerName, currentBindingIndex).second)
{
currentBindingIndex++;
}
uint32_t bindingIndex = textureIndex;
uint32_t arrayOffset = 0;
uint32_t arraySize = static_cast<uint32_t>(samplerBinding.boundTextureUnits.size());
if (!useOldRewriteStructSamplers)
{
bindingIndex = mappedSamplerNameToBindingIndex[mappedSamplerName];
arrayOffset = mappedSamplerNameToArrayOffset[mappedSamplerName];
// Front-end generates array elements in order, so we can just increment
// the offset each time we process a nested array.
mappedSamplerNameToArrayOffset[mappedSamplerName] += arraySize;
}
for (uint32_t arrayElement = 0; arrayElement < arraySize; ++arrayElement)
{
GLuint textureUnit = samplerBinding.boundTextureUnits[arrayElement];
TextureVk *textureVk = activeTextures[textureUnit].texture;
SamplerVk *samplerVk = activeTextures[textureUnit].sampler;
vk::ImageHelper &image = textureVk->getImage();
VkDescriptorImageInfo &imageInfo = descriptorImageInfo[writeCount];
// Use bound sampler object if one present, otherwise use texture's sampler
const vk::Sampler &sampler =
(samplerVk != nullptr) ? samplerVk->getSampler() : textureVk->getSampler();
imageInfo.sampler = sampler.getHandle();
imageInfo.imageLayout = image.getCurrentLayout();
if (emulateSeamfulCubeMapSampling)
{
// If emulating seamful cubemapping, use the fetch image view. This is basically
// the same image view as read, except it's a 2DArray view for cube maps.
imageInfo.imageView =
textureVk->getFetchImageViewAndRecordUse(contextVk).getHandle();
}
else
{
imageInfo.imageView =
textureVk->getReadImageViewAndRecordUse(contextVk).getHandle();
}
VkWriteDescriptorSet &writeInfo = writeDescriptorInfo[writeCount];
writeInfo.sType = VK_STRUCTURE_TYPE_WRITE_DESCRIPTOR_SET;
writeInfo.pNext = nullptr;
writeInfo.dstSet = descriptorSet;
writeInfo.dstBinding = bindingIndex;
writeInfo.dstArrayElement = arrayOffset + arrayElement;
writeInfo.descriptorCount = 1;
writeInfo.descriptorType = VK_DESCRIPTOR_TYPE_COMBINED_IMAGE_SAMPLER;
writeInfo.pImageInfo = &imageInfo;
writeInfo.pBufferInfo = nullptr;
writeInfo.pTexelBufferView = nullptr;
++writeCount;
}
}
VkDevice device = contextVk->getDevice();
ASSERT(writeCount > 0);
vkUpdateDescriptorSets(device, writeCount, writeDescriptorInfo.data(), 0, nullptr);
mTextureDescriptorsCache.emplace(texturesDesc, descriptorSet);
return angle::Result::Continue;
}
angle::Result ProgramExecutableVk::updateDescriptorSets(ContextVk *contextVk,
vk::CommandBuffer *commandBuffer)
{
// Can probably use better dirty bits here.
if (mDescriptorSets.empty())
return angle::Result::Continue;
// Find the maximum non-null descriptor set. This is used in conjunction with a driver
// workaround to bind empty descriptor sets only for gaps in between 0 and max and avoid
// binding unnecessary empty descriptor sets for the sets beyond max.
const size_t descriptorSetStart = kUniformsAndXfbDescriptorSetIndex;
size_t descriptorSetRange = 0;
for (size_t descriptorSetIndex = descriptorSetStart;
descriptorSetIndex < mDescriptorSets.size(); ++descriptorSetIndex)
{
if (mDescriptorSets[descriptorSetIndex] != VK_NULL_HANDLE)
{
descriptorSetRange = descriptorSetIndex + 1;
}
}
const gl::State &glState = contextVk->getState();
const VkPipelineBindPoint pipelineBindPoint = glState.getProgramExecutable()->isCompute()
? VK_PIPELINE_BIND_POINT_COMPUTE
: VK_PIPELINE_BIND_POINT_GRAPHICS;
for (uint32_t descriptorSetIndex = descriptorSetStart; descriptorSetIndex < descriptorSetRange;
++descriptorSetIndex)
{
VkDescriptorSet descSet = mDescriptorSets[descriptorSetIndex];
if (descSet == VK_NULL_HANDLE)
{
if (!contextVk->getRenderer()->getFeatures().bindEmptyForUnusedDescriptorSets.enabled)
{
continue;
}
// Workaround a driver bug where missing (though unused) descriptor sets indices cause
// later sets to misbehave.
if (mEmptyDescriptorSets[descriptorSetIndex] == VK_NULL_HANDLE)
{
const vk::DescriptorSetLayout &descriptorSetLayout =
mDescriptorSetLayouts[descriptorSetIndex].get();
ANGLE_TRY(mDynamicDescriptorPools[descriptorSetIndex].allocateSets(
contextVk, descriptorSetLayout.ptr(), 1,
&mDescriptorPoolBindings[descriptorSetIndex],
&mEmptyDescriptorSets[descriptorSetIndex]));
}
descSet = mEmptyDescriptorSets[descriptorSetIndex];
}
// Default uniforms are encompassed in a block per shader stage, and they are assigned
// through dynamic uniform buffers (requiring dynamic offsets). No other descriptor
// requires a dynamic offset.
const uint32_t uniformBlockOffsetCount =
descriptorSetIndex == kUniformsAndXfbDescriptorSetIndex
? static_cast<uint32_t>(mDynamicBufferOffsets.size())
: 0;
commandBuffer->bindDescriptorSets(mPipelineLayout.get(), pipelineBindPoint,
descriptorSetIndex, 1, &descSet, uniformBlockOffsetCount,
mDynamicBufferOffsets.data());
}
for (vk::BufferHelper *buffer : mDescriptorBuffersCache)
{
buffer->retain(&contextVk->getResourceUseList());
}
return angle::Result::Continue;
}
} // namespace rx
//
// 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.
//
// ProgramExecutableVk.h: Collects the information and interfaces common to both ProgramVks and
// ProgramPipelineVks in order to execute/draw with either.
#ifndef LIBANGLE_RENDERER_VULKAN_PROGRAMEXECUTABLEVK_H_
#define LIBANGLE_RENDERER_VULKAN_PROGRAMEXECUTABLEVK_H_
#include "common/utilities.h"
#include "libANGLE/Context.h"
#include "libANGLE/InfoLog.h"
#include "libANGLE/renderer/glslang_wrapper_utils.h"
#include "libANGLE/renderer/vulkan/ContextVk.h"
#include "libANGLE/renderer/vulkan/vk_helpers.h"
namespace rx
{
// State for the default uniform blocks.
struct DefaultUniformBlock final : private angle::NonCopyable
{
DefaultUniformBlock();
~DefaultUniformBlock();
vk::DynamicBuffer storage;
// Shadow copies of the shader uniform data.
angle::MemoryBuffer uniformData;
// Since the default blocks are laid out in std140, this tells us where to write on a call
// to a setUniform method. They are arranged in uniform location order.
std::vector<sh::BlockMemberInfo> uniformLayout;
};
class ProgramExecutableVk
{
public:
ProgramExecutableVk();
virtual ~ProgramExecutableVk();
void reset(ContextVk *contextVk);
void clearVariableInfoMap();
ShaderInterfaceVariableInfoMap &getShaderInterfaceVariableInfoMap() { return mVariableInfoMap; }
angle::Result createPipelineLayout(const gl::Context *glContext,
const gl::ProgramExecutable &glExecutable,
const gl::ProgramState &programState);
angle::Result updateTexturesDescriptorSet(const gl::ProgramState &programState,
ContextVk *contextVk);
angle::Result updateShaderResourcesDescriptorSet(const gl::ProgramState &programState,
ContextVk *contextVk,
vk::ResourceUseList *resourceUseList,
CommandBufferHelper *commandBufferHelper);
angle::Result updateTransformFeedbackDescriptorSet(
const gl::ProgramState &programState,
gl::ShaderMap<DefaultUniformBlock> &defaultUniformBlocks,
ContextVk *contextVk);
angle::Result updateDescriptorSets(ContextVk *contextVk, vk::CommandBuffer *commandBuffer);
private:
friend class ProgramVk;
friend class ProgramPipelineVk;
void updateBindingOffsets(const gl::ProgramState &programState);
uint32_t getUniformBlockBindingsOffset() const { return 0; }
uint32_t getStorageBlockBindingsOffset() const { return mStorageBlockBindingsOffset; }
uint32_t getAtomicCounterBufferBindingsOffset() const
{
return mAtomicCounterBufferBindingsOffset;
}
uint32_t getImageBindingsOffset() const { return mImageBindingsOffset; }
angle::Result allocateDescriptorSet(ContextVk *contextVk, uint32_t descriptorSetIndex);
angle::Result allocateDescriptorSetAndGetInfo(ContextVk *contextVk,
uint32_t descriptorSetIndex,
bool *newPoolAllocatedOut);
void updateDefaultUniformsDescriptorSet(
const gl::ProgramState &programState,
gl::ShaderMap<DefaultUniformBlock> &defaultUniformBlocks,
ContextVk *contextVk);
void updateTransformFeedbackDescriptorSetImpl(const gl::ProgramState &programState,
ContextVk *contextVk);
void updateBuffersDescriptorSet(ContextVk *contextVk,
vk::ResourceUseList *resourceUseList,
CommandBufferHelper *commandBufferHelper,
const std::vector<gl::InterfaceBlock> &blocks,
VkDescriptorType descriptorType);
void updateAtomicCounterBuffersDescriptorSet(const gl::ProgramState &programState,
ContextVk *contextVk,
vk::ResourceUseList *resourceUseList,
CommandBufferHelper *commandBufferHelper);
angle::Result updateImagesDescriptorSet(const gl::ProgramState &programState,
ContextVk *contextVk);
// In their descriptor set, uniform buffers are placed first, then storage buffers, then atomic
// counter buffers and then images. These cached values contain the offsets where storage
// buffer, atomic counter buffer and image bindings start.
uint32_t mStorageBlockBindingsOffset;
uint32_t mAtomicCounterBufferBindingsOffset;
uint32_t mImageBindingsOffset;
// This is a special "empty" placeholder buffer for when a shader has no uniforms or doesn't
// use all slots in the atomic counter buffer array.
//
// It is necessary because we want to keep a compatible pipeline layout in all cases,
// and Vulkan does not tolerate having null handles in a descriptor set.
vk::BufferHelper mEmptyBuffer;
// Descriptor sets for uniform blocks and textures for this program.
std::vector<VkDescriptorSet> mDescriptorSets;
vk::DescriptorSetLayoutArray<VkDescriptorSet> mEmptyDescriptorSets;
std::vector<vk::BufferHelper *> mDescriptorBuffersCache;
std::unordered_map<vk::TextureDescriptorDesc, VkDescriptorSet> mTextureDescriptorsCache;
// We keep a reference to the pipeline and descriptor set layouts. This ensures they don't get
// deleted while this program is in use.
vk::BindingPointer<vk::PipelineLayout> mPipelineLayout;
vk::DescriptorSetLayoutPointerArray mDescriptorSetLayouts;
// Keep bindings to the descriptor pools. This ensures the pools stay valid while the Program
// is in use.
vk::DescriptorSetLayoutArray<vk::RefCountedDescriptorPoolBinding> mDescriptorPoolBindings;
// Store descriptor pools here. We store the descriptors in the Program to facilitate descriptor
// cache management. It can also allow fewer descriptors for shaders which use fewer
// textures/buffers.
vk::DescriptorSetLayoutArray<vk::DynamicDescriptorPool> mDynamicDescriptorPools;
gl::ShaderVector<uint32_t> mDynamicBufferOffsets;
ShaderInterfaceVariableInfoMap mVariableInfoMap;
};
} // namespace rx
#endif // LIBANGLE_RENDERER_VULKAN_PROGRAMEXECUTABLEVK_H_
......@@ -153,205 +153,6 @@ angle::Result SyncDefaultUniformBlock(ContextVk *contextVk,
return angle::Result::Continue;
}
uint32_t GetInterfaceBlockArraySize(const std::vector<gl::InterfaceBlock> &blocks,
uint32_t bufferIndex)
{
const gl::InterfaceBlock &block = blocks[bufferIndex];
if (!block.isArray)
{
return 1;
}
ASSERT(block.arrayElement == 0);
// Search consecutively until all array indices of this block are visited.
uint32_t arraySize;
for (arraySize = 1; bufferIndex + arraySize < blocks.size(); ++arraySize)
{
const gl::InterfaceBlock &nextBlock = blocks[bufferIndex + arraySize];
if (nextBlock.arrayElement != arraySize)
{
break;
}
// It's unexpected for an array to start at a non-zero array size, so we can always rely on
// the sequential `arrayElement`s to belong to the same block.
ASSERT(nextBlock.name == block.name);
ASSERT(nextBlock.isArray);
}
return arraySize;
}
void AddInterfaceBlockDescriptorSetDesc(const std::vector<gl::InterfaceBlock> &blocks,
uint32_t bindingStart,
VkDescriptorType descType,
vk::DescriptorSetLayoutDesc *descOut)
{
uint32_t bindingIndex = 0;
for (uint32_t bufferIndex = 0; bufferIndex < blocks.size();)
{
const uint32_t arraySize = GetInterfaceBlockArraySize(blocks, bufferIndex);
VkShaderStageFlags activeStages =
gl_vk::GetShaderStageFlags(blocks[bufferIndex].activeShaders());
descOut->update(bindingStart + bindingIndex, descType, arraySize, activeStages);
bufferIndex += arraySize;
++bindingIndex;
}
}
void AddAtomicCounterBufferDescriptorSetDesc(
const std::vector<gl::AtomicCounterBuffer> &atomicCounterBuffers,
uint32_t bindingStart,
vk::DescriptorSetLayoutDesc *descOut)
{
if (atomicCounterBuffers.empty())
{
return;
}
VkShaderStageFlags activeStages = 0;
for (const gl::AtomicCounterBuffer &buffer : atomicCounterBuffers)
{
activeStages |= gl_vk::GetShaderStageFlags(buffer.activeShaders());
}
// A single storage buffer array is used for all stages for simplicity.
descOut->update(bindingStart, VK_DESCRIPTOR_TYPE_STORAGE_BUFFER,
gl::IMPLEMENTATION_MAX_ATOMIC_COUNTER_BUFFERS, activeStages);
}
void AddImageDescriptorSetDesc(const gl::ProgramState &programState,
uint32_t bindingStart,
vk::DescriptorSetLayoutDesc *descOut)
{
const std::vector<gl::ImageBinding> &imageBindings = programState.getImageBindings();
const std::vector<gl::LinkedUniform> &uniforms = programState.getUniforms();
for (uint32_t imageIndex = 0; imageIndex < imageBindings.size(); ++imageIndex)
{
const gl::ImageBinding &imageBinding = imageBindings[imageIndex];
uint32_t uniformIndex = programState.getUniformIndexFromImageIndex(imageIndex);
const gl::LinkedUniform &imageUniform = uniforms[uniformIndex];
// The front-end always binds array image units sequentially.
uint32_t arraySize = static_cast<uint32_t>(imageBinding.boundImageUnits.size());
VkShaderStageFlags activeStages = gl_vk::GetShaderStageFlags(imageUniform.activeShaders());
uint32_t bindingIndex = bindingStart + imageIndex;
descOut->update(bindingIndex, VK_DESCRIPTOR_TYPE_STORAGE_IMAGE, arraySize, activeStages);
}
}
void AddTextureDescriptorSetDesc(const gl::ProgramState &programState,
bool useOldRewriteStructSamplers,
vk::DescriptorSetLayoutDesc *descOut)
{
uint32_t bindingIndex = 0;
const std::vector<gl::SamplerBinding> &samplerBindings = programState.getSamplerBindings();
const std::vector<gl::LinkedUniform> &uniforms = programState.getUniforms();
for (uint32_t textureIndex = 0; textureIndex < samplerBindings.size(); ++textureIndex)
{
const gl::SamplerBinding &samplerBinding = samplerBindings[textureIndex];
uint32_t uniformIndex = programState.getUniformIndexFromSamplerIndex(textureIndex);
const gl::LinkedUniform &samplerUniform = uniforms[uniformIndex];
// The front-end always binds array sampler units sequentially.
uint32_t arraySize = static_cast<uint32_t>(samplerBinding.boundTextureUnits.size());
VkShaderStageFlags activeStages =
gl_vk::GetShaderStageFlags(samplerUniform.activeShaders());
if (!useOldRewriteStructSamplers)
{
// 2D arrays are split into multiple 1D arrays when generating
// LinkedUniforms. Since they are flattened into one array, ignore the
// nonzero elements and expand the array to the total array size.
if (gl::SamplerNameContainsNonZeroArrayElement(samplerUniform.name))
{
continue;
}
for (unsigned int outerArraySize : samplerUniform.outerArraySizes)
{
arraySize *= outerArraySize;
}
}
descOut->update(bindingIndex++, VK_DESCRIPTOR_TYPE_COMBINED_IMAGE_SAMPLER, arraySize,
activeStages);
}
}
void WriteBufferDescriptorSetBinding(const gl::OffsetBindingPointer<gl::Buffer> &bufferBinding,
VkDeviceSize maxSize,
VkDescriptorSet descSet,
VkDescriptorType descType,
uint32_t bindingIndex,
uint32_t arrayElement,
VkDeviceSize requiredOffsetAlignment,
VkDescriptorBufferInfo *bufferInfoOut,
VkWriteDescriptorSet *writeInfoOut)
{
gl::Buffer *buffer = bufferBinding.get();
ASSERT(buffer != nullptr);
// Make sure there's no possible under/overflow with binding size.
static_assert(sizeof(VkDeviceSize) >= sizeof(bufferBinding.getSize()),
"VkDeviceSize too small");
ASSERT(bufferBinding.getSize() >= 0);
BufferVk *bufferVk = vk::GetImpl(buffer);
VkDeviceSize offset = bufferBinding.getOffset();
VkDeviceSize size = bufferBinding.getSize();
vk::BufferHelper &bufferHelper = bufferVk->getBuffer();
// If size is 0, we can't always use VK_WHOLE_SIZE (or bufferHelper.getSize()), as the
// backing buffer may be larger than max*BufferRange. In that case, we use the minimum of
// the backing buffer size (what's left after offset) and the buffer size as defined by the
// shader. That latter is only valid for UBOs, as SSBOs may have variable length arrays.
size = size > 0 ? size : (bufferHelper.getSize() - offset);
if (maxSize > 0)
{
size = std::min(size, maxSize);
}
// If requiredOffsetAlignment is 0, the buffer offset is guaranteed to have the necessary
// alignment through other means (the backend specifying the alignment through a GLES limit that
// the frontend then enforces). If it's not 0, we need to bind the buffer at an offset that's
// aligned. The difference in offsets is communicated to the shader via driver uniforms.
if (requiredOffsetAlignment)
{
VkDeviceSize alignedOffset = (offset / requiredOffsetAlignment) * requiredOffsetAlignment;
VkDeviceSize offsetDiff = offset - alignedOffset;
offset = alignedOffset;
size += offsetDiff;
}
bufferInfoOut->buffer = bufferHelper.getBuffer().getHandle();
bufferInfoOut->offset = offset;
bufferInfoOut->range = size;
writeInfoOut->sType = VK_STRUCTURE_TYPE_WRITE_DESCRIPTOR_SET;
writeInfoOut->pNext = nullptr;
writeInfoOut->dstSet = descSet;
writeInfoOut->dstBinding = bindingIndex;
writeInfoOut->dstArrayElement = arrayElement;
writeInfoOut->descriptorCount = 1;
writeInfoOut->descriptorType = descType;
writeInfoOut->pImageInfo = nullptr;
writeInfoOut->pBufferInfo = bufferInfoOut;
writeInfoOut->pTexelBufferView = nullptr;
ASSERT(writeInfoOut->pBufferInfo[0].buffer != VK_NULL_HANDLE);
}
class Std140BlockLayoutEncoderFactory : public gl::CustomBlockLayoutEncoderFactory
{
public:
......@@ -460,16 +261,7 @@ void ProgramVk::ProgramInfo::release(ContextVk *contextVk)
}
// ProgramVk implementation.
ProgramVk::DefaultUniformBlock::DefaultUniformBlock() {}
ProgramVk::DefaultUniformBlock::~DefaultUniformBlock() = default;
ProgramVk::ProgramVk(const gl::ProgramState &state)
: ProgramImpl(state),
mDynamicBufferOffsets{},
mStorageBlockBindingsOffset(0),
mAtomicCounterBufferBindingsOffset(0),
mImageBindingsOffset(0)
ProgramVk::ProgramVk(const gl::ProgramState &state) : ProgramImpl(state)
{
GlslangWrapperVk::ResetGlslangProgramInterfaceInfo(&mGlslangProgramInterfaceInfo);
}
......@@ -484,42 +276,20 @@ void ProgramVk::destroy(const gl::Context *context)
void ProgramVk::reset(ContextVk *contextVk)
{
for (auto &descriptorSetLayout : mDescriptorSetLayouts)
{
descriptorSetLayout.reset();
}
mPipelineLayout.reset();
RendererVk *renderer = contextVk->getRenderer();
for (auto &uniformBlock : mDefaultUniformBlocks)
{
uniformBlock.storage.release(renderer);
}
mShaderInfo.release(contextVk);
mDefaultProgramInfo.release(contextVk);
mLineRasterProgramInfo.release(contextVk);
mEmptyBuffer.release(renderer);
mDescriptorSets.clear();
mEmptyDescriptorSets.fill(VK_NULL_HANDLE);
for (vk::RefCountedDescriptorPoolBinding &binding : mDescriptorPoolBindings)
{
binding.reset();
}
for (vk::DynamicDescriptorPool &descriptorPool : mDynamicDescriptorPools)
for (auto &uniformBlock : mDefaultUniformBlocks)
{
descriptorPool.release(contextVk);
uniformBlock.storage.release(renderer);
}
mTextureDescriptorsCache.clear();
mDescriptorBuffersCache.clear();
GlslangWrapperVk::ResetGlslangProgramInterfaceInfo(&mGlslangProgramInterfaceInfo);
mExecutable.reset(contextVk);
}
std::unique_ptr<rx::LinkEvent> ProgramVk::load(const gl::Context *context,
......@@ -559,7 +329,9 @@ std::unique_ptr<rx::LinkEvent> ProgramVk::load(const gl::Context *context,
return std::make_unique<LinkEventDone>(status);
}
return std::make_unique<LinkEventDone>(linkImpl(context, infoLog));
const gl::ProgramExecutable &glExecutable = mState.getProgramExecutable();
status = mExecutable.createPipelineLayout(context, glExecutable, mState);
return std::make_unique<LinkEventDone>(status);
}
void ProgramVk::save(const gl::Context *context, gl::BinaryOutputStream *stream)
......@@ -609,13 +381,13 @@ std::unique_ptr<LinkEvent> ProgramVk::link(const gl::Context *context,
// Gather variable info and transform sources.
gl::ShaderMap<std::string> shaderSources;
ShaderInterfaceVariableInfoMap variableInfoMap;
GlslangWrapperVk::GetShaderSource(contextVk->getRenderer()->getFeatures(), mState, resources,
&mGlslangProgramInterfaceInfo, &shaderSources,
&variableInfoMap);
&mExecutable.mVariableInfoMap);
// Compile the shaders.
angle::Result status = mShaderInfo.initShaders(contextVk, shaderSources, variableInfoMap);
angle::Result status =
mShaderInfo.initShaders(contextVk, shaderSources, mExecutable.mVariableInfoMap);
if (status != angle::Result::Continue)
{
return std::make_unique<LinkEventDone>(status);
......@@ -629,176 +401,9 @@ std::unique_ptr<LinkEvent> ProgramVk::link(const gl::Context *context,
// TODO(jie.a.chen@intel.com): Parallelize linking.
// http://crbug.com/849576
return std::make_unique<LinkEventDone>(linkImpl(context, infoLog));
}
angle::Result ProgramVk::linkImpl(const gl::Context *glContext, gl::InfoLog &infoLog)
{
const gl::State &glState = glContext->getState();
ContextVk *contextVk = vk::GetImpl(glContext);
RendererVk *renderer = contextVk->getRenderer();
gl::TransformFeedback *transformFeedback = glState.getCurrentTransformFeedback();
const gl::ProgramExecutable &glExecutable = mState.getProgramExecutable();
updateBindingOffsets();
// Store a reference to the pipeline and descriptor set layouts. This will create them if they
// don't already exist in the cache.
// Default uniforms and transform feedback:
vk::DescriptorSetLayoutDesc uniformsAndXfbSetDesc;
uint32_t uniformBindingIndex = 0;
for (const gl::ShaderType shaderType : glExecutable.getLinkedShaderStages())
{
uniformsAndXfbSetDesc.update(uniformBindingIndex++,
VK_DESCRIPTOR_TYPE_UNIFORM_BUFFER_DYNAMIC, 1,
gl_vk::kShaderStageMap[shaderType]);
}
if (glExecutable.hasLinkedShaderStage(gl::ShaderType::Vertex) && transformFeedback &&
!mState.getLinkedTransformFeedbackVaryings().empty())
{
size_t xfbBufferCount = mState.getTransformFeedbackBufferCount();
TransformFeedbackVk *transformFeedbackVk = vk::GetImpl(transformFeedback);
transformFeedbackVk->updateDescriptorSetLayout(contextVk, xfbBufferCount,
&uniformsAndXfbSetDesc);
}
ANGLE_TRY(renderer->getDescriptorSetLayout(
contextVk, uniformsAndXfbSetDesc,
&mDescriptorSetLayouts[kUniformsAndXfbDescriptorSetIndex]));
// Uniform and storage buffers, atomic counter buffers and images:
vk::DescriptorSetLayoutDesc resourcesSetDesc;
AddInterfaceBlockDescriptorSetDesc(mState.getUniformBlocks(), getUniformBlockBindingsOffset(),
VK_DESCRIPTOR_TYPE_UNIFORM_BUFFER, &resourcesSetDesc);
AddInterfaceBlockDescriptorSetDesc(mState.getShaderStorageBlocks(),
getStorageBlockBindingsOffset(),
VK_DESCRIPTOR_TYPE_STORAGE_BUFFER, &resourcesSetDesc);
AddAtomicCounterBufferDescriptorSetDesc(mState.getAtomicCounterBuffers(),
getAtomicCounterBufferBindingsOffset(),
&resourcesSetDesc);
AddImageDescriptorSetDesc(mState, getImageBindingsOffset(), &resourcesSetDesc);
ANGLE_TRY(renderer->getDescriptorSetLayout(
contextVk, resourcesSetDesc, &mDescriptorSetLayouts[kShaderResourceDescriptorSetIndex]));
// Textures:
vk::DescriptorSetLayoutDesc texturesSetDesc;
AddTextureDescriptorSetDesc(mState, contextVk->useOldRewriteStructSamplers(), &texturesSetDesc);
ANGLE_TRY(renderer->getDescriptorSetLayout(contextVk, texturesSetDesc,
&mDescriptorSetLayouts[kTextureDescriptorSetIndex]));
// Driver uniforms:
VkShaderStageFlags driverUniformsStages =
glExecutable.isCompute() ? VK_SHADER_STAGE_COMPUTE_BIT : VK_SHADER_STAGE_ALL_GRAPHICS;
vk::DescriptorSetLayoutDesc driverUniformsSetDesc =
contextVk->getDriverUniformsDescriptorSetDesc(driverUniformsStages);
ANGLE_TRY(renderer->getDescriptorSetLayout(
contextVk, driverUniformsSetDesc,
&mDescriptorSetLayouts[kDriverUniformsDescriptorSetIndex]));
// Create pipeline layout with these 4 descriptor sets.
vk::PipelineLayoutDesc pipelineLayoutDesc;
pipelineLayoutDesc.updateDescriptorSetLayout(kUniformsAndXfbDescriptorSetIndex,
uniformsAndXfbSetDesc);
pipelineLayoutDesc.updateDescriptorSetLayout(kShaderResourceDescriptorSetIndex,
resourcesSetDesc);
pipelineLayoutDesc.updateDescriptorSetLayout(kTextureDescriptorSetIndex, texturesSetDesc);
pipelineLayoutDesc.updateDescriptorSetLayout(kDriverUniformsDescriptorSetIndex,
driverUniformsSetDesc);
ANGLE_TRY(renderer->getPipelineLayout(contextVk, pipelineLayoutDesc, mDescriptorSetLayouts,
&mPipelineLayout));
// Initialize descriptor pools.
std::array<VkDescriptorPoolSize, 2> uniformAndXfbSetSize = {
{{VK_DESCRIPTOR_TYPE_UNIFORM_BUFFER_DYNAMIC,
static_cast<uint32_t>(glExecutable.getLinkedShaderStageCount())},
{VK_DESCRIPTOR_TYPE_STORAGE_BUFFER, gl::IMPLEMENTATION_MAX_TRANSFORM_FEEDBACK_BUFFERS}}};
uint32_t uniformBlockCount = static_cast<uint32_t>(mState.getUniformBlocks().size());
uint32_t storageBlockCount = static_cast<uint32_t>(mState.getShaderStorageBlocks().size());
uint32_t atomicCounterBufferCount =
static_cast<uint32_t>(mState.getAtomicCounterBuffers().size());
uint32_t imageCount = static_cast<uint32_t>(mState.getImageBindings().size());
uint32_t textureCount = static_cast<uint32_t>(mState.getSamplerBindings().size());
if (renderer->getFeatures().bindEmptyForUnusedDescriptorSets.enabled)
{
// For this workaround, we have to create an empty descriptor set for each descriptor set
// index, so make sure their pools are initialized.
uniformBlockCount = std::max(uniformBlockCount, 1u);
textureCount = std::max(textureCount, 1u);
}
constexpr size_t kResourceTypesInResourcesSet = 3;
angle::FixedVector<VkDescriptorPoolSize, kResourceTypesInResourcesSet> resourceSetSize;
if (uniformBlockCount > 0)
{
resourceSetSize.emplace_back(VK_DESCRIPTOR_TYPE_UNIFORM_BUFFER, uniformBlockCount);
}
if (storageBlockCount > 0 || atomicCounterBufferCount > 0)
{
// Note that we always use an array of IMPLEMENTATION_MAX_ATOMIC_COUNTER_BUFFERS storage
// buffers for emulating atomic counters, so if there are any atomic counter buffers, we
// need to allocate IMPLEMENTATION_MAX_ATOMIC_COUNTER_BUFFERS descriptors.
const uint32_t atomicCounterStorageBufferCount =
atomicCounterBufferCount > 0 ? gl::IMPLEMENTATION_MAX_ATOMIC_COUNTER_BUFFERS : 0;
const uint32_t storageBufferDescCount = storageBlockCount + atomicCounterStorageBufferCount;
resourceSetSize.emplace_back(VK_DESCRIPTOR_TYPE_STORAGE_BUFFER, storageBufferDescCount);
}
if (imageCount > 0)
{
resourceSetSize.emplace_back(VK_DESCRIPTOR_TYPE_STORAGE_IMAGE, imageCount);
}
VkDescriptorPoolSize textureSetSize = {VK_DESCRIPTOR_TYPE_COMBINED_IMAGE_SAMPLER, textureCount};
ANGLE_TRY(mDynamicDescriptorPools[kUniformsAndXfbDescriptorSetIndex].init(
contextVk, uniformAndXfbSetSize.data(), uniformAndXfbSetSize.size()));
if (resourceSetSize.size() > 0)
{
ANGLE_TRY(mDynamicDescriptorPools[kShaderResourceDescriptorSetIndex].init(
contextVk, resourceSetSize.data(), static_cast<uint32_t>(resourceSetSize.size())));
}
if (textureCount > 0)
{
ANGLE_TRY(mDynamicDescriptorPools[kTextureDescriptorSetIndex].init(contextVk,
&textureSetSize, 1));
}
mDynamicBufferOffsets.resize(glExecutable.getLinkedShaderStageCount());
// Initialize an "empty" buffer for use with default uniform blocks where there are no uniforms,
// or atomic counter buffer array indices that are unused.
constexpr VkBufferUsageFlags kEmptyBufferUsage =
VK_BUFFER_USAGE_UNIFORM_BUFFER_BIT | VK_BUFFER_USAGE_STORAGE_BUFFER_BIT;
VkBufferCreateInfo emptyBufferInfo = {};
emptyBufferInfo.sType = VK_STRUCTURE_TYPE_BUFFER_CREATE_INFO;
emptyBufferInfo.flags = 0;
emptyBufferInfo.size = 4;
emptyBufferInfo.usage = kEmptyBufferUsage;
emptyBufferInfo.sharingMode = VK_SHARING_MODE_EXCLUSIVE;
emptyBufferInfo.queueFamilyIndexCount = 0;
emptyBufferInfo.pQueueFamilyIndices = nullptr;
constexpr VkMemoryPropertyFlags kMemoryType = VK_MEMORY_PROPERTY_DEVICE_LOCAL_BIT;
return mEmptyBuffer.init(contextVk, emptyBufferInfo, kMemoryType);
}
void ProgramVk::updateBindingOffsets()
{
mStorageBlockBindingsOffset = static_cast<uint32_t>(mState.getUniqueUniformBlockCount());
mAtomicCounterBufferBindingsOffset =
static_cast<uint32_t>(mStorageBlockBindingsOffset + mState.getUniqueStorageBlockCount());
uint32_t atomicCounterBindingCount = mState.getAtomicCounterBuffers().empty() ? 0 : 1;
mImageBindingsOffset = mAtomicCounterBufferBindingsOffset + atomicCounterBindingCount;
status = mExecutable.createPipelineLayout(context, glExecutable, mState);
return std::make_unique<LinkEventDone>(status);
}
void ProgramVk::linkResources(const gl::ProgramLinkedResources &resources)
......@@ -1191,34 +796,6 @@ void ProgramVk::setUniformMatrix4x3fv(GLint location,
setUniformMatrixfv<4, 3>(location, count, transpose, value);
}
angle::Result ProgramVk::allocateDescriptorSet(ContextVk *contextVk, uint32_t descriptorSetIndex)
{
bool ignoreNewPoolAllocated;
return allocateDescriptorSetAndGetInfo(contextVk, descriptorSetIndex, &ignoreNewPoolAllocated);
}
angle::Result ProgramVk::allocateDescriptorSetAndGetInfo(ContextVk *contextVk,
uint32_t descriptorSetIndex,
bool *newPoolAllocatedOut)
{
vk::DynamicDescriptorPool &dynamicDescriptorPool = mDynamicDescriptorPools[descriptorSetIndex];
uint32_t potentialNewCount = descriptorSetIndex + 1;
if (potentialNewCount > mDescriptorSets.size())
{
mDescriptorSets.resize(potentialNewCount, VK_NULL_HANDLE);
}
const vk::DescriptorSetLayout &descriptorSetLayout =
mDescriptorSetLayouts[descriptorSetIndex].get();
ANGLE_TRY(dynamicDescriptorPool.allocateSetsAndGetInfo(
contextVk, descriptorSetLayout.ptr(), 1, &mDescriptorPoolBindings[descriptorSetIndex],
&mDescriptorSets[descriptorSetIndex], newPoolAllocatedOut));
mEmptyDescriptorSets[descriptorSetIndex] = VK_NULL_HANDLE;
return angle::Result::Continue;
}
void ProgramVk::getUniformfv(const gl::Context *context, GLint location, GLfloat *params) const
{
getUniformImpl(location, params, GL_FLOAT);
......@@ -1250,9 +827,9 @@ angle::Result ProgramVk::updateUniforms(ContextVk *contextVk)
if (mDefaultUniformBlocksDirty[shaderType])
{
bool bufferModified = false;
ANGLE_TRY(
SyncDefaultUniformBlock(contextVk, &uniformBlock.storage, uniformBlock.uniformData,
&mDynamicBufferOffsets[offsetIndex], &bufferModified));
ANGLE_TRY(SyncDefaultUniformBlock(
contextVk, &uniformBlock.storage, uniformBlock.uniformData,
&mExecutable.mDynamicBufferOffsets[offsetIndex], &bufferModified));
mDefaultUniformBlocksDirty.reset(shaderType);
if (bufferModified)
......@@ -1268,497 +845,14 @@ angle::Result ProgramVk::updateUniforms(ContextVk *contextVk)
{
// We need to reinitialize the descriptor sets if we newly allocated buffers since we can't
// modify the descriptor sets once initialized.
ANGLE_TRY(allocateDescriptorSet(contextVk, kUniformsAndXfbDescriptorSetIndex));
updateDefaultUniformsDescriptorSet(contextVk);
updateTransformFeedbackDescriptorSetImpl(contextVk);
ANGLE_TRY(mExecutable.allocateDescriptorSet(contextVk, kUniformsAndXfbDescriptorSetIndex));
mExecutable.updateDefaultUniformsDescriptorSet(mState, mDefaultUniformBlocks, contextVk);
mExecutable.updateTransformFeedbackDescriptorSetImpl(mState, contextVk);
}
return angle::Result::Continue;
}
void ProgramVk::updateDefaultUniformsDescriptorSet(ContextVk *contextVk)
{
const gl::ProgramExecutable &glExecutable = mState.getProgramExecutable();
uint32_t shaderStageCount = static_cast<uint32_t>(glExecutable.getLinkedShaderStageCount());
gl::ShaderVector<VkDescriptorBufferInfo> descriptorBufferInfo(shaderStageCount);
gl::ShaderVector<VkWriteDescriptorSet> writeDescriptorInfo(shaderStageCount);
uint32_t bindingIndex = 0;
mDescriptorBuffersCache.clear();
// Write default uniforms for each shader type.
for (const gl::ShaderType shaderType : glExecutable.getLinkedShaderStages())
{
DefaultUniformBlock &uniformBlock = mDefaultUniformBlocks[shaderType];
VkDescriptorBufferInfo &bufferInfo = descriptorBufferInfo[bindingIndex];
VkWriteDescriptorSet &writeInfo = writeDescriptorInfo[bindingIndex];
if (!uniformBlock.uniformData.empty())
{
vk::BufferHelper *bufferHelper = uniformBlock.storage.getCurrentBuffer();
bufferInfo.buffer = bufferHelper->getBuffer().getHandle();
mDescriptorBuffersCache.emplace_back(bufferHelper);
}
else
{
mEmptyBuffer.retain(&contextVk->getResourceUseList());
bufferInfo.buffer = mEmptyBuffer.getBuffer().getHandle();
mDescriptorBuffersCache.emplace_back(&mEmptyBuffer);
}
bufferInfo.offset = 0;
bufferInfo.range = VK_WHOLE_SIZE;
writeInfo.sType = VK_STRUCTURE_TYPE_WRITE_DESCRIPTOR_SET;
writeInfo.pNext = nullptr;
writeInfo.dstSet = mDescriptorSets[kUniformsAndXfbDescriptorSetIndex];
writeInfo.dstBinding = bindingIndex;
writeInfo.dstArrayElement = 0;
writeInfo.descriptorCount = 1;
writeInfo.descriptorType = VK_DESCRIPTOR_TYPE_UNIFORM_BUFFER_DYNAMIC;
writeInfo.pImageInfo = nullptr;
writeInfo.pBufferInfo = &bufferInfo;
writeInfo.pTexelBufferView = nullptr;
++bindingIndex;
}
VkDevice device = contextVk->getDevice();
ASSERT(bindingIndex == shaderStageCount);
ASSERT(shaderStageCount <= kReservedDefaultUniformBindingCount);
vkUpdateDescriptorSets(device, shaderStageCount, writeDescriptorInfo.data(), 0, nullptr);
}
void ProgramVk::updateBuffersDescriptorSet(ContextVk *contextVk,
vk::ResourceUseList *resourceUseList,
CommandBufferHelper *commandBufferHelper,
const std::vector<gl::InterfaceBlock> &blocks,
VkDescriptorType descriptorType)
{
if (blocks.empty())
{
return;
}
VkDescriptorSet descriptorSet = mDescriptorSets[kShaderResourceDescriptorSetIndex];
ASSERT(descriptorType == VK_DESCRIPTOR_TYPE_UNIFORM_BUFFER ||
descriptorType == VK_DESCRIPTOR_TYPE_STORAGE_BUFFER);
const bool isStorageBuffer = descriptorType == VK_DESCRIPTOR_TYPE_STORAGE_BUFFER;
const uint32_t bindingStart =
isStorageBuffer ? getStorageBlockBindingsOffset() : getUniformBlockBindingsOffset();
static_assert(
gl::IMPLEMENTATION_MAX_SHADER_STORAGE_BUFFER_BINDINGS >=
gl::IMPLEMENTATION_MAX_UNIFORM_BUFFER_BINDINGS,
"The descriptor arrays here would have inadequate size for uniform buffer objects");
gl::StorageBuffersArray<VkDescriptorBufferInfo> descriptorBufferInfo;
gl::StorageBuffersArray<VkWriteDescriptorSet> writeDescriptorInfo;
uint32_t writeCount = 0;
// The binding is incremented every time arrayElement 0 is encountered, which means there will
// be an increment right at the start. Start from -1 to get 0 as the first binding.
int32_t currentBinding = -1;
// Write uniform or storage buffers.
const gl::State &glState = contextVk->getState();
for (uint32_t bufferIndex = 0; bufferIndex < blocks.size(); ++bufferIndex)
{
const gl::InterfaceBlock &block = blocks[bufferIndex];
const gl::OffsetBindingPointer<gl::Buffer> &bufferBinding =
isStorageBuffer ? glState.getIndexedShaderStorageBuffer(block.binding)
: glState.getIndexedUniformBuffer(block.binding);
if (!block.isArray || block.arrayElement == 0)
{
// Array indices of the same buffer binding are placed sequentially in `blocks`.
// Thus, the block binding is updated only when array index 0 is encountered.
++currentBinding;
}
if (bufferBinding.get() == nullptr)
{
continue;
}
uint32_t binding = bindingStart + currentBinding;
uint32_t arrayElement = block.isArray ? block.arrayElement : 0;
VkDeviceSize maxBlockSize = isStorageBuffer ? 0 : block.dataSize;
VkDescriptorBufferInfo &bufferInfo = descriptorBufferInfo[writeCount];
VkWriteDescriptorSet &writeInfo = writeDescriptorInfo[writeCount];
WriteBufferDescriptorSetBinding(bufferBinding, maxBlockSize, descriptorSet, descriptorType,
binding, arrayElement, 0, &bufferInfo, &writeInfo);
BufferVk *bufferVk = vk::GetImpl(bufferBinding.get());
vk::BufferHelper &bufferHelper = bufferVk->getBuffer();
if (isStorageBuffer)
{
// We set the SHADER_READ_BIT to be conservative.
VkAccessFlags accessFlags = VK_ACCESS_SHADER_READ_BIT | VK_ACCESS_SHADER_WRITE_BIT;
commandBufferHelper->bufferWrite(resourceUseList, accessFlags, &bufferHelper);
}
else
{
commandBufferHelper->bufferRead(resourceUseList, VK_ACCESS_UNIFORM_READ_BIT,
&bufferHelper);
}
++writeCount;
}
VkDevice device = contextVk->getDevice();
vkUpdateDescriptorSets(device, writeCount, writeDescriptorInfo.data(), 0, nullptr);
}
void ProgramVk::updateAtomicCounterBuffersDescriptorSet(ContextVk *contextVk,
vk::ResourceUseList *resourceUseList,
CommandBufferHelper *commandBufferHelper)
{
const gl::State &glState = contextVk->getState();
const std::vector<gl::AtomicCounterBuffer> &atomicCounterBuffers =
mState.getAtomicCounterBuffers();
if (atomicCounterBuffers.empty())
{
return;
}
VkDescriptorSet descriptorSet = mDescriptorSets[kShaderResourceDescriptorSetIndex];
const uint32_t bindingStart = getAtomicCounterBufferBindingsOffset();
gl::AtomicCounterBuffersArray<VkDescriptorBufferInfo> descriptorBufferInfo;
gl::AtomicCounterBuffersArray<VkWriteDescriptorSet> writeDescriptorInfo;
gl::AtomicCounterBufferMask writtenBindings;
RendererVk *rendererVk = contextVk->getRenderer();
const VkDeviceSize requiredOffsetAlignment =
rendererVk->getPhysicalDeviceProperties().limits.minStorageBufferOffsetAlignment;
// Write atomic counter buffers.
for (uint32_t bufferIndex = 0; bufferIndex < atomicCounterBuffers.size(); ++bufferIndex)
{
const gl::AtomicCounterBuffer &atomicCounterBuffer = atomicCounterBuffers[bufferIndex];
uint32_t binding = atomicCounterBuffer.binding;
const gl::OffsetBindingPointer<gl::Buffer> &bufferBinding =
glState.getIndexedAtomicCounterBuffer(binding);
if (bufferBinding.get() == nullptr)
{
continue;
}
VkDescriptorBufferInfo &bufferInfo = descriptorBufferInfo[binding];
VkWriteDescriptorSet &writeInfo = writeDescriptorInfo[binding];
WriteBufferDescriptorSetBinding(bufferBinding, 0, descriptorSet,
VK_DESCRIPTOR_TYPE_STORAGE_BUFFER, bindingStart, binding,
requiredOffsetAlignment, &bufferInfo, &writeInfo);
BufferVk *bufferVk = vk::GetImpl(bufferBinding.get());
vk::BufferHelper &bufferHelper = bufferVk->getBuffer();
// We set SHADER_READ_BIT to be conservative.
commandBufferHelper->bufferWrite(
resourceUseList, VK_ACCESS_SHADER_READ_BIT | VK_ACCESS_SHADER_WRITE_BIT, &bufferHelper);
writtenBindings.set(binding);
}
// Bind the empty buffer to every array slot that's unused.
mEmptyBuffer.retain(&contextVk->getResourceUseList());
for (size_t binding : ~writtenBindings)
{
VkDescriptorBufferInfo &bufferInfo = descriptorBufferInfo[binding];
VkWriteDescriptorSet &writeInfo = writeDescriptorInfo[binding];
bufferInfo.buffer = mEmptyBuffer.getBuffer().getHandle();
bufferInfo.offset = 0;
bufferInfo.range = VK_WHOLE_SIZE;
writeInfo.sType = VK_STRUCTURE_TYPE_WRITE_DESCRIPTOR_SET;
writeInfo.pNext = nullptr;
writeInfo.dstSet = descriptorSet;
writeInfo.dstBinding = bindingStart;
writeInfo.dstArrayElement = static_cast<uint32_t>(binding);
writeInfo.descriptorCount = 1;
writeInfo.descriptorType = VK_DESCRIPTOR_TYPE_STORAGE_BUFFER;
writeInfo.pImageInfo = nullptr;
writeInfo.pBufferInfo = &bufferInfo;
writeInfo.pTexelBufferView = nullptr;
}
VkDevice device = contextVk->getDevice();
vkUpdateDescriptorSets(device, gl::IMPLEMENTATION_MAX_ATOMIC_COUNTER_BUFFERS,
writeDescriptorInfo.data(), 0, nullptr);
}
angle::Result ProgramVk::updateImagesDescriptorSet(ContextVk *contextVk)
{
const gl::State &glState = contextVk->getState();
const std::vector<gl::ImageBinding> &imageBindings = mState.getImageBindings();
if (imageBindings.empty())
{
return angle::Result::Continue;
}
VkDescriptorSet descriptorSet = mDescriptorSets[kShaderResourceDescriptorSetIndex];
const gl::ActiveTextureArray<TextureVk *> &activeImages = contextVk->getActiveImages();
const uint32_t bindingStart = getImageBindingsOffset();
gl::ImagesArray<VkDescriptorImageInfo> descriptorImageInfo;
gl::ImagesArray<VkWriteDescriptorSet> writeDescriptorInfo;
uint32_t writeCount = 0;
// Write images.
for (uint32_t imageIndex = 0; imageIndex < imageBindings.size(); ++imageIndex)
{
const gl::ImageBinding &imageBinding = imageBindings[imageIndex];
ASSERT(!imageBinding.unreferenced);
for (uint32_t arrayElement = 0; arrayElement < imageBinding.boundImageUnits.size();
++arrayElement)
{
GLuint imageUnit = imageBinding.boundImageUnits[arrayElement];
const gl::ImageUnit &binding = glState.getImageUnit(imageUnit);
TextureVk *textureVk = activeImages[imageUnit];
vk::ImageHelper *image = &textureVk->getImage();
const vk::ImageView *imageView = nullptr;
ANGLE_TRY(textureVk->getStorageImageView(contextVk, (binding.layered == GL_TRUE),
binding.level, binding.layer, &imageView));
// Note: binding.access is unused because it is implied by the shader.
// TODO(syoussefi): Support image data reinterpretation by using binding.format.
// http://anglebug.com/3563
VkDescriptorImageInfo &imageInfo = descriptorImageInfo[writeCount];
VkWriteDescriptorSet &writeInfo = writeDescriptorInfo[writeCount];
imageInfo.sampler = VK_NULL_HANDLE;
imageInfo.imageView = imageView->getHandle();
imageInfo.imageLayout = image->getCurrentLayout();
writeInfo.sType = VK_STRUCTURE_TYPE_WRITE_DESCRIPTOR_SET;
writeInfo.pNext = nullptr;
writeInfo.dstSet = descriptorSet;
writeInfo.dstBinding = bindingStart + imageIndex;
writeInfo.dstArrayElement = arrayElement;
writeInfo.descriptorCount = 1;
writeInfo.descriptorType = VK_DESCRIPTOR_TYPE_STORAGE_IMAGE;
writeInfo.pImageInfo = &imageInfo;
writeInfo.pBufferInfo = nullptr;
writeInfo.pTexelBufferView = nullptr;
++writeCount;
}
}
VkDevice device = contextVk->getDevice();
vkUpdateDescriptorSets(device, writeCount, writeDescriptorInfo.data(), 0, nullptr);
return angle::Result::Continue;
}
angle::Result ProgramVk::updateShaderResourcesDescriptorSet(
ContextVk *contextVk,
vk::ResourceUseList *resourceUseList,
CommandBufferHelper *commandBufferHelper)
{
ANGLE_TRY(allocateDescriptorSet(contextVk, kShaderResourceDescriptorSetIndex));
updateBuffersDescriptorSet(contextVk, resourceUseList, commandBufferHelper,
mState.getUniformBlocks(), VK_DESCRIPTOR_TYPE_UNIFORM_BUFFER);
updateBuffersDescriptorSet(contextVk, resourceUseList, commandBufferHelper,
mState.getShaderStorageBlocks(), VK_DESCRIPTOR_TYPE_STORAGE_BUFFER);
updateAtomicCounterBuffersDescriptorSet(contextVk, resourceUseList, commandBufferHelper);
return updateImagesDescriptorSet(contextVk);
}
angle::Result ProgramVk::updateTransformFeedbackDescriptorSet(ContextVk *contextVk)
{
ASSERT(hasTransformFeedbackOutput());
ANGLE_TRY(allocateDescriptorSet(contextVk, kUniformsAndXfbDescriptorSetIndex));
updateDefaultUniformsDescriptorSet(contextVk);
updateTransformFeedbackDescriptorSetImpl(contextVk);
return angle::Result::Continue;
}
void ProgramVk::updateTransformFeedbackDescriptorSetImpl(ContextVk *contextVk)
{
const gl::State &glState = contextVk->getState();
gl::TransformFeedback *transformFeedback = glState.getCurrentTransformFeedback();
if (!hasTransformFeedbackOutput())
{
// If xfb has no output there is no need to update descriptor set.
return;
}
if (!glState.isTransformFeedbackActive())
{
// We set empty Buffer to xfb descriptor set because xfb descriptor set
// requires valid buffer bindings, even if they are empty buffer,
// otherwise Vulkan validation layer generates errors.
if (transformFeedback)
{
TransformFeedbackVk *transformFeedbackVk = vk::GetImpl(transformFeedback);
transformFeedbackVk->initDescriptorSet(
contextVk, mState.getTransformFeedbackBufferCount(), &mEmptyBuffer,
mDescriptorSets[kUniformsAndXfbDescriptorSetIndex]);
}
return;
}
TransformFeedbackVk *transformFeedbackVk = vk::GetImpl(glState.getCurrentTransformFeedback());
transformFeedbackVk->updateDescriptorSet(contextVk, mState,
mDescriptorSets[kUniformsAndXfbDescriptorSetIndex]);
}
angle::Result ProgramVk::updateTexturesDescriptorSet(ContextVk *contextVk)
{
const vk::TextureDescriptorDesc &texturesDesc = contextVk->getActiveTexturesDesc();
auto iter = mTextureDescriptorsCache.find(texturesDesc);
if (iter != mTextureDescriptorsCache.end())
{
mDescriptorSets[kTextureDescriptorSetIndex] = iter->second;
return angle::Result::Continue;
}
ASSERT(hasTextures());
bool newPoolAllocated;
ANGLE_TRY(
allocateDescriptorSetAndGetInfo(contextVk, kTextureDescriptorSetIndex, &newPoolAllocated));
// Clear descriptor set cache. It may no longer be valid.
if (newPoolAllocated)
{
mTextureDescriptorsCache.clear();
}
VkDescriptorSet descriptorSet = mDescriptorSets[kTextureDescriptorSetIndex];
gl::ActiveTextureArray<VkDescriptorImageInfo> descriptorImageInfo;
gl::ActiveTextureArray<VkWriteDescriptorSet> writeDescriptorInfo;
uint32_t writeCount = 0;
const gl::ActiveTextureArray<vk::TextureUnit> &activeTextures = contextVk->getActiveTextures();
bool emulateSeamfulCubeMapSampling = contextVk->emulateSeamfulCubeMapSampling();
bool useOldRewriteStructSamplers = contextVk->useOldRewriteStructSamplers();
std::unordered_map<std::string, uint32_t> mappedSamplerNameToBindingIndex;
std::unordered_map<std::string, uint32_t> mappedSamplerNameToArrayOffset;
uint32_t currentBindingIndex = 0;
for (uint32_t textureIndex = 0; textureIndex < mState.getSamplerBindings().size();
++textureIndex)
{
const gl::SamplerBinding &samplerBinding = mState.getSamplerBindings()[textureIndex];
ASSERT(!samplerBinding.unreferenced);
uint32_t uniformIndex = mState.getUniformIndexFromSamplerIndex(textureIndex);
const gl::LinkedUniform &samplerUniform = mState.getUniforms()[uniformIndex];
std::string mappedSamplerName = GlslangGetMappedSamplerName(samplerUniform.name);
if (useOldRewriteStructSamplers ||
mappedSamplerNameToBindingIndex.emplace(mappedSamplerName, currentBindingIndex).second)
{
currentBindingIndex++;
}
uint32_t bindingIndex = textureIndex;
uint32_t arrayOffset = 0;
uint32_t arraySize = static_cast<uint32_t>(samplerBinding.boundTextureUnits.size());
if (!useOldRewriteStructSamplers)
{
bindingIndex = mappedSamplerNameToBindingIndex[mappedSamplerName];
arrayOffset = mappedSamplerNameToArrayOffset[mappedSamplerName];
// Front-end generates array elements in order, so we can just increment
// the offset each time we process a nested array.
mappedSamplerNameToArrayOffset[mappedSamplerName] += arraySize;
}
for (uint32_t arrayElement = 0; arrayElement < arraySize; ++arrayElement)
{
GLuint textureUnit = samplerBinding.boundTextureUnits[arrayElement];
TextureVk *textureVk = activeTextures[textureUnit].texture;
SamplerVk *samplerVk = activeTextures[textureUnit].sampler;
vk::ImageHelper &image = textureVk->getImage();
VkDescriptorImageInfo &imageInfo = descriptorImageInfo[writeCount];
// Use bound sampler object if one present, otherwise use texture's sampler
const vk::Sampler &sampler =
(samplerVk != nullptr) ? samplerVk->getSampler() : textureVk->getSampler();
imageInfo.sampler = sampler.getHandle();
imageInfo.imageLayout = image.getCurrentLayout();
if (emulateSeamfulCubeMapSampling)
{
// If emulating seamful cubemapping, use the fetch image view. This is basically
// the same image view as read, except it's a 2DArray view for cube maps.
imageInfo.imageView =
textureVk->getFetchImageViewAndRecordUse(contextVk).getHandle();
}
else
{
imageInfo.imageView =
textureVk->getReadImageViewAndRecordUse(contextVk).getHandle();
}
VkWriteDescriptorSet &writeInfo = writeDescriptorInfo[writeCount];
writeInfo.sType = VK_STRUCTURE_TYPE_WRITE_DESCRIPTOR_SET;
writeInfo.pNext = nullptr;
writeInfo.dstSet = descriptorSet;
writeInfo.dstBinding = bindingIndex;
writeInfo.dstArrayElement = arrayOffset + arrayElement;
writeInfo.descriptorCount = 1;
writeInfo.descriptorType = VK_DESCRIPTOR_TYPE_COMBINED_IMAGE_SAMPLER;
writeInfo.pImageInfo = &imageInfo;
writeInfo.pBufferInfo = nullptr;
writeInfo.pTexelBufferView = nullptr;
++writeCount;
}
}
VkDevice device = contextVk->getDevice();
ASSERT(writeCount > 0);
vkUpdateDescriptorSets(device, writeCount, writeDescriptorInfo.data(), 0, nullptr);
mTextureDescriptorsCache.emplace(texturesDesc, descriptorSet);
return angle::Result::Continue;
}
void ProgramVk::setDefaultUniformBlocksMinSizeForTesting(size_t minSize)
{
for (DefaultUniformBlock &block : mDefaultUniformBlocks)
......@@ -1767,76 +861,4 @@ void ProgramVk::setDefaultUniformBlocksMinSizeForTesting(size_t minSize)
}
}
angle::Result ProgramVk::updateDescriptorSets(ContextVk *contextVk,
vk::CommandBuffer *commandBuffer)
{
// Can probably use better dirty bits here.
if (mDescriptorSets.empty())
return angle::Result::Continue;
// Find the maximum non-null descriptor set. This is used in conjunction with a driver
// workaround to bind empty descriptor sets only for gaps in between 0 and max and avoid
// binding unnecessary empty descriptor sets for the sets beyond max.
const size_t descriptorSetStart = kUniformsAndXfbDescriptorSetIndex;
size_t descriptorSetRange = 0;
for (size_t descriptorSetIndex = descriptorSetStart;
descriptorSetIndex < mDescriptorSets.size(); ++descriptorSetIndex)
{
if (mDescriptorSets[descriptorSetIndex] != VK_NULL_HANDLE)
{
descriptorSetRange = descriptorSetIndex + 1;
}
}
const VkPipelineBindPoint pipelineBindPoint = mState.getProgramExecutable().isCompute()
? VK_PIPELINE_BIND_POINT_COMPUTE
: VK_PIPELINE_BIND_POINT_GRAPHICS;
for (uint32_t descriptorSetIndex = descriptorSetStart; descriptorSetIndex < descriptorSetRange;
++descriptorSetIndex)
{
VkDescriptorSet descSet = mDescriptorSets[descriptorSetIndex];
if (descSet == VK_NULL_HANDLE)
{
if (!contextVk->getRenderer()->getFeatures().bindEmptyForUnusedDescriptorSets.enabled)
{
continue;
}
// Workaround a driver bug where missing (though unused) descriptor sets indices cause
// later sets to misbehave.
if (mEmptyDescriptorSets[descriptorSetIndex] == VK_NULL_HANDLE)
{
const vk::DescriptorSetLayout &descriptorSetLayout =
mDescriptorSetLayouts[descriptorSetIndex].get();
ANGLE_TRY(mDynamicDescriptorPools[descriptorSetIndex].allocateSets(
contextVk, descriptorSetLayout.ptr(), 1,
&mDescriptorPoolBindings[descriptorSetIndex],
&mEmptyDescriptorSets[descriptorSetIndex]));
}
descSet = mEmptyDescriptorSets[descriptorSetIndex];
}
// Default uniforms are encompassed in a block per shader stage, and they are assigned
// through dynamic uniform buffers (requiring dynamic offsets). No other descriptor
// requires a dynamic offset.
const uint32_t uniformBlockOffsetCount =
descriptorSetIndex == kUniformsAndXfbDescriptorSetIndex
? static_cast<uint32_t>(mDynamicBufferOffsets.size())
: 0;
commandBuffer->bindDescriptorSets(mPipelineLayout.get(), pipelineBindPoint,
descriptorSetIndex, 1, &descSet, uniformBlockOffsetCount,
mDynamicBufferOffsets.data());
}
for (vk::BufferHelper *buffer : mDescriptorBuffersCache)
{
buffer->retain(&contextVk->getResourceUseList());
}
return angle::Result::Continue;
}
} // namespace rx
......@@ -16,6 +16,7 @@
#include "libANGLE/renderer/ProgramImpl.h"
#include "libANGLE/renderer/glslang_wrapper_utils.h"
#include "libANGLE/renderer/vulkan/ContextVk.h"
#include "libANGLE/renderer/vulkan/ProgramExecutableVk.h"
#include "libANGLE/renderer/vulkan/RendererVk.h"
#include "libANGLE/renderer/vulkan/TransformFeedbackVk.h"
#include "libANGLE/renderer/vulkan/vk_helpers.h"
......@@ -99,31 +100,14 @@ class ProgramVk : public ProgramImpl
void getUniformiv(const gl::Context *context, GLint location, GLint *params) const override;
void getUniformuiv(const gl::Context *context, GLint location, GLuint *params) const override;
// Also initializes the pipeline layout, descriptor set layouts, and used descriptor ranges.
angle::Result updateUniforms(ContextVk *contextVk);
angle::Result updateTexturesDescriptorSet(ContextVk *contextVk);
angle::Result updateShaderResourcesDescriptorSet(ContextVk *contextVk,
vk::ResourceUseList *resourceUseList,
CommandBufferHelper *commandBufferHelper);
angle::Result updateTransformFeedbackDescriptorSet(ContextVk *contextVk);
angle::Result updateDescriptorSets(ContextVk *contextVk, vk::CommandBuffer *commandBuffer);
// For testing only.
void setDefaultUniformBlocksMinSizeForTesting(size_t minSize);
const vk::PipelineLayout &getPipelineLayout() const { return mPipelineLayout.get(); }
bool hasDefaultUniforms() const { return !mState.getDefaultUniformRange().empty(); }
bool hasTextures() const { return !mState.getSamplerBindings().empty(); }
bool hasUniformBuffers() const { return !mState.getUniformBlocks().empty(); }
bool hasStorageBuffers() const { return !mState.getShaderStorageBlocks().empty(); }
bool hasAtomicCounterBuffers() const { return !mState.getAtomicCounterBuffers().empty(); }
bool hasImages() const { return !mState.getImageBindings().empty(); }
bool hasTransformFeedbackOutput() const
const vk::PipelineLayout &getPipelineLayout() const
{
return !mState.getLinkedTransformFeedbackVaryings().empty();
return mExecutable.mPipelineLayout.get();
}
bool dirtyUniforms() const { return mDefaultUniformBlocksDirty.any(); }
......@@ -143,8 +127,9 @@ class ProgramVk : public ProgramImpl
ANGLE_TRY(renderer->getPipelineCache(&pipelineCache));
return shaderProgram->getGraphicsPipeline(
contextVk, &contextVk->getRenderPassCache(), *pipelineCache,
contextVk->getCurrentQueueSerial(), mPipelineLayout.get(), desc, activeAttribLocations,
mState.getProgramExecutable().getAttributesTypeMask(), descPtrOut, pipelineOut);
contextVk->getCurrentQueueSerial(), mExecutable.mPipelineLayout.get(), desc,
activeAttribLocations, mState.getProgramExecutable().getAttributesTypeMask(),
descPtrOut, pipelineOut);
}
angle::Result getComputePipeline(ContextVk *contextVk, vk::PipelineAndSerial **pipelineOut)
......@@ -152,15 +137,21 @@ class ProgramVk : public ProgramImpl
vk::ShaderProgramHelper *shaderProgram;
ANGLE_TRY(initComputeProgram(contextVk, &shaderProgram));
ASSERT(!shaderProgram->isGraphicsProgram());
return shaderProgram->getComputePipeline(contextVk, mPipelineLayout.get(), pipelineOut);
return shaderProgram->getComputePipeline(contextVk, mExecutable.mPipelineLayout.get(),
pipelineOut);
}
// Used in testing only.
vk::DynamicDescriptorPool *getDynamicDescriptorPool(uint32_t poolIndex)
{
return &mDynamicDescriptorPools[poolIndex];
return &mExecutable.mDynamicDescriptorPools[poolIndex];
}
const ProgramExecutableVk &getExecutable() const { return mExecutable; }
ProgramExecutableVk &getExecutable() { return mExecutable; }
gl::ShaderMap<DefaultUniformBlock> &getDefaultUniformBlocks() { return mDefaultUniformBlocks; }
private:
template <int cols, int rows>
void setUniformMatrixfv(GLint location,
......@@ -169,10 +160,6 @@ class ProgramVk : public ProgramImpl
const GLfloat *value);
void reset(ContextVk *contextVk);
angle::Result allocateDescriptorSet(ContextVk *contextVk, uint32_t descriptorSetIndex);
angle::Result allocateDescriptorSetAndGetInfo(ContextVk *contextVk,
uint32_t descriptorSetIndex,
bool *newPoolAllocatedOut);
angle::Result initDefaultUniformBlocks(const gl::Context *glContext);
void generateUniformLayoutMapping(gl::ShaderMap<sh::BlockLayoutMap> &layoutMap,
gl::ShaderMap<size_t> &requiredBufferSize);
......@@ -180,18 +167,6 @@ class ProgramVk : public ProgramImpl
angle::Result resizeUniformBlockMemory(ContextVk *contextVk,
gl::ShaderMap<size_t> &requiredBufferSize);
void updateDefaultUniformsDescriptorSet(ContextVk *contextVk);
void updateTransformFeedbackDescriptorSetImpl(ContextVk *contextVk);
void updateBuffersDescriptorSet(ContextVk *contextVk,
vk::ResourceUseList *resourceUseList,
CommandBufferHelper *commandBufferHelper,
const std::vector<gl::InterfaceBlock> &blocks,
VkDescriptorType descriptorType);
void updateAtomicCounterBuffersDescriptorSet(ContextVk *contextVk,
vk::ResourceUseList *resourceUseList,
CommandBufferHelper *commandBufferHelper);
angle::Result updateImagesDescriptorSet(ContextVk *contextVk);
template <class T>
void getUniformImpl(GLint location, T *v, GLenum entryPointType) const;
......@@ -200,15 +175,6 @@ class ProgramVk : public ProgramImpl
angle::Result linkImpl(const gl::Context *glContext, gl::InfoLog &infoLog);
void linkResources(const gl::ProgramLinkedResources &resources);
void updateBindingOffsets();
uint32_t getUniformBlockBindingsOffset() const { return 0; }
uint32_t getStorageBlockBindingsOffset() const { return mStorageBlockBindingsOffset; }
uint32_t getAtomicCounterBufferBindingsOffset() const
{
return mAtomicCounterBufferBindingsOffset;
}
uint32_t getImageBindingsOffset() const { return mImageBindingsOffset; }
class ProgramInfo;
ANGLE_INLINE angle::Result initProgram(ContextVk *contextVk,
bool enableLineRasterEmulation,
......@@ -247,50 +213,9 @@ class ProgramVk : public ProgramImpl
return initProgram(contextVk, false, &mDefaultProgramInfo, shaderProgramOut);
}
// State for the default uniform blocks.
struct DefaultUniformBlock final : private angle::NonCopyable
{
DefaultUniformBlock();
~DefaultUniformBlock();
vk::DynamicBuffer storage;
// Shadow copies of the shader uniform data.
angle::MemoryBuffer uniformData;
// Since the default blocks are laid out in std140, this tells us where to write on a call
// to a setUniform method. They are arranged in uniform location order.
std::vector<sh::BlockMemberInfo> uniformLayout;
};
gl::ShaderMap<DefaultUniformBlock> mDefaultUniformBlocks;
gl::ShaderBitSet mDefaultUniformBlocksDirty;
gl::ShaderVector<uint32_t> mDynamicBufferOffsets;
// This is a special "empty" placeholder buffer for when a shader has no uniforms or doesn't
// use all slots in the atomic counter buffer array.
//
// It is necessary because we want to keep a compatible pipeline layout in all cases,
// and Vulkan does not tolerate having null handles in a descriptor set.
vk::BufferHelper mEmptyBuffer;
// Descriptor sets for uniform blocks and textures for this program.
std::vector<VkDescriptorSet> mDescriptorSets;
vk::DescriptorSetLayoutArray<VkDescriptorSet> mEmptyDescriptorSets;
std::vector<vk::BufferHelper *> mDescriptorBuffersCache;
std::unordered_map<vk::TextureDescriptorDesc, VkDescriptorSet> mTextureDescriptorsCache;
// We keep a reference to the pipeline and descriptor set layouts. This ensures they don't get
// deleted while this program is in use.
vk::BindingPointer<vk::PipelineLayout> mPipelineLayout;
vk::DescriptorSetLayoutPointerArray mDescriptorSetLayouts;
// Keep bindings to the descriptor pools. This ensures the pools stay valid while the Program
// is in use.
vk::DescriptorSetLayoutArray<vk::RefCountedDescriptorPoolBinding> mDescriptorPoolBindings;
class ShaderInfo final : angle::NonCopyable
{
public:
......@@ -341,19 +266,9 @@ class ProgramVk : public ProgramImpl
// We keep the SPIR-V code to use for draw call pipeline creation.
ShaderInfo mShaderInfo;
// In their descriptor set, uniform buffers are placed first, then storage buffers, then atomic
// counter buffers and then images. These cached values contain the offsets where storage
// buffer, atomic counter buffer and image bindings start.
uint32_t mStorageBlockBindingsOffset;
uint32_t mAtomicCounterBufferBindingsOffset;
uint32_t mImageBindingsOffset;
// Store descriptor pools here. We store the descriptors in the Program to facilitate descriptor
// cache management. It can also allow fewer descriptors for shaders which use fewer
// textures/buffers.
vk::DescriptorSetLayoutArray<vk::DynamicDescriptorPool> mDynamicDescriptorPools;
GlslangProgramInterfaceInfo mGlslangProgramInterfaceInfo;
ProgramExecutableVk mExecutable;
};
} // namespace rx
......
Markdown is supported
0% or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment