Commit 377e7487 by Xinyi He Committed by Commit Bot

Vulkan: Support array of array image type

Implement supporting the array of array of image type in uniform. Add a new end2end test for it. Bug: angleproject:3881 Change-Id: Idd757ae1d0ed34d585ae1ca5e0b6577459a0acb7 Reviewed-on: https://chromium-review.googlesource.com/c/angle/angle/+/2379335 Commit-Queue: Shahbaz Youssefi <syoussefi@chromium.org> Reviewed-by: 's avatarJamie Madill <jmadill@chromium.org> Reviewed-by: 's avatarShahbaz Youssefi <syoussefi@chromium.org>
parent b3a8f0bc
......@@ -343,6 +343,7 @@ class TType
bool isSamplerCube() const { return type == EbtSamplerCube; }
bool isAtomicCounter() const { return IsAtomicCounter(type); }
bool isSamplerVideoWEBGL() const { return type == EbtSamplerVideoWEBGL; }
bool isImage() const { return IsImage(type); }
private:
constexpr void invalidateMangledName() { mMangledName = nullptr; }
......
......@@ -125,9 +125,9 @@ void TraverseArrayOfArraysVariable(const ShaderVariable &variable,
}
else
{
if (gl::IsSamplerType(variable.type))
if (gl::IsSamplerType(variable.type) || gl::IsImageType(variable.type))
{
visitor->visitSampler(elementVar);
visitor->visitSamplerOrImage(elementVar);
}
else
{
......@@ -455,24 +455,24 @@ std::string VariableNameVisitor::collapseMappedNameStack() const
return CollapseNameStack(mMappedNameStack);
}
void VariableNameVisitor::visitSampler(const sh::ShaderVariable &sampler)
void VariableNameVisitor::visitSamplerOrImage(const sh::ShaderVariable &variable)
{
if (!sampler.hasParentArrayIndex())
if (!variable.hasParentArrayIndex())
{
mNameStack.push_back(sampler.name);
mMappedNameStack.push_back(sampler.mappedName);
mNameStack.push_back(variable.name);
mMappedNameStack.push_back(variable.mappedName);
}
std::string name = collapseNameStack();
std::string mappedName = collapseMappedNameStack();
if (!sampler.hasParentArrayIndex())
if (!variable.hasParentArrayIndex())
{
mNameStack.pop_back();
mMappedNameStack.pop_back();
}
visitNamedSampler(sampler, name, mappedName, mArraySizeStack);
visitNamedSamplerOrImage(variable, name, mappedName, mArraySizeStack);
}
void VariableNameVisitor::visitVariable(const ShaderVariable &variable, bool isRowMajor)
......@@ -610,9 +610,9 @@ void TraverseShaderVariable(const ShaderVariable &variable,
{
TraverseArrayOfArraysVariable(variable, 0u, isRowMajor, visitor);
}
else if (gl::IsSamplerType(variable.type))
else if (gl::IsSamplerType(variable.type) || gl::IsImageType(variable.type))
{
visitor->visitSampler(variable);
visitor->visitSamplerOrImage(variable);
}
else
{
......
......@@ -201,7 +201,7 @@ class ShaderVariableVisitor
virtual void enterArrayElement(const ShaderVariable &arrayVar, unsigned int arrayElement) {}
virtual void exitArrayElement(const ShaderVariable &arrayVar, unsigned int arrayElement) {}
virtual void visitSampler(const sh::ShaderVariable &sampler) {}
virtual void visitSamplerOrImage(const sh::ShaderVariable &variable) {}
virtual void visitVariable(const ShaderVariable &variable, bool isRowMajor) = 0;
......@@ -225,10 +225,10 @@ class VariableNameVisitor : public ShaderVariableVisitor
void exitArrayElement(const ShaderVariable &arrayVar, unsigned int arrayElement) override;
protected:
virtual void visitNamedSampler(const sh::ShaderVariable &sampler,
const std::string &name,
const std::string &mappedName,
const std::vector<unsigned int> &arraySizes)
virtual void visitNamedSamplerOrImage(const sh::ShaderVariable &sampler,
const std::string &name,
const std::string &mappedName,
const std::vector<unsigned int> &arraySizes)
{}
virtual void visitNamedVariable(const ShaderVariable &variable,
bool isRowMajor,
......@@ -240,7 +240,7 @@ class VariableNameVisitor : public ShaderVariableVisitor
std::string collapseMappedNameStack() const;
private:
void visitSampler(const sh::ShaderVariable &sampler) final;
void visitSamplerOrImage(const sh::ShaderVariable &variable) final;
void visitVariable(const ShaderVariable &variable, bool isRowMajor) final;
std::vector<std::string> mNameStack;
......
......@@ -353,7 +353,7 @@ class Traverser final : public TIntermTraverser, public ArrayTraverser
mMultiReplacements.emplace_back(getParentNode()->getAsBlock(), decl, *newSequence);
}
if (type.isSampler() && type.isArray())
if ((type.isSampler() || type.isImage()) && type.isArray())
{
TIntermSequence *newSequence = new TIntermSequence;
TIntermSymbol *asSymbol = declarator->getAsSymbolNode();
......@@ -373,9 +373,10 @@ class Traverser final : public TIntermTraverser, public ArrayTraverser
{
if (visit != PreVisit)
return true;
// If the node isn't a sampler or if this isn't the outermost access,
// continue.
if (!node->getType().isSampler() || node->getType().isArray())
// If the node isn't a sampler and it isn't an image or if this isn't the outermost
// access, continue.
if ((!node->getType().isSampler() && !node->getType().isImage()) ||
node->getType().isArray())
{
return true;
}
......
......@@ -3705,6 +3705,9 @@ void Program::linkSamplerAndImageBindings(GLuint *combinedImageUniforms)
std::vector<ImageBinding> &imageBindings = hasComputeShader
? mState.mExecutable->mComputeImageBindings
: mState.mExecutable->mGraphicsImageBindings;
// The arrays of arrays are flattened to arrays, it needs to record the array offset for the
// correct binding image unit.
uint32_t arrayOffset = 0;
// If uniform is a image type, insert it into the mImageBindings array.
for (unsigned int imageIndex : mState.mExecutable->getImageUniformRange())
{
......@@ -3719,12 +3722,21 @@ void Program::linkSamplerAndImageBindings(GLuint *combinedImageUniforms)
}
else
{
imageBindings.emplace_back(
ImageBinding(imageUniform.binding, imageUniform.getBasicTypeElementCount(), false));
imageBindings.emplace_back(ImageBinding(imageUniform.binding + arrayOffset,
imageUniform.getBasicTypeElementCount(),
false));
}
GLuint arraySize = imageUniform.isArray() ? imageUniform.arraySizes[0] : 1u;
*combinedImageUniforms += imageUniform.activeShaderCount() * arraySize;
if (imageUniform.hasParentArrayIndex() && imageUniform.isArray())
{
arrayOffset += arraySize;
}
else
{
arrayOffset = 0;
}
}
highIter = lowIter;
......
......@@ -397,10 +397,10 @@ class FlattenUniformVisitor : public sh::VariableNameVisitor
mUnusedUniforms(unusedUniforms)
{}
void visitNamedSampler(const sh::ShaderVariable &sampler,
const std::string &name,
const std::string &mappedName,
const std::vector<unsigned int> &arraySizes) override
void visitNamedSamplerOrImage(const sh::ShaderVariable &sampler,
const std::string &name,
const std::string &mappedName,
const std::vector<unsigned int> &arraySizes) override
{
visitNamedVariable(sampler, false, name, mappedName, arraySizes);
}
......
......@@ -222,10 +222,10 @@ class UniformEncodingVisitorD3D : public sh::BlockEncoderVisitor
mUniformMapOut(uniformMapOut)
{}
void visitNamedSampler(const sh::ShaderVariable &sampler,
const std::string &name,
const std::string &mappedName,
const std::vector<unsigned int> &arraySizes) override
void visitNamedSamplerOrImage(const sh::ShaderVariable &sampler,
const std::string &name,
const std::string &mappedName,
const std::vector<unsigned int> &arraySizes) override
{
auto uniformMapEntry = mUniformMapOut->find(name);
if (uniformMapEntry == mUniformMapOut->end())
......
......@@ -450,6 +450,7 @@ void ProgramExecutableVk::addAtomicCounterBufferDescriptorSetDesc(
}
void ProgramExecutableVk::addImageDescriptorSetDesc(const gl::ProgramExecutable &executable,
bool useOldRewriteStructSamplers,
vk::DescriptorSetLayoutDesc *descOut)
{
const std::vector<gl::ImageBinding> &imageBindings = executable.getImageBindings();
......@@ -461,9 +462,29 @@ void ProgramExecutableVk::addImageDescriptorSetDesc(const gl::ProgramExecutable
uint32_t uniformIndex = executable.getUniformIndexFromImageIndex(imageIndex);
const gl::LinkedUniform &imageUniform = uniforms[uniformIndex];
std::string imageName = useOldRewriteStructSamplers
? GetMappedSamplerNameOld(imageUniform.name)
: GlslangGetMappedSamplerName(imageUniform.name);
// The front-end always binds array image units sequentially.
uint32_t arraySize = static_cast<uint32_t>(imageBinding.boundImageUnits.size());
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(imageUniform.name))
{
continue;
}
for (unsigned int outerArraySize : imageUniform.outerArraySizes)
{
arraySize *= outerArraySize;
}
}
for (const gl::ShaderType shaderType : executable.getLinkedShaderStages())
{
if (!imageUniform.isActive(shaderType))
......@@ -471,9 +492,8 @@ void ProgramExecutableVk::addImageDescriptorSetDesc(const gl::ProgramExecutable
continue;
}
std::string name = imageUniform.mappedName;
GetImageNameWithoutIndices(&name);
ShaderInterfaceVariableInfo &info = mVariableInfoMap[shaderType][name];
GetImageNameWithoutIndices(&imageName);
ShaderInterfaceVariableInfo &info = mVariableInfoMap[shaderType][imageName];
VkShaderStageFlags activeStages = gl_vk::kShaderStageMap[shaderType];
descOut->update(info.binding, VK_DESCRIPTOR_TYPE_STORAGE_IMAGE, arraySize, activeStages,
nullptr);
......@@ -736,7 +756,8 @@ angle::Result ProgramExecutableVk::updatePipelineLayout(
{
const gl::ProgramState *programState = programStates[shaderType];
ASSERT(programState);
addImageDescriptorSetDesc(programState->getExecutable(), &resourcesSetDesc);
addImageDescriptorSetDesc(programState->getExecutable(),
contextVk->useOldRewriteStructSamplers(), &resourcesSetDesc);
}
ANGLE_TRY(renderer->getDescriptorSetLayout(
......@@ -1146,6 +1167,9 @@ angle::Result ProgramExecutableVk::updateImagesDescriptorSet(
const gl::ActiveTextureArray<TextureVk *> &activeImages = contextVk->getActiveImages();
bool useOldRewriteStructSamplers = contextVk->useOldRewriteStructSamplers();
std::unordered_map<std::string, uint32_t> mappedImageNameToArrayOffset;
// Write images.
for (uint32_t imageIndex = 0; imageIndex < imageBindings.size(); ++imageIndex)
{
......@@ -1158,14 +1182,34 @@ angle::Result ProgramExecutableVk::updateImagesDescriptorSet(
continue;
}
std::string name = imageUniform.mappedName;
GetImageNameWithoutIndices(&name);
ShaderInterfaceVariableInfo &info = mVariableInfoMap[shaderType][name];
std::string mappedImageName;
if (!useOldRewriteStructSamplers)
{
mappedImageName = GlslangGetMappedSamplerName(imageUniform.mappedName);
}
else
{
mappedImageName = imageUniform.mappedName;
}
GetImageNameWithoutIndices(&mappedImageName);
ASSERT(!imageBinding.unreferenced);
for (uint32_t arrayElement = 0; arrayElement < imageBinding.boundImageUnits.size();
++arrayElement)
uint32_t arrayOffset = 0;
uint32_t arraySize = static_cast<uint32_t>(imageBinding.boundImageUnits.size());
if (!useOldRewriteStructSamplers)
{
arrayOffset = mappedImageNameToArrayOffset[mappedImageName];
// Front-end generates array elements in order, so we can just increment
// the offset each time we process a nested array.
mappedImageNameToArrayOffset[mappedImageName] += arraySize;
}
VkDescriptorImageInfo *imageInfos = contextVk->allocDescriptorImageInfos(arraySize);
VkWriteDescriptorSet *writeInfos = contextVk->allocWriteDescriptorSets(arraySize);
for (uint32_t arrayElement = 0; arrayElement < arraySize; ++arrayElement)
{
GLuint imageUnit = imageBinding.boundImageUnits[arrayElement];
const gl::ImageUnit &binding = glState.getImageUnit(imageUnit);
......@@ -1181,23 +1225,26 @@ angle::Result ProgramExecutableVk::updateImagesDescriptorSet(
// TODO(syoussefi): Support image data reinterpretation by using binding.format.
// http://anglebug.com/3563
VkDescriptorImageInfo &imageInfo = contextVk->allocDescriptorImageInfo();
VkWriteDescriptorSet &writeInfo = contextVk->allocWriteDescriptorSet();
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 = info.binding;
writeInfo.dstArrayElement = arrayElement;
writeInfo.descriptorCount = 1;
writeInfo.descriptorType = VK_DESCRIPTOR_TYPE_STORAGE_IMAGE;
writeInfo.pImageInfo = &imageInfo;
writeInfo.pBufferInfo = nullptr;
writeInfo.pTexelBufferView = nullptr;
imageInfos[arrayElement].sampler = VK_NULL_HANDLE;
imageInfos[arrayElement].imageView = imageView->getHandle();
imageInfos[arrayElement].imageLayout = image->getCurrentLayout();
ShaderInterfaceVariableInfoMap &variableInfoMap = mVariableInfoMap[shaderType];
const std::string imageName = useOldRewriteStructSamplers
? GetMappedSamplerNameOld(imageUniform.name)
: GlslangGetMappedSamplerName(imageUniform.name);
ShaderInterfaceVariableInfo &info = variableInfoMap[imageName];
writeInfos[arrayElement].sType = VK_STRUCTURE_TYPE_WRITE_DESCRIPTOR_SET;
writeInfos[arrayElement].pNext = nullptr;
writeInfos[arrayElement].dstSet = descriptorSet;
writeInfos[arrayElement].dstBinding = info.binding;
writeInfos[arrayElement].dstArrayElement = arrayOffset + arrayElement;
writeInfos[arrayElement].descriptorCount = 1;
writeInfos[arrayElement].descriptorType = VK_DESCRIPTOR_TYPE_STORAGE_IMAGE;
writeInfos[arrayElement].pImageInfo = &imageInfos[arrayElement];
writeInfos[arrayElement].pBufferInfo = nullptr;
writeInfos[arrayElement].pTexelBufferView = nullptr;
}
}
......
......@@ -191,6 +191,7 @@ class ProgramExecutableVk
const gl::ShaderType shaderType,
vk::DescriptorSetLayoutDesc *descOut);
void addImageDescriptorSetDesc(const gl::ProgramExecutable &executable,
bool useOldRewriteStructSamplers,
vk::DescriptorSetLayoutDesc *descOut);
void addTextureDescriptorSetDesc(const gl::ProgramState &programState,
bool useOldRewriteStructSamplers,
......
......@@ -163,9 +163,9 @@
// Not failing in last official run, but failed recently:
4110 SWIFTSHADER : dEQP-GLES31.functional.shaders.helper_invocation.* = FAIL
// Vulkan doesn't support array of array of opaque uniforms. Emulation is not yet done for images
3881 VULKAN : dEQP-GLES31.functional.program_interface_query.uniform.location.default_block.array.array.image_2d = SKIP
3881 VULKAN : dEQP-GLES31.functional.program_interface_query.uniform.location.default_block.array.array.iimage_2d_array = SKIP
// Vulkan Android doesn't support array of array of opaque uniforms. Emulation is not yet done for images
3881 VULKAN ANDROID : dEQP-GLES31.functional.program_interface_query.uniform.location.default_block.array.array.image_2d = SKIP
3881 VULKAN ANDROID : dEQP-GLES31.functional.program_interface_query.uniform.location.default_block.array.array.iimage_2d_array = SKIP
// Flaky crashes in Windows x86 Subzero Reactor
4482 WIN SWIFTSHADER : dEQP-GLES31.functional.synchronization.inter_invocation.* = SKIP
......
......@@ -3421,6 +3421,86 @@ TEST_P(GLSLTest_ES31, ArraysOfArraysSampler)
EXPECT_PIXEL_COLOR_EQ(0, 0, GLColor::green);
}
// Test that arrays of arrays of images work as expected.
TEST_P(GLSLTest_ES31, ArraysOfArraysImage)
{
// anglebug.com/2703 - QC doesn't support arrays of image as parameters,
// so image array of array handling is disabled
ANGLE_SKIP_TEST_IF(IsAndroid() && IsVulkan());
// Fails on D3D due to mistranslation.
ANGLE_SKIP_TEST_IF(IsD3D());
// Fails on Android on GLES.
ANGLE_SKIP_TEST_IF(IsAndroid() && IsOpenGLES());
GLint maxTextures, maxComputeImageUniforms;
glGetIntegerv(GL_MAX_TEXTURE_IMAGE_UNITS, &maxTextures);
glGetIntegerv(GL_MAX_COMPUTE_IMAGE_UNIFORMS, &maxComputeImageUniforms);
ANGLE_SKIP_TEST_IF(maxTextures < 1 * 2 * 3);
ANGLE_SKIP_TEST_IF(maxComputeImageUniforms < 1 * 2 * 3);
constexpr char kComputeShader[] = R"(#version 310 es
layout(local_size_x = 1, local_size_y = 1, local_size_z = 1) in;
layout(binding = 0, r32ui) uniform highp readonly uimage2D image[1][2][3];
layout(binding = 1, std430) buffer Output {
uint image_value;
} outbuf;
void main(void)
{
outbuf.image_value = uint(0.0);
outbuf.image_value += imageLoad(image[0][0][0], ivec2(0, 0)).x;
outbuf.image_value += imageLoad(image[0][0][1], ivec2(0, 0)).x;
outbuf.image_value += imageLoad(image[0][0][2], ivec2(0, 0)).x;
outbuf.image_value += imageLoad(image[0][1][0], ivec2(0, 0)).x;
outbuf.image_value += imageLoad(image[0][1][1], ivec2(0, 0)).x;
outbuf.image_value += imageLoad(image[0][1][2], ivec2(0, 0)).x;
})";
ANGLE_GL_COMPUTE_PROGRAM(program, kComputeShader);
EXPECT_GL_NO_ERROR();
glUseProgram(program);
GLuint outputInitData[1] = {10};
GLBuffer outputBuffer;
glBindBuffer(GL_SHADER_STORAGE_BUFFER, outputBuffer);
glBufferData(GL_SHADER_STORAGE_BUFFER, sizeof(outputInitData), outputInitData, GL_STATIC_DRAW);
glBindBufferBase(GL_SHADER_STORAGE_BUFFER, 1, outputBuffer);
EXPECT_GL_NO_ERROR();
GLuint imageData = 200u;
GLTexture images[1][2][3];
for (int i = 0; i < 1; i++)
{
for (int j = 0; j < 2; j++)
{
for (int k = 0; k < 3; k++)
{
glBindTexture(GL_TEXTURE_2D, images[i][j][k]);
glTexStorage2D(GL_TEXTURE_2D, 1, GL_R32UI, 1, 1);
glTexSubImage2D(GL_TEXTURE_2D, 0, 0, 0, 1, 1, GL_RED_INTEGER, GL_UNSIGNED_INT,
&imageData);
glBindImageTexture(i * 6 + j * 3 + k, images[i][j][k], 0, GL_FALSE, 0, GL_READ_ONLY,
GL_R32UI);
EXPECT_GL_NO_ERROR();
}
}
}
glDispatchCompute(1, 1, 1);
EXPECT_GL_NO_ERROR();
glMemoryBarrier(GL_BUFFER_UPDATE_BARRIER_BIT);
// read back
const GLuint *ptr = reinterpret_cast<const GLuint *>(
glMapBufferRange(GL_SHADER_STORAGE_BUFFER, 0, sizeof(outputInitData), GL_MAP_READ_BIT));
memcpy(outputInitData, ptr, sizeof(outputInitData));
EXPECT_EQ(outputInitData[0], imageData * 1 * 2 * 3);
glUnmapBuffer(GL_SHADER_STORAGE_BUFFER);
}
// Test that structs containing arrays of samplers work as expected.
TEST_P(GLSLTest_ES31, StructArraySampler)
{
......
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