Vulkan: Compute shader support
A DispatchHelper class is created as the equivalent of FramebufferHelper
as a command graph resource. There's currently a single dispatcher and
all dispatch calls are recorded on that. Context dirty bits are set up
in such a way that graphics and compute workloads are independently
handled, so that issuing a dispatch call wouldn't cause a framebuffer's
render pass to rebind resources.
Bug: angleproject:3562
Change-Id: Ib96db48297074d99b04324e44b067cfbfd43e333
Reviewed-on: https://chromium-review.googlesource.com/c/angle/angle/+/1688504
Commit-Queue: Shahbaz Youssefi <syoussefi@chromium.org>
Reviewed-by: Jamie Madill <jmadill@chromium.org>
diff --git a/src/compiler/translator/Compiler.cpp b/src/compiler/translator/Compiler.cpp
index 339ea1e..42a556f 100644
--- a/src/compiler/translator/Compiler.cpp
+++ b/src/compiler/translator/Compiler.cpp
@@ -1429,6 +1429,16 @@
return false;
}
+void EmitWorkGroupSizeGLSL(const TCompiler &compiler, TInfoSinkBase &sink)
+{
+ if (compiler.isComputeShaderLocalSizeDeclared())
+ {
+ const sh::WorkGroupSize &localSize = compiler.getComputeShaderLocalSize();
+ sink << "layout (local_size_x=" << localSize[0] << ", local_size_y=" << localSize[1]
+ << ", local_size_z=" << localSize[2] << ") in;\n";
+ }
+}
+
void EmitMultiviewGLSL(const TCompiler &compiler,
const ShCompileOptions &compileOptions,
const TBehavior behavior,
diff --git a/src/compiler/translator/Compiler.h b/src/compiler/translator/Compiler.h
index d831c4d..70c0285 100644
--- a/src/compiler/translator/Compiler.h
+++ b/src/compiler/translator/Compiler.h
@@ -296,6 +296,7 @@
TCompiler *ConstructCompiler(sh::GLenum type, ShShaderSpec spec, ShShaderOutput output);
void DeleteCompiler(TCompiler *);
+void EmitWorkGroupSizeGLSL(const TCompiler &, TInfoSinkBase &sink);
void EmitMultiviewGLSL(const TCompiler &, const ShCompileOptions &, TBehavior, TInfoSinkBase &sink);
} // namespace sh
diff --git a/src/compiler/translator/TranslatorESSL.cpp b/src/compiler/translator/TranslatorESSL.cpp
index 8a3efb5..9631a32 100644
--- a/src/compiler/translator/TranslatorESSL.cpp
+++ b/src/compiler/translator/TranslatorESSL.cpp
@@ -84,11 +84,9 @@
// Write array bounds clamping emulation if needed.
getArrayBoundsClamper().OutputClampingFunctionDefinition(sink);
- if (getShaderType() == GL_COMPUTE_SHADER && isComputeShaderLocalSizeDeclared())
+ if (getShaderType() == GL_COMPUTE_SHADER)
{
- const sh::WorkGroupSize &localSize = getComputeShaderLocalSize();
- sink << "layout (local_size_x=" << localSize[0] << ", local_size_y=" << localSize[1]
- << ", local_size_z=" << localSize[2] << ") in;\n";
+ EmitWorkGroupSizeGLSL(*this, sink);
}
if (getShaderType() == GL_GEOMETRY_SHADER_EXT)
diff --git a/src/compiler/translator/TranslatorGLSL.cpp b/src/compiler/translator/TranslatorGLSL.cpp
index d82b279..81b43b8 100644
--- a/src/compiler/translator/TranslatorGLSL.cpp
+++ b/src/compiler/translator/TranslatorGLSL.cpp
@@ -194,11 +194,9 @@
}
}
- if (getShaderType() == GL_COMPUTE_SHADER && isComputeShaderLocalSizeDeclared())
+ if (getShaderType() == GL_COMPUTE_SHADER)
{
- const sh::WorkGroupSize &localSize = getComputeShaderLocalSize();
- sink << "layout (local_size_x=" << localSize[0] << ", local_size_y=" << localSize[1]
- << ", local_size_z=" << localSize[2] << ") in;\n";
+ EmitWorkGroupSizeGLSL(*this, sink);
}
if (getShaderType() == GL_GEOMETRY_SHADER_EXT)
diff --git a/src/compiler/translator/TranslatorVulkan.cpp b/src/compiler/translator/TranslatorVulkan.cpp
index 6ce8568..2dd7c38 100644
--- a/src/compiler/translator/TranslatorVulkan.cpp
+++ b/src/compiler/translator/TranslatorVulkan.cpp
@@ -692,9 +692,12 @@
sink << "};\n";
}
- const TVariable *driverUniforms = AddDriverUniformsToShader(root, &getSymbolTable());
-
- ReplaceGLDepthRangeWithDriverUniform(root, driverUniforms, &getSymbolTable());
+ const TVariable *driverUniforms = nullptr;
+ if (getShaderType() != GL_COMPUTE_SHADER)
+ {
+ driverUniforms = AddDriverUniformsToShader(root, &getSymbolTable());
+ ReplaceGLDepthRangeWithDriverUniform(root, driverUniforms, &getSymbolTable());
+ }
// Declare gl_FragColor and glFragData as webgl_FragColor and webgl_FragData
// if it's core profile shaders and they are used.
@@ -775,10 +778,8 @@
RewriteDfdy(root, getSymbolTable(), getShaderVersion(), viewportYScale);
}
}
- else
+ else if (getShaderType() == GL_VERTEX_SHADER)
{
- ASSERT(getShaderType() == GL_VERTEX_SHADER);
-
AddANGLEPositionVarying(root, &getSymbolTable());
// Add a macro to declare transform feedback buffers.
@@ -790,6 +791,11 @@
// Append depth range translation to main.
AppendVertexShaderDepthCorrectionToMain(root, &getSymbolTable());
}
+ else
+ {
+ ASSERT(getShaderType() == GL_COMPUTE_SHADER);
+ EmitWorkGroupSizeGLSL(*this, sink);
+ }
// Write translated shader.
root->traverse(&outputGLSL);
diff --git a/src/libANGLE/Context.cpp b/src/libANGLE/Context.cpp
index c65eb12..499d1c0 100644
--- a/src/libANGLE/Context.cpp
+++ b/src/libANGLE/Context.cpp
@@ -3344,18 +3344,22 @@
// Limit textures as well, so we can use fast bitsets with texture bindings.
LimitCap(&mState.mCaps.maxCombinedTextureImageUnits, IMPLEMENTATION_MAX_ACTIVE_TEXTURES);
- LimitCap(&mState.mCaps.maxShaderTextureImageUnits[ShaderType::Vertex],
- IMPLEMENTATION_MAX_ACTIVE_TEXTURES / 2);
- LimitCap(&mState.mCaps.maxShaderTextureImageUnits[ShaderType::Fragment],
- IMPLEMENTATION_MAX_ACTIVE_TEXTURES / 2);
+ for (ShaderType shaderType : AllShaderTypes())
+ {
+ LimitCap(&mState.mCaps.maxShaderTextureImageUnits[shaderType],
+ IMPLEMENTATION_MAX_SHADER_TEXTURES);
+ }
LimitCap(&mState.mCaps.maxImageUnits, IMPLEMENTATION_MAX_IMAGE_UNITS);
LimitCap(&mState.mCaps.maxCombinedAtomicCounterBuffers,
IMPLEMENTATION_MAX_ATOMIC_COUNTER_BUFFERS);
- LimitCap(&mState.mCaps.maxShaderStorageBlocks[ShaderType::Compute],
- IMPLEMENTATION_MAX_SHADER_STORAGE_BUFFER_BINDINGS);
+ for (ShaderType shaderType : AllShaderTypes())
+ {
+ LimitCap(&mState.mCaps.maxShaderStorageBlocks[shaderType],
+ IMPLEMENTATION_MAX_SHADER_STORAGE_BUFFER_BINDINGS);
+ }
LimitCap(&mState.mCaps.maxShaderStorageBufferBindings,
IMPLEMENTATION_MAX_SHADER_STORAGE_BUFFER_BINDINGS);
LimitCap(&mState.mCaps.maxCombinedShaderStorageBlocks,
diff --git a/src/libANGLE/Program.h b/src/libANGLE/Program.h
index f246a55..f147b44 100644
--- a/src/libANGLE/Program.h
+++ b/src/libANGLE/Program.h
@@ -384,9 +384,10 @@
const ShaderBitSet &getLinkedShaderStages() const { return mLinkedShaderStages; }
bool hasLinkedShaderStage(ShaderType shaderType) const
{
- return mLinkedShaderStages.test(shaderType);
+ return mLinkedShaderStages[shaderType];
}
size_t getLinkedShaderStageCount() const { return mLinkedShaderStages.count(); }
+ bool isCompute() const { return hasLinkedShaderStage(ShaderType::Compute); }
bool hasAttachedShader() const;
@@ -588,8 +589,9 @@
bool hasLinkedShaderStage(ShaderType shaderType) const
{
ASSERT(shaderType != ShaderType::InvalidEnum);
- return mState.mLinkedShaderStages[shaderType];
+ return mState.hasLinkedShaderStage(shaderType);
}
+ bool isCompute() const { return mState.isCompute(); }
angle::Result loadBinary(const Context *context,
GLenum binaryFormat,
diff --git a/src/libANGLE/renderer/vulkan/BufferVk.cpp b/src/libANGLE/renderer/vulkan/BufferVk.cpp
index 7c76255..cc555a3 100644
--- a/src/libANGLE/renderer/vulkan/BufferVk.cpp
+++ b/src/libANGLE/renderer/vulkan/BufferVk.cpp
@@ -231,10 +231,16 @@
angle::Result BufferVk::unmap(const gl::Context *context, GLboolean *result)
{
- return unmapImpl(vk::GetImpl(context));
+ unmapImpl(vk::GetImpl(context));
+
+ // This should be false if the contents have been corrupted through external means. Vulkan
+ // doesn't provide such information.
+ *result = true;
+
+ return angle::Result::Continue;
}
-angle::Result BufferVk::unmapImpl(ContextVk *contextVk)
+void BufferVk::unmapImpl(ContextVk *contextVk)
{
ASSERT(mBuffer.valid());
@@ -242,8 +248,6 @@
mBuffer.onExternalWrite(VK_ACCESS_HOST_WRITE_BIT);
markConversionBuffersDirty();
-
- return angle::Result::Continue;
}
angle::Result BufferVk::getIndexRange(const gl::Context *context,
diff --git a/src/libANGLE/renderer/vulkan/BufferVk.h b/src/libANGLE/renderer/vulkan/BufferVk.h
index 3504c41..cd4a593 100644
--- a/src/libANGLE/renderer/vulkan/BufferVk.h
+++ b/src/libANGLE/renderer/vulkan/BufferVk.h
@@ -97,7 +97,7 @@
VkDeviceSize length,
GLbitfield access,
void **mapPtr);
- angle::Result unmapImpl(ContextVk *contextVk);
+ void unmapImpl(ContextVk *contextVk);
// Calls copyBuffer internally.
angle::Result copyToBuffer(ContextVk *contextVk,
diff --git a/src/libANGLE/renderer/vulkan/CommandGraph.cpp b/src/libANGLE/renderer/vulkan/CommandGraph.cpp
index 666a361..25da4e5 100644
--- a/src/libANGLE/renderer/vulkan/CommandGraph.cpp
+++ b/src/libANGLE/renderer/vulkan/CommandGraph.cpp
@@ -88,6 +88,8 @@
UNREACHABLE();
return "Query";
}
+ case CommandGraphResourceType::Dispatcher:
+ return "Dispatcher";
case CommandGraphResourceType::EmulatedQuery:
switch (function)
{
@@ -1032,6 +1034,7 @@
int framebufferIDCounter = 1;
int imageIDCounter = 1;
int queryIDCounter = 1;
+ int dispatcherIDCounter = 1;
int fenceIDCounter = 1;
int xfbIDCounter = 1;
@@ -1107,6 +1110,9 @@
case CommandGraphResourceType::Image:
id = imageIDCounter++;
break;
+ case CommandGraphResourceType::Dispatcher:
+ id = dispatcherIDCounter++;
+ break;
case CommandGraphResourceType::FenceSync:
id = fenceIDCounter++;
break;
diff --git a/src/libANGLE/renderer/vulkan/CommandGraph.h b/src/libANGLE/renderer/vulkan/CommandGraph.h
index fc9c309..ca00e70 100644
--- a/src/libANGLE/renderer/vulkan/CommandGraph.h
+++ b/src/libANGLE/renderer/vulkan/CommandGraph.h
@@ -32,6 +32,7 @@
Framebuffer,
Image,
Query,
+ Dispatcher,
// Transform feedback queries could be handled entirely on the CPU (if not using
// VK_EXT_transform_feedback), but still need to generate a command graph barrier node.
EmulatedQuery,
diff --git a/src/libANGLE/renderer/vulkan/ContextVk.cpp b/src/libANGLE/renderer/vulkan/ContextVk.cpp
index dc87007..413ff06 100644
--- a/src/libANGLE/renderer/vulkan/ContextVk.cpp
+++ b/src/libANGLE/renderer/vulkan/ContextVk.cpp
@@ -171,6 +171,7 @@
: ContextImpl(state, errorSet),
vk::Context(renderer),
mCurrentGraphicsPipeline(nullptr),
+ mCurrentComputePipeline(nullptr),
mCurrentDrawMode(gl::PrimitiveMode::InvalidEnum),
mCurrentWindowSurface(nullptr),
mVertexArray(nullptr),
@@ -209,6 +210,11 @@
mNewGraphicsCommandBufferDirtyBits.set(DIRTY_BIT_TRANSFORM_FEEDBACK_BUFFERS);
mNewGraphicsCommandBufferDirtyBits.set(DIRTY_BIT_DESCRIPTOR_SETS);
+ mNewComputeCommandBufferDirtyBits.set(DIRTY_BIT_PIPELINE);
+ mNewComputeCommandBufferDirtyBits.set(DIRTY_BIT_TEXTURES);
+ mNewComputeCommandBufferDirtyBits.set(DIRTY_BIT_UNIFORM_AND_STORAGE_BUFFERS);
+ mNewComputeCommandBufferDirtyBits.set(DIRTY_BIT_DESCRIPTOR_SETS);
+
mGraphicsDirtyBitHandlers[DIRTY_BIT_DEFAULT_ATTRIBS] =
&ContextVk::handleDirtyGraphicsDefaultAttribs;
mGraphicsDirtyBitHandlers[DIRTY_BIT_PIPELINE] = &ContextVk::handleDirtyGraphicsPipeline;
@@ -225,7 +231,15 @@
mGraphicsDirtyBitHandlers[DIRTY_BIT_DESCRIPTOR_SETS] =
&ContextVk::handleDirtyGraphicsDescriptorSets;
+ mComputeDirtyBitHandlers[DIRTY_BIT_PIPELINE] = &ContextVk::handleDirtyComputePipeline;
+ mComputeDirtyBitHandlers[DIRTY_BIT_TEXTURES] = &ContextVk::handleDirtyComputeTextures;
+ mComputeDirtyBitHandlers[DIRTY_BIT_UNIFORM_AND_STORAGE_BUFFERS] =
+ &ContextVk::handleDirtyComputeUniformAndStorageBuffers;
+ mComputeDirtyBitHandlers[DIRTY_BIT_DESCRIPTOR_SETS] =
+ &ContextVk::handleDirtyComputeDescriptorSets;
+
mGraphicsDirtyBits = mNewGraphicsCommandBufferDirtyBits;
+ mComputeDirtyBits = mNewComputeCommandBufferDirtyBits;
mActiveTextures.fill(nullptr);
@@ -502,6 +516,30 @@
mIndexedDirtyBitsMask, commandBufferOut);
}
+angle::Result ContextVk::setupDispatch(const gl::Context *context,
+ vk::CommandBuffer **commandBufferOut)
+{
+ ANGLE_TRY(mDispatcher.recordCommands(this, commandBufferOut));
+
+ if (mProgram->dirtyUniforms())
+ {
+ ANGLE_TRY(mProgram->updateUniforms(this));
+ mComputeDirtyBits.set(DIRTY_BIT_DESCRIPTOR_SETS);
+ }
+
+ DirtyBits dirtyBits = mComputeDirtyBits;
+
+ // Flush any relevant dirty bits.
+ for (size_t dirtyBit : dirtyBits)
+ {
+ ANGLE_TRY((this->*mComputeDirtyBitHandlers[dirtyBit])(context, *commandBufferOut));
+ }
+
+ mComputeDirtyBits.reset();
+
+ return angle::Result::Continue;
+}
+
angle::Result ContextVk::handleDirtyGraphicsDefaultAttribs(const gl::Context *context,
vk::CommandBuffer *commandBuffer)
{
@@ -557,18 +595,47 @@
return angle::Result::Continue;
}
-angle::Result ContextVk::handleDirtyGraphicsTextures(const gl::Context *context,
- vk::CommandBuffer *commandBuffer)
+angle::Result ContextVk::handleDirtyComputePipeline(const gl::Context *context,
+ vk::CommandBuffer *commandBuffer)
{
- ANGLE_TRY(updateActiveTextures(context));
+ if (!mCurrentComputePipeline)
+ {
+ ANGLE_TRY(mProgram->getComputePipeline(this, &mCurrentComputePipeline));
+ }
+
+ commandBuffer->bindComputePipeline(mCurrentComputePipeline->get());
+ mCurrentComputePipeline->updateSerial(getCurrentQueueSerial());
+
+ return angle::Result::Continue;
+}
+
+ANGLE_INLINE angle::Result ContextVk::handleDirtyTexturesImpl(const gl::Context *context,
+ vk::CommandBuffer *commandBuffer,
+ vk::CommandGraphResource *recorder)
+{
+
+ ANGLE_TRY(updateActiveTextures(context, recorder));
if (mProgram->hasTextures())
{
- ANGLE_TRY(mProgram->updateTexturesDescriptorSet(this, mDrawFramebuffer->getFramebuffer()));
+ ANGLE_TRY(mProgram->updateTexturesDescriptorSet(this));
}
+
return angle::Result::Continue;
}
+angle::Result ContextVk::handleDirtyGraphicsTextures(const gl::Context *context,
+ vk::CommandBuffer *commandBuffer)
+{
+ return handleDirtyTexturesImpl(context, commandBuffer, mDrawFramebuffer->getFramebuffer());
+}
+
+angle::Result ContextVk::handleDirtyComputeTextures(const gl::Context *context,
+ vk::CommandBuffer *commandBuffer)
+{
+ return handleDirtyTexturesImpl(context, commandBuffer, &mDispatcher);
+}
+
angle::Result ContextVk::handleDirtyGraphicsVertexBuffers(const gl::Context *context,
vk::CommandBuffer *commandBuffer)
{
@@ -611,16 +678,31 @@
return angle::Result::Continue;
}
+ANGLE_INLINE angle::Result ContextVk::handleDirtyUniformAndStorageBuffersImpl(
+ const gl::Context *context,
+ vk::CommandBuffer *commandBuffer,
+ vk::CommandGraphResource *recorder)
+{
+ if (mProgram->hasUniformBuffers() || mProgram->hasStorageBuffers())
+ {
+ ANGLE_TRY(mProgram->updateUniformAndStorageBuffersDescriptorSet(this, recorder));
+ }
+ return angle::Result::Continue;
+}
+
angle::Result ContextVk::handleDirtyGraphicsUniformAndStorageBuffers(
const gl::Context *context,
vk::CommandBuffer *commandBuffer)
{
- if (mProgram->hasUniformBuffers() || mProgram->hasStorageBuffers())
- {
- ANGLE_TRY(mProgram->updateUniformAndStorageBuffersDescriptorSet(
- this, mDrawFramebuffer->getFramebuffer()));
- }
- return angle::Result::Continue;
+ return handleDirtyUniformAndStorageBuffersImpl(context, commandBuffer,
+ mDrawFramebuffer->getFramebuffer());
+}
+
+angle::Result ContextVk::handleDirtyComputeUniformAndStorageBuffers(
+ const gl::Context *context,
+ vk::CommandBuffer *commandBuffer)
+{
+ return handleDirtyUniformAndStorageBuffersImpl(context, commandBuffer, &mDispatcher);
}
angle::Result ContextVk::handleDirtyGraphicsTransformFeedbackBuffers(
@@ -648,6 +730,12 @@
return angle::Result::Continue;
}
+angle::Result ContextVk::handleDirtyComputeDescriptorSets(const gl::Context *context,
+ vk::CommandBuffer *commandBuffer)
+{
+ return mProgram->updateDescriptorSets(this, commandBuffer);
+}
+
angle::Result ContextVk::submitFrame(const VkSubmitInfo &submitInfo,
vk::PrimaryCommandBuffer &&commandBuffer)
{
@@ -671,6 +759,7 @@
// recording command buffer is valid. Thus we need to explicitly notify every other Context
// using this VkQueue that they their current command buffer is no longer valid.
onRenderPassFinished();
+ mComputeDirtyBits |= mNewComputeCommandBufferDirtyBits;
// Store this command buffer in the in-flight list.
batch.commandPool = std::move(mCommandPool);
@@ -1376,13 +1465,14 @@
const gl::State::DirtyBits &dirtyBits,
const gl::State::DirtyBits &bitMask)
{
- if ((dirtyBits & mPipelineDirtyBitsMask).any())
+ const gl::State &glState = context->getState();
+
+ if ((dirtyBits & mPipelineDirtyBitsMask).any() &&
+ (glState.getProgram() == nullptr || !glState.getProgram()->isCompute()))
{
invalidateVertexAndIndexBuffers();
}
- const gl::State &glState = context->getState();
-
for (size_t dirtyBit : dirtyBits)
{
switch (dirtyBit)
@@ -1609,13 +1699,21 @@
{
invalidateCurrentTextures();
invalidateCurrentUniformAndStorageBuffers();
- // No additional work is needed here. We will update the pipeline desc later.
- invalidateDefaultAttributes(context->getStateCache().getActiveDefaultAttribsMask());
- bool useVertexBuffer = (mProgram->getState().getMaxActiveAttribLocation());
- mNonIndexedDirtyBitsMask.set(DIRTY_BIT_VERTEX_BUFFERS, useVertexBuffer);
- mIndexedDirtyBitsMask.set(DIRTY_BIT_VERTEX_BUFFERS, useVertexBuffer);
- mCurrentGraphicsPipeline = nullptr;
- mGraphicsPipelineTransition.reset();
+ if (glState.getProgram()->isCompute())
+ {
+ invalidateCurrentComputePipeline();
+ }
+ else
+ {
+ // No additional work is needed here. We will update the pipeline desc later.
+ invalidateDefaultAttributes(
+ context->getStateCache().getActiveDefaultAttribsMask());
+ bool useVertexBuffer = (mProgram->getState().getMaxActiveAttribLocation());
+ mNonIndexedDirtyBitsMask.set(DIRTY_BIT_VERTEX_BUFFERS, useVertexBuffer);
+ mIndexedDirtyBitsMask.set(DIRTY_BIT_VERTEX_BUFFERS, useVertexBuffer);
+ mCurrentGraphicsPipeline = nullptr;
+ mGraphicsPipelineTransition.reset();
+ }
break;
}
case gl::State::DIRTY_BIT_TEXTURE_BINDINGS:
@@ -1848,6 +1946,8 @@
{
mGraphicsDirtyBits.set(DIRTY_BIT_TEXTURES);
mGraphicsDirtyBits.set(DIRTY_BIT_DESCRIPTOR_SETS);
+ mComputeDirtyBits.set(DIRTY_BIT_TEXTURES);
+ mComputeDirtyBits.set(DIRTY_BIT_DESCRIPTOR_SETS);
}
}
@@ -1858,6 +1958,8 @@
{
mGraphicsDirtyBits.set(DIRTY_BIT_UNIFORM_AND_STORAGE_BUFFERS);
mGraphicsDirtyBits.set(DIRTY_BIT_DESCRIPTOR_SETS);
+ mComputeDirtyBits.set(DIRTY_BIT_UNIFORM_AND_STORAGE_BUFFERS);
+ mComputeDirtyBits.set(DIRTY_BIT_DESCRIPTOR_SETS);
}
}
@@ -1892,8 +1994,12 @@
GLuint numGroupsY,
GLuint numGroupsZ)
{
- ANGLE_VK_UNREACHABLE(this);
- return angle::Result::Stop;
+ vk::CommandBuffer *commandBuffer;
+ ANGLE_TRY(setupDispatch(context, &commandBuffer));
+
+ commandBuffer->dispatch(numGroupsX, numGroupsY, numGroupsZ);
+
+ return angle::Result::Continue;
}
angle::Result ContextVk::dispatchComputeIndirect(const gl::Context *context, GLintptr indirect)
@@ -2039,7 +2145,8 @@
mErrors->handleError(glErrorCode, errorStream.str().c_str(), file, function, line);
}
-angle::Result ContextVk::updateActiveTextures(const gl::Context *context)
+angle::Result ContextVk::updateActiveTextures(const gl::Context *context,
+ vk::CommandGraphResource *recorder)
{
const gl::State &glState = mState;
const gl::Program *program = glState.getProgram();
@@ -2052,6 +2159,10 @@
const gl::ActiveTextureMask &activeTextures = program->getActiveSamplersMask();
const gl::ActiveTextureTypeArray &textureTypes = program->getActiveSamplerTypes();
+ const vk::ImageLayout textureLayout = program->isCompute()
+ ? vk::ImageLayout::ComputeShaderReadOnly
+ : vk::ImageLayout::FragmentShaderReadOnly;
+
for (size_t textureUnit : activeTextures)
{
gl::Texture *texture = textures[textureUnit];
@@ -2072,18 +2183,17 @@
// we can't verify it has no staged updates right here.
// Ensure the image is in read-only layout
- if (image.isLayoutChangeNecessary(vk::ImageLayout::FragmentShaderReadOnly))
+ if (image.isLayoutChangeNecessary(textureLayout))
{
vk::CommandBuffer *srcLayoutChange;
ANGLE_TRY(image.recordCommands(this, &srcLayoutChange));
VkImageAspectFlags aspectFlags = image.getAspectFlags();
ASSERT(aspectFlags != 0);
- image.changeLayout(aspectFlags, vk::ImageLayout::FragmentShaderReadOnly,
- srcLayoutChange);
+ image.changeLayout(aspectFlags, textureLayout, srcLayoutChange);
}
- image.addReadDependency(mDrawFramebuffer->getFramebuffer());
+ image.addReadDependency(recorder);
mActiveTextures[textureUnit] = textureVk;
mActiveTexturesDesc.update(textureUnit, textureVk->getSerial());
diff --git a/src/libANGLE/renderer/vulkan/ContextVk.h b/src/libANGLE/renderer/vulkan/ContextVk.h
index 4b01a04..daecc53 100644
--- a/src/libANGLE/renderer/vulkan/ContextVk.h
+++ b/src/libANGLE/renderer/vulkan/ContextVk.h
@@ -331,6 +331,7 @@
vk::CommandBuffer *commandBuffer);
std::array<DirtyBitHandler, DIRTY_BIT_MAX> mGraphicsDirtyBitHandlers;
+ std::array<DirtyBitHandler, DIRTY_BIT_MAX> mComputeDirtyBitHandlers;
angle::Result setupDraw(const gl::Context *context,
gl::PrimitiveMode mode,
@@ -356,6 +357,7 @@
const void *indices,
vk::CommandBuffer **commandBufferOut,
size_t *numIndicesOut);
+ angle::Result setupDispatch(const gl::Context *context, vk::CommandBuffer **commandBufferOut);
void updateViewport(FramebufferVk *framebufferVk,
const gl::Rectangle &viewport,
@@ -367,13 +369,19 @@
void updateFlipViewportDrawFramebuffer(const gl::State &glState);
void updateFlipViewportReadFramebuffer(const gl::State &glState);
- angle::Result updateActiveTextures(const gl::Context *context);
+ angle::Result updateActiveTextures(const gl::Context *context,
+ vk::CommandGraphResource *recorder);
angle::Result updateDefaultAttribute(size_t attribIndex);
ANGLE_INLINE void invalidateCurrentGraphicsPipeline()
{
mGraphicsDirtyBits.set(DIRTY_BIT_PIPELINE);
}
+ ANGLE_INLINE void invalidateCurrentComputePipeline()
+ {
+ mComputeDirtyBits.set(DIRTY_BIT_PIPELINE);
+ mCurrentComputePipeline = nullptr;
+ }
void invalidateCurrentTextures();
void invalidateCurrentUniformAndStorageBuffers();
@@ -399,6 +407,24 @@
angle::Result handleDirtyGraphicsDescriptorSets(const gl::Context *context,
vk::CommandBuffer *commandBuffer);
+ // Handlers for compute pipeline dirty bits.
+ angle::Result handleDirtyComputePipeline(const gl::Context *context,
+ vk::CommandBuffer *commandBuffer);
+ angle::Result handleDirtyComputeTextures(const gl::Context *context,
+ vk::CommandBuffer *commandBuffer);
+ angle::Result handleDirtyComputeUniformAndStorageBuffers(const gl::Context *context,
+ vk::CommandBuffer *commandBuffer);
+ angle::Result handleDirtyComputeDescriptorSets(const gl::Context *context,
+ vk::CommandBuffer *commandBuffer);
+
+ // Common parts of the common dirty bit handlers.
+ angle::Result handleDirtyTexturesImpl(const gl::Context *context,
+ vk::CommandBuffer *commandBuffer,
+ vk::CommandGraphResource *recorder);
+ angle::Result handleDirtyUniformAndStorageBuffersImpl(const gl::Context *context,
+ vk::CommandBuffer *commandBuffer,
+ vk::CommandGraphResource *recorder);
+
angle::Result submitFrame(const VkSubmitInfo &submitInfo,
vk::PrimaryCommandBuffer &&commandBuffer);
void freeAllInFlightResources();
@@ -416,6 +442,7 @@
void waitForSwapchainImageIfNecessary();
vk::PipelineHelper *mCurrentGraphicsPipeline;
+ vk::PipelineAndSerial *mCurrentComputePipeline;
gl::PrimitiveMode mCurrentDrawMode;
WindowSurfaceVk *mCurrentWindowSurface;
@@ -434,15 +461,20 @@
// Dirty bits.
DirtyBits mGraphicsDirtyBits;
+ DirtyBits mComputeDirtyBits;
DirtyBits mNonIndexedDirtyBitsMask;
DirtyBits mIndexedDirtyBitsMask;
DirtyBits mNewGraphicsCommandBufferDirtyBits;
+ DirtyBits mNewComputeCommandBufferDirtyBits;
// Cached back-end objects.
VertexArrayVk *mVertexArray;
FramebufferVk *mDrawFramebuffer;
ProgramVk *mProgram;
+ // Graph resource used to record dispatch commands and hold resource dependencies.
+ vk::DispatchHelper mDispatcher;
+
// The offset we had the last time we bound the index buffer.
const GLvoid *mLastIndexBufferOffset;
gl::DrawElementsType mCurrentDrawElementsType;
diff --git a/src/libANGLE/renderer/vulkan/GlslangWrapper.cpp b/src/libANGLE/renderer/vulkan/GlslangWrapper.cpp
index 06dcdcb..841aa76 100644
--- a/src/libANGLE/renderer/vulkan/GlslangWrapper.cpp
+++ b/src/libANGLE/renderer/vulkan/GlslangWrapper.cpp
@@ -98,7 +98,8 @@
class IntermediateShaderSource final : angle::NonCopyable
{
public:
- IntermediateShaderSource(const std::string &source);
+ void init(const std::string &source);
+ bool empty() const { return mTokens.empty(); }
// Find @@ LAYOUT-name(extra, args) @@ and replace it with:
//
@@ -217,7 +218,7 @@
return readCount;
}
-IntermediateShaderSource::IntermediateShaderSource(const std::string &source)
+void IntermediateShaderSource::init(const std::string &source)
{
size_t cur = 0;
@@ -491,7 +492,7 @@
void AssignAttributeLocations(const gl::ProgramState &programState,
IntermediateShaderSource *vertexSource)
{
- ASSERT(vertexSource != nullptr);
+ ASSERT(!vertexSource->empty());
// Parse attribute locations and replace them in the vertex shader.
// See corresponding code in OutputVulkanGLSL.cpp.
@@ -510,7 +511,7 @@
void AssignOutputLocations(const gl::ProgramState &programState,
IntermediateShaderSource *fragmentSource)
{
- ASSERT(fragmentSource != nullptr);
+ ASSERT(!fragmentSource->empty());
// Parse output locations and replace them in the fragment shader.
// See corresponding code in OutputVulkanGLSL.cpp.
@@ -549,6 +550,9 @@
IntermediateShaderSource *outStageSource,
IntermediateShaderSource *inStageSource)
{
+ ASSERT(!outStageSource->empty());
+ ASSERT(!inStageSource->empty());
+
// Assign varying locations.
for (const gl::PackedVaryingRegister &varyingReg : resources.varyingPacking.getRegisterList())
{
@@ -623,7 +627,7 @@
inStageSource->insertQualifierSpecifier(kVaryingName, "in");
}
-void AssignUniformBindings(const gl::ShaderMap<IntermediateShaderSource *> &shaderSources)
+void AssignUniformBindings(gl::ShaderMap<IntermediateShaderSource> *shaderSources)
{
// Bind the default uniforms for vertex and fragment shaders.
// See corresponding code in OutputVulkanGLSL.cpp.
@@ -631,18 +635,18 @@
constexpr char kDefaultUniformsBlockName[] = "defaultUniforms";
size_t bindingIndex = 0;
- for (IntermediateShaderSource *shaderSource : shaderSources)
+ for (IntermediateShaderSource &shaderSource : *shaderSources)
{
- if (shaderSource)
+ if (!shaderSource.empty())
{
std::string defaultUniformsBinding =
uniformsDescriptorSet + ", binding = " + Str(bindingIndex++);
- shaderSource->insertLayoutSpecifier(kDefaultUniformsBlockName, defaultUniformsBinding);
+ shaderSource.insertLayoutSpecifier(kDefaultUniformsBlockName, defaultUniformsBinding);
}
}
- if (shaderSources[gl::ShaderType::Compute] != nullptr)
+ if (!(*shaderSources)[gl::ShaderType::Compute].empty())
{
// Compute doesn't need driver uniforms.
return;
@@ -653,13 +657,10 @@
"set = " + Str(kDriverUniformsDescriptorSetIndex) + ", binding = 0";
constexpr char kDriverBlockName[] = "ANGLEUniformBlock";
- for (IntermediateShaderSource *shaderSource : shaderSources)
+ for (IntermediateShaderSource &shaderSource : *shaderSources)
{
- if (shaderSource)
- {
- shaderSource->insertLayoutSpecifier(kDriverBlockName, driverBlockLayoutString);
- shaderSource->insertQualifierSpecifier(kDriverBlockName, kUniformQualifier);
- }
+ shaderSource.insertLayoutSpecifier(kDriverBlockName, driverBlockLayoutString);
+ shaderSource.insertQualifierSpecifier(kDriverBlockName, kUniformQualifier);
}
}
@@ -669,31 +670,30 @@
const std::string &bindingString,
const char *qualifier,
const char *unusedSubstitution,
- const gl::ShaderMap<IntermediateShaderSource *> &shaderSources)
+ gl::ShaderMap<IntermediateShaderSource> *shaderSources)
{
for (const gl::ShaderType shaderType : gl::AllShaderTypes())
{
- IntermediateShaderSource *shaderSource = shaderSources[shaderType];
- if (shaderSource)
+ IntermediateShaderSource &shaderSource = (*shaderSources)[shaderType];
+ if (!shaderSource.empty())
{
if (activeShaders[shaderType])
{
- shaderSource->insertLayoutSpecifier(name, bindingString);
- shaderSource->insertQualifierSpecifier(name, qualifier);
+ shaderSource.insertLayoutSpecifier(name, bindingString);
+ shaderSource.insertQualifierSpecifier(name, qualifier);
}
else
{
- shaderSource->eraseLayoutAndQualifierSpecifiers(name, unusedSubstitution);
+ shaderSource.eraseLayoutAndQualifierSpecifiers(name, unusedSubstitution);
}
}
}
}
-uint32_t AssignInterfaceBlockBindings(
- const std::vector<gl::InterfaceBlock> &blocks,
- const char *qualifier,
- uint32_t bindingStart,
- const gl::ShaderMap<IntermediateShaderSource *> &shaderSources)
+uint32_t AssignInterfaceBlockBindings(const std::vector<gl::InterfaceBlock> &blocks,
+ const char *qualifier,
+ uint32_t bindingStart,
+ gl::ShaderMap<IntermediateShaderSource> *shaderSources)
{
const std::string buffersDescriptorSet = "set = " + Str(kBufferDescriptorSetIndex);
@@ -714,7 +714,7 @@
}
void AssignBufferBindings(const gl::ProgramState &programState,
- const gl::ShaderMap<IntermediateShaderSource *> &shaderSources)
+ gl::ShaderMap<IntermediateShaderSource> *shaderSources)
{
uint32_t bindingStart = 0;
@@ -730,7 +730,7 @@
}
void AssignTextureBindings(const gl::ProgramState &programState,
- const gl::ShaderMap<IntermediateShaderSource *> &shaderSources)
+ gl::ShaderMap<IntermediateShaderSource> *shaderSources)
{
const std::string texturesDescriptorSet = "set = " + Str(kTextureDescriptorSetIndex);
@@ -755,10 +755,10 @@
void CleanupUnusedEntities(const gl::ProgramState &programState,
const gl::ProgramLinkedResources &resources,
gl::Shader *glVertexShader,
- const gl::ShaderMap<IntermediateShaderSource *> &shaderSources)
+ gl::ShaderMap<IntermediateShaderSource> *shaderSources)
{
- IntermediateShaderSource *vertexSource = shaderSources[gl::ShaderType::Vertex];
- if (vertexSource)
+ IntermediateShaderSource &vertexSource = (*shaderSources)[gl::ShaderType::Vertex];
+ if (!vertexSource.empty())
{
ASSERT(glVertexShader != nullptr);
@@ -772,32 +772,26 @@
continue;
}
- vertexSource->eraseLayoutAndQualifierSpecifiers(attribute.name, "");
+ vertexSource.eraseLayoutAndQualifierSpecifiers(attribute.name, "");
}
}
// Remove all the markers for unused varyings.
for (const std::string &varyingName : resources.varyingPacking.getInactiveVaryingNames())
{
- for (IntermediateShaderSource *shaderSource : shaderSources)
+ for (IntermediateShaderSource &shaderSource : *shaderSources)
{
- if (shaderSource)
- {
- shaderSource->eraseLayoutAndQualifierSpecifiers(varyingName, "");
- }
+ shaderSource.eraseLayoutAndQualifierSpecifiers(varyingName, "");
}
}
// Remove all the markers for unused interface blocks, and replace them with |struct|.
for (const std::string &unusedInterfaceBlock : resources.unusedInterfaceBlocks)
{
- for (IntermediateShaderSource *shaderSource : shaderSources)
+ for (IntermediateShaderSource &shaderSource : *shaderSources)
{
- if (shaderSource)
- {
- shaderSource->eraseLayoutAndQualifierSpecifiers(unusedInterfaceBlock,
- kUnusedBlockSubstitution);
- }
+ shaderSource.eraseLayoutAndQualifierSpecifiers(unusedInterfaceBlock,
+ kUnusedBlockSubstitution);
}
}
@@ -808,16 +802,19 @@
std::string uniformName =
unusedUniform.isSampler ? GetMappedSamplerName(unusedUniform.name) : unusedUniform.name;
- for (IntermediateShaderSource *shaderSource : shaderSources)
+ for (IntermediateShaderSource &shaderSource : *shaderSources)
{
- if (shaderSource)
- {
- shaderSource->eraseLayoutAndQualifierSpecifiers(uniformName,
- kUnusedUniformSubstitution);
- }
+ shaderSource.eraseLayoutAndQualifierSpecifiers(uniformName, kUnusedUniformSubstitution);
}
}
}
+
+constexpr gl::ShaderMap<EShLanguage> kShLanguageMap = {
+ {gl::ShaderType::Vertex, EShLangVertex},
+ {gl::ShaderType::Geometry, EShLangGeometry},
+ {gl::ShaderType::Fragment, EShLangFragment},
+ {gl::ShaderType::Compute, EShLangCompute},
+};
} // anonymous namespace
// static
@@ -837,127 +834,138 @@
// static
void GlslangWrapper::GetShaderSource(const gl::ProgramState &programState,
const gl::ProgramLinkedResources &resources,
- std::string *vertexSourceOut,
- std::string *fragmentSourceOut)
+ gl::ShaderMap<std::string> *shaderSourcesOut)
{
- gl::Shader *glVertexShader = programState.getAttachedShader(gl::ShaderType::Vertex);
- gl::Shader *glFragmentShader = programState.getAttachedShader(gl::ShaderType::Fragment);
+ gl::ShaderMap<IntermediateShaderSource> intermediateSources;
- IntermediateShaderSource vertexSource(glVertexShader->getTranslatedSource());
- IntermediateShaderSource fragmentSource(glFragmentShader->getTranslatedSource());
+ for (const gl::ShaderType shaderType : gl::AllShaderTypes())
+ {
+ gl::Shader *glShader = programState.getAttachedShader(shaderType);
+ if (glShader)
+ {
+ intermediateSources[shaderType].init(glShader->getTranslatedSource());
+ }
+ }
- gl::ShaderMap<IntermediateShaderSource *> shaderSources{};
+ IntermediateShaderSource *vertexSource = &intermediateSources[gl::ShaderType::Vertex];
+ IntermediateShaderSource *fragmentSource = &intermediateSources[gl::ShaderType::Fragment];
- shaderSources[gl::ShaderType::Vertex] = &vertexSource;
- shaderSources[gl::ShaderType::Fragment] = &fragmentSource;
+ if (!vertexSource->empty())
+ {
+ AssignAttributeLocations(programState, vertexSource);
+ AssignOutputLocations(programState, fragmentSource);
+ AssignVaryingLocations(resources, vertexSource, fragmentSource);
+ }
+ AssignUniformBindings(&intermediateSources);
+ AssignBufferBindings(programState, &intermediateSources);
+ AssignTextureBindings(programState, &intermediateSources);
- AssignAttributeLocations(programState, shaderSources[gl::ShaderType::Vertex]);
- AssignOutputLocations(programState, shaderSources[gl::ShaderType::Fragment]);
- AssignVaryingLocations(resources, shaderSources[gl::ShaderType::Vertex],
- shaderSources[gl::ShaderType::Fragment]);
- AssignUniformBindings(shaderSources);
- AssignBufferBindings(programState, shaderSources);
- AssignTextureBindings(programState, shaderSources);
-
- CleanupUnusedEntities(programState, resources, glVertexShader, shaderSources);
+ CleanupUnusedEntities(programState, resources,
+ programState.getAttachedShader(gl::ShaderType::Vertex),
+ &intermediateSources);
// Write transform feedback output code.
- if (programState.getLinkedTransformFeedbackVaryings().empty())
+ if (!vertexSource->empty())
{
- vertexSource.insertTransformFeedbackDeclaration("");
- vertexSource.insertTransformFeedbackOutput("");
- }
- else
- {
- GenerateTransformFeedbackOutputs(programState, &vertexSource);
+ if (programState.getLinkedTransformFeedbackVaryings().empty())
+ {
+ vertexSource->insertTransformFeedbackDeclaration("");
+ vertexSource->insertTransformFeedbackOutput("");
+ }
+ else
+ {
+ GenerateTransformFeedbackOutputs(programState, vertexSource);
+ }
}
- *vertexSourceOut = vertexSource.getShaderSource();
- *fragmentSourceOut = fragmentSource.getShaderSource();
+ for (const gl::ShaderType shaderType : gl::AllShaderTypes())
+ {
+ (*shaderSourcesOut)[shaderType] = intermediateSources[shaderType].getShaderSource();
+ }
}
// static
angle::Result GlslangWrapper::GetShaderCode(vk::Context *context,
const gl::Caps &glCaps,
bool enableLineRasterEmulation,
- const std::string &vertexSource,
- const std::string &fragmentSource,
- std::vector<uint32_t> *vertexCodeOut,
- std::vector<uint32_t> *fragmentCodeOut)
+ const gl::ShaderMap<std::string> &shaderSources,
+ gl::ShaderMap<std::vector<uint32_t>> *shaderCodeOut)
{
if (enableLineRasterEmulation)
{
- std::string patchedVertexSource = vertexSource;
- std::string patchedFragmentSource = fragmentSource;
+ ASSERT(shaderSources[gl::ShaderType::Compute].empty());
+
+ gl::ShaderMap<std::string> patchedSources = shaderSources;
// #defines must come after the #version directive.
- ANGLE_VK_CHECK(
- context,
- angle::ReplaceSubstring(&patchedVertexSource, kVersionDefine, kLineRasterDefine),
- VK_ERROR_INVALID_SHADER_NV);
- ANGLE_VK_CHECK(
- context,
- angle::ReplaceSubstring(&patchedFragmentSource, kVersionDefine, kLineRasterDefine),
- VK_ERROR_INVALID_SHADER_NV);
+ ANGLE_VK_CHECK(context,
+ angle::ReplaceSubstring(&patchedSources[gl::ShaderType::Vertex],
+ kVersionDefine, kLineRasterDefine),
+ VK_ERROR_INVALID_SHADER_NV);
+ ANGLE_VK_CHECK(context,
+ angle::ReplaceSubstring(&patchedSources[gl::ShaderType::Fragment],
+ kVersionDefine, kLineRasterDefine),
+ VK_ERROR_INVALID_SHADER_NV);
- return GetShaderCodeImpl(context, glCaps, patchedVertexSource, patchedFragmentSource,
- vertexCodeOut, fragmentCodeOut);
+ return GetShaderCodeImpl(context, glCaps, patchedSources, shaderCodeOut);
}
else
{
- return GetShaderCodeImpl(context, glCaps, vertexSource, fragmentSource, vertexCodeOut,
- fragmentCodeOut);
+ return GetShaderCodeImpl(context, glCaps, shaderSources, shaderCodeOut);
}
}
// static
angle::Result GlslangWrapper::GetShaderCodeImpl(vk::Context *context,
const gl::Caps &glCaps,
- const std::string &vertexSource,
- const std::string &fragmentSource,
- std::vector<uint32_t> *vertexCodeOut,
- std::vector<uint32_t> *fragmentCodeOut)
+ const gl::ShaderMap<std::string> &shaderSources,
+ gl::ShaderMap<std::vector<uint32_t>> *shaderCodeOut)
{
- std::array<const char *, 2> strings = {{vertexSource.c_str(), fragmentSource.c_str()}};
- std::array<int, 2> lengths = {
- {static_cast<int>(vertexSource.length()), static_cast<int>(fragmentSource.length())}};
-
// Enable SPIR-V and Vulkan rules when parsing GLSL
EShMessages messages = static_cast<EShMessages>(EShMsgSpvRules | EShMsgVulkanRules);
- glslang::TShader vertexShader(EShLangVertex);
- vertexShader.setStringsWithLengths(&strings[0], &lengths[0], 1);
- vertexShader.setEntryPoint("main");
-
TBuiltInResource builtInResources(glslang::DefaultTBuiltInResource);
GetBuiltInResourcesFromCaps(glCaps, &builtInResources);
- bool vertexResult =
- vertexShader.parse(&builtInResources, 450, ECoreProfile, false, false, messages);
- if (!vertexResult)
- {
- ERR() << "Internal error parsing Vulkan vertex shader:\n"
- << vertexShader.getInfoLog() << "\n"
- << vertexShader.getInfoDebugLog() << "\n";
- ANGLE_VK_CHECK(context, false, VK_ERROR_INVALID_SHADER_NV);
- }
-
+ glslang::TShader vertexShader(EShLangVertex);
glslang::TShader fragmentShader(EShLangFragment);
- fragmentShader.setStringsWithLengths(&strings[1], &lengths[1], 1);
- fragmentShader.setEntryPoint("main");
- bool fragmentResult =
- fragmentShader.parse(&builtInResources, 450, ECoreProfile, false, false, messages);
- if (!fragmentResult)
+ glslang::TShader geometryShader(EShLangGeometry);
+ glslang::TShader computeShader(EShLangCompute);
+
+ gl::ShaderMap<glslang::TShader *> shaders = {
+ {gl::ShaderType::Vertex, &vertexShader},
+ {gl::ShaderType::Fragment, &fragmentShader},
+ {gl::ShaderType::Geometry, &geometryShader},
+ {gl::ShaderType::Compute, &computeShader},
+ };
+ glslang::TProgram program;
+
+ for (const gl::ShaderType shaderType : gl::AllShaderTypes())
{
- ERR() << "Internal error parsing Vulkan fragment shader:\n"
- << fragmentShader.getInfoLog() << "\n"
- << fragmentShader.getInfoDebugLog() << "\n";
- ANGLE_VK_CHECK(context, false, VK_ERROR_INVALID_SHADER_NV);
+ if (shaderSources[shaderType].empty())
+ {
+ continue;
+ }
+
+ const char *shaderString = shaderSources[shaderType].c_str();
+ int shaderLength = static_cast<int>(shaderSources[shaderType].size());
+
+ glslang::TShader *shader = shaders[shaderType];
+ shader->setStringsWithLengths(&shaderString, &shaderLength, 1);
+ shader->setEntryPoint("main");
+
+ bool result = shader->parse(&builtInResources, 450, ECoreProfile, false, false, messages);
+ if (!result)
+ {
+ ERR() << "Internal error parsing Vulkan shader corresponding to " << shaderType << ":\n"
+ << shader->getInfoLog() << "\n"
+ << shader->getInfoDebugLog() << "\n";
+ ANGLE_VK_CHECK(context, false, VK_ERROR_INVALID_SHADER_NV);
+ }
+
+ program.addShader(shader);
}
- glslang::TProgram program;
- program.addShader(&vertexShader);
- program.addShader(&fragmentShader);
bool linkResult = program.link(messages);
if (!linkResult)
{
@@ -965,10 +973,16 @@
ANGLE_VK_CHECK(context, false, VK_ERROR_INVALID_SHADER_NV);
}
- glslang::TIntermediate *vertexStage = program.getIntermediate(EShLangVertex);
- glslang::TIntermediate *fragmentStage = program.getIntermediate(EShLangFragment);
- glslang::GlslangToSpv(*vertexStage, *vertexCodeOut);
- glslang::GlslangToSpv(*fragmentStage, *fragmentCodeOut);
+ for (const gl::ShaderType shaderType : gl::AllShaderTypes())
+ {
+ if (shaderSources[shaderType].empty())
+ {
+ continue;
+ }
+
+ glslang::TIntermediate *intermediate = program.getIntermediate(kShLanguageMap[shaderType]);
+ glslang::GlslangToSpv(*intermediate, (*shaderCodeOut)[shaderType]);
+ }
return angle::Result::Continue;
}
diff --git a/src/libANGLE/renderer/vulkan/GlslangWrapper.h b/src/libANGLE/renderer/vulkan/GlslangWrapper.h
index 6adccd4..4ab47be 100644
--- a/src/libANGLE/renderer/vulkan/GlslangWrapper.h
+++ b/src/libANGLE/renderer/vulkan/GlslangWrapper.h
@@ -24,24 +24,19 @@
static void GetShaderSource(const gl::ProgramState &programState,
const gl::ProgramLinkedResources &resources,
- std::string *vertexSourceOut,
- std::string *fragmentSourceOut);
+ gl::ShaderMap<std::string> *shaderSourcesOut);
static angle::Result GetShaderCode(vk::Context *context,
const gl::Caps &glCaps,
bool enableLineRasterEmulation,
- const std::string &vertexSource,
- const std::string &fragmentSource,
- std::vector<uint32_t> *vertexCodeOut,
- std::vector<uint32_t> *fragmentCodeOut);
+ const gl::ShaderMap<std::string> &shaderSources,
+ gl::ShaderMap<std::vector<uint32_t>> *shaderCodesOut);
private:
static angle::Result GetShaderCodeImpl(vk::Context *context,
const gl::Caps &glCaps,
- const std::string &vertexSource,
- const std::string &fragmentSource,
- std::vector<uint32_t> *vertexCodeOut,
- std::vector<uint32_t> *fragmentCodeOut);
+ const gl::ShaderMap<std::string> &shaderSources,
+ gl::ShaderMap<std::vector<uint32_t>> *shaderCodesOut);
};
} // namespace rx
diff --git a/src/libANGLE/renderer/vulkan/ProgramVk.cpp b/src/libANGLE/renderer/vulkan/ProgramVk.cpp
index 7a9040a..eb62701 100644
--- a/src/libANGLE/renderer/vulkan/ProgramVk.cpp
+++ b/src/libANGLE/renderer/vulkan/ProgramVk.cpp
@@ -195,25 +195,26 @@
ProgramVk::ShaderInfo::~ShaderInfo() = default;
angle::Result ProgramVk::ShaderInfo::initShaders(ContextVk *contextVk,
- const std::string &vertexSource,
- const std::string &fragmentSource,
+ const gl::ShaderMap<std::string> &shaderSources,
bool enableLineRasterEmulation)
{
ASSERT(!valid());
- std::vector<uint32_t> vertexCode;
- std::vector<uint32_t> fragmentCode;
- ANGLE_TRY(GlslangWrapper::GetShaderCode(contextVk, contextVk->getCaps(),
- enableLineRasterEmulation, vertexSource, fragmentSource,
- &vertexCode, &fragmentCode));
+ gl::ShaderMap<std::vector<uint32_t>> shaderCodes;
+ ANGLE_TRY(GlslangWrapper::GetShaderCode(
+ contextVk, contextVk->getCaps(), enableLineRasterEmulation, shaderSources, &shaderCodes));
- ANGLE_TRY(vk::InitShaderAndSerial(contextVk, &mShaders[gl::ShaderType::Vertex].get(),
- vertexCode.data(), vertexCode.size() * sizeof(uint32_t)));
- ANGLE_TRY(vk::InitShaderAndSerial(contextVk, &mShaders[gl::ShaderType::Fragment].get(),
- fragmentCode.data(), fragmentCode.size() * sizeof(uint32_t)));
+ for (const gl::ShaderType shaderType : gl::AllShaderTypes())
+ {
+ if (!shaderSources[shaderType].empty())
+ {
+ ANGLE_TRY(vk::InitShaderAndSerial(contextVk, &mShaders[shaderType].get(),
+ shaderCodes[shaderType].data(),
+ shaderCodes[shaderType].size() * sizeof(uint32_t)));
- mProgramHelper.setShader(gl::ShaderType::Vertex, &mShaders[gl::ShaderType::Vertex]);
- mProgramHelper.setShader(gl::ShaderType::Fragment, &mShaders[gl::ShaderType::Fragment]);
+ mProgramHelper.setShader(shaderType, &mShaders[shaderType]);
+ }
+ }
return angle::Result::Continue;
}
@@ -223,7 +224,7 @@
// Read in shader sources for all shader types
for (const gl::ShaderType shaderType : gl::AllShaderTypes())
{
- mShaderSource[shaderType] = stream->readString();
+ mShaderSources[shaderType] = stream->readString();
}
return angle::Result::Continue;
@@ -234,7 +235,7 @@
// Write out shader sources for all shader types
for (const gl::ShaderType shaderType : gl::AllShaderTypes())
{
- stream->writeString(mShaderSource[shaderType]);
+ stream->writeString(mShaderSources[shaderType]);
}
}
@@ -338,8 +339,7 @@
// assignment done in that function.
linkResources(resources);
- GlslangWrapper::GetShaderSource(mState, resources, &mShaderSource[gl::ShaderType::Vertex],
- &mShaderSource[gl::ShaderType::Fragment]);
+ GlslangWrapper::GetShaderSource(mState, resources, &mShaderSources);
// TODO(jie.a.chen@intel.com): Parallelize linking.
// http://crbug.com/849576
@@ -1013,7 +1013,7 @@
}
void ProgramVk::updateBuffersDescriptorSet(ContextVk *contextVk,
- vk::FramebufferHelper *framebufferVk,
+ vk::CommandGraphResource *recorder,
const std::vector<gl::InterfaceBlock> &blocks,
VkDescriptorType descriptorType)
{
@@ -1074,19 +1074,23 @@
if (isStorageBuffer)
{
- bufferHelper.onWrite(contextVk, framebufferVk, VK_ACCESS_SHADER_READ_BIT,
+ bufferHelper.onWrite(contextVk, recorder, VK_ACCESS_SHADER_READ_BIT,
VK_ACCESS_SHADER_WRITE_BIT);
}
else
{
- bufferHelper.onRead(framebufferVk, VK_ACCESS_UNIFORM_READ_BIT);
+ bufferHelper.onRead(recorder, VK_ACCESS_UNIFORM_READ_BIT);
}
// 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.
- size = std::min(size > 0 ? size : (bufferHelper.getSize() - offset), blockSize);
+ // shader. That latter is only valid for UBOs, as SSBOs may have variable length arrays.
+ size = size > 0 ? size : (bufferHelper.getSize() - offset);
+ if (!isStorageBuffer)
+ {
+ size = std::min(size, blockSize);
+ }
VkDescriptorBufferInfo &bufferInfo = descriptorBufferInfo[writeCount];
@@ -1118,13 +1122,13 @@
angle::Result ProgramVk::updateUniformAndStorageBuffersDescriptorSet(
ContextVk *contextVk,
- vk::FramebufferHelper *framebufferVk)
+ vk::CommandGraphResource *recorder)
{
ANGLE_TRY(allocateDescriptorSet(contextVk, kBufferDescriptorSetIndex));
- updateBuffersDescriptorSet(contextVk, framebufferVk, mState.getUniformBlocks(),
+ updateBuffersDescriptorSet(contextVk, recorder, mState.getUniformBlocks(),
VK_DESCRIPTOR_TYPE_UNIFORM_BUFFER);
- updateBuffersDescriptorSet(contextVk, framebufferVk, mState.getShaderStorageBlocks(),
+ updateBuffersDescriptorSet(contextVk, recorder, mState.getShaderStorageBlocks(),
VK_DESCRIPTOR_TYPE_STORAGE_BUFFER);
return angle::Result::Continue;
@@ -1163,8 +1167,7 @@
mDescriptorSets[kUniformsAndXfbDescriptorSetIndex]);
}
-angle::Result ProgramVk::updateTexturesDescriptorSet(ContextVk *contextVk,
- vk::FramebufferHelper *framebuffer)
+angle::Result ProgramVk::updateTexturesDescriptorSet(ContextVk *contextVk)
{
const vk::TextureDescriptorDesc &texturesDesc = contextVk->getActiveTexturesDesc();
@@ -1272,6 +1275,9 @@
}
}
+ const VkPipelineBindPoint pipelineBindPoint =
+ mState.isCompute() ? VK_PIPELINE_BIND_POINT_COMPUTE : VK_PIPELINE_BIND_POINT_GRAPHICS;
+
for (size_t descriptorSetIndex = 0; descriptorSetIndex < descriptorSetRange;
++descriptorSetIndex)
{
@@ -1305,7 +1311,7 @@
descriptorSetIndex == kUniformsAndXfbDescriptorSetIndex ? mDynamicBufferOffsets.size()
: 0;
- commandBuffer->bindDescriptorSets(mPipelineLayout.get(), VK_PIPELINE_BIND_POINT_GRAPHICS,
+ commandBuffer->bindDescriptorSets(mPipelineLayout.get(), pipelineBindPoint,
descriptorSetIndex, 1, &descSet, uniformBlockOffsetCount,
mDynamicBufferOffsets.data());
}
diff --git a/src/libANGLE/renderer/vulkan/ProgramVk.h b/src/libANGLE/renderer/vulkan/ProgramVk.h
index 2b5c80f..885755e 100644
--- a/src/libANGLE/renderer/vulkan/ProgramVk.h
+++ b/src/libANGLE/renderer/vulkan/ProgramVk.h
@@ -106,10 +106,9 @@
// Also initializes the pipeline layout, descriptor set layouts, and used descriptor ranges.
angle::Result updateUniforms(ContextVk *contextVk);
- angle::Result updateTexturesDescriptorSet(ContextVk *contextVk,
- vk::FramebufferHelper *framebuffer);
+ angle::Result updateTexturesDescriptorSet(ContextVk *contextVk);
angle::Result updateUniformAndStorageBuffersDescriptorSet(ContextVk *contextVk,
- vk::FramebufferHelper *framebuffer);
+ vk::CommandGraphResource *recorder);
angle::Result updateTransformFeedbackDescriptorSet(ContextVk *contextVk,
vk::FramebufferHelper *framebuffer);
@@ -138,7 +137,7 @@
vk::PipelineHelper **pipelineOut)
{
vk::ShaderProgramHelper *shaderProgram;
- ANGLE_TRY(initShaders(contextVk, mode, &shaderProgram));
+ ANGLE_TRY(initGraphicsShaders(contextVk, mode, &shaderProgram));
ASSERT(shaderProgram->isGraphicsProgram());
RendererVk *renderer = contextVk->getRenderer();
return shaderProgram->getGraphicsPipeline(
@@ -147,6 +146,14 @@
mState.getAttributesTypeMask(), descPtrOut, pipelineOut);
}
+ angle::Result getComputePipeline(ContextVk *contextVk, vk::PipelineAndSerial **pipelineOut)
+ {
+ vk::ShaderProgramHelper *shaderProgram;
+ ANGLE_TRY(initComputeShader(contextVk, &shaderProgram));
+ ASSERT(!shaderProgram->isGraphicsProgram());
+ return shaderProgram->getComputePipeline(contextVk, mPipelineLayout.get(), pipelineOut);
+ }
+
// Used in testing only.
vk::DynamicDescriptorPool *getDynamicDescriptorPool(uint32_t poolIndex)
{
@@ -170,7 +177,7 @@
void updateDefaultUniformsDescriptorSet(ContextVk *contextVk);
void updateTransformFeedbackDescriptorSetImpl(ContextVk *contextVk);
void updateBuffersDescriptorSet(ContextVk *contextVk,
- vk::FramebufferHelper *framebufferVk,
+ vk::CommandGraphResource *recorder,
const std::vector<gl::InterfaceBlock> &blocks,
VkDescriptorType descriptorType);
@@ -186,26 +193,40 @@
uint32_t getUniformBlockBindingsOffset() const { return 0; }
uint32_t getStorageBlockBindingsOffset() const { return mStorageBlockBindingsOffset; }
+ class ShaderInfo;
ANGLE_INLINE angle::Result initShaders(ContextVk *contextVk,
- gl::PrimitiveMode mode,
+ bool enableLineRasterEmulation,
+ ShaderInfo *shaderInfo,
vk::ShaderProgramHelper **shaderProgramOut)
{
+ if (!shaderInfo->valid())
+ {
+ ANGLE_TRY(
+ shaderInfo->initShaders(contextVk, mShaderSources, enableLineRasterEmulation));
+ }
+
+ ASSERT(shaderInfo->valid());
+ *shaderProgramOut = &shaderInfo->getShaderProgram();
+
+ return angle::Result::Continue;
+ }
+
+ ANGLE_INLINE angle::Result initGraphicsShaders(ContextVk *contextVk,
+ gl::PrimitiveMode mode,
+ vk::ShaderProgramHelper **shaderProgramOut)
+ {
bool enableLineRasterEmulation = UseLineRaster(contextVk, mode);
ShaderInfo &shaderInfo =
enableLineRasterEmulation ? mLineRasterShaderInfo : mDefaultShaderInfo;
- if (!shaderInfo.valid())
- {
- ANGLE_TRY(shaderInfo.initShaders(contextVk, mShaderSource[gl::ShaderType::Vertex],
- mShaderSource[gl::ShaderType::Fragment],
- enableLineRasterEmulation));
- }
+ return initShaders(contextVk, enableLineRasterEmulation, &shaderInfo, shaderProgramOut);
+ }
- ASSERT(shaderInfo.valid());
- *shaderProgramOut = &shaderInfo.getShaderProgram();
-
- return angle::Result::Continue;
+ ANGLE_INLINE angle::Result initComputeShader(ContextVk *contextVk,
+ vk::ShaderProgramHelper **shaderProgramOut)
+ {
+ return initShaders(contextVk, false, &mDefaultShaderInfo, shaderProgramOut);
}
// Save and load implementation for GLES Program Binary support.
@@ -260,12 +281,15 @@
~ShaderInfo();
angle::Result initShaders(ContextVk *contextVk,
- const std::string &vertexSource,
- const std::string &fragmentSource,
+ const gl::ShaderMap<std::string> &shaderSources,
bool enableLineRasterEmulation);
void release(ContextVk *contextVk);
- ANGLE_INLINE bool valid() const { return mShaders[gl::ShaderType::Vertex].get().valid(); }
+ ANGLE_INLINE bool valid() const
+ {
+ return mShaders[gl::ShaderType::Vertex].get().valid() ||
+ mShaders[gl::ShaderType::Compute].get().valid();
+ }
vk::ShaderProgramHelper &getShaderProgram() { return mProgramHelper; }
@@ -278,7 +302,7 @@
ShaderInfo mLineRasterShaderInfo;
// We keep the translated linked shader sources to use with shader draw call patching.
- gl::ShaderMap<std::string> mShaderSource;
+ gl::ShaderMap<std::string> mShaderSources;
// Storage buffers are placed after uniform buffers in their descriptor set. This cached value
// contains the offset where storage buffer bindings start.
diff --git a/src/libANGLE/renderer/vulkan/VertexArrayVk.cpp b/src/libANGLE/renderer/vulkan/VertexArrayVk.cpp
index 445470c..16a9757 100644
--- a/src/libANGLE/renderer/vulkan/VertexArrayVk.cpp
+++ b/src/libANGLE/renderer/vulkan/VertexArrayVk.cpp
@@ -304,7 +304,7 @@
0, numVertices, binding.getStride(), vertexFormat.vertexLoadFunction,
&mCurrentArrayBuffers[attribIndex],
&conversion->lastAllocationOffset));
- ANGLE_TRY(srcBuffer->unmapImpl(contextVk));
+ srcBuffer->unmapImpl(contextVk);
ASSERT(conversion->dirty);
conversion->dirty = false;
diff --git a/src/libANGLE/renderer/vulkan/vk_caps_utils.cpp b/src/libANGLE/renderer/vulkan/vk_caps_utils.cpp
index 5b4304f..d603842 100644
--- a/src/libANGLE/renderer/vulkan/vk_caps_utils.cpp
+++ b/src/libANGLE/renderer/vulkan/vk_caps_utils.cpp
@@ -35,6 +35,7 @@
ASSERT(mCurrentQueueFamilyIndex < mQueueFamilyProperties.size());
const VkQueueFamilyProperties &queueFamilyProperties =
mQueueFamilyProperties[mCurrentQueueFamilyIndex];
+ const VkPhysicalDeviceLimits &limitsVk = mPhysicalDeviceProperties.limits;
mNativeExtensions.setTextureExtensionSupport(mNativeTextureCaps);
@@ -98,12 +99,9 @@
mNativeExtensions.queryCounterBitsTimestamp = queueFamilyProperties.timestampValidBits;
mNativeExtensions.textureFilterAnisotropic =
- mPhysicalDeviceFeatures.samplerAnisotropy &&
- mPhysicalDeviceProperties.limits.maxSamplerAnisotropy > 1.0f;
+ mPhysicalDeviceFeatures.samplerAnisotropy && limitsVk.maxSamplerAnisotropy > 1.0f;
mNativeExtensions.maxTextureAnisotropy =
- mNativeExtensions.textureFilterAnisotropic
- ? mPhysicalDeviceProperties.limits.maxSamplerAnisotropy
- : 0.0f;
+ mNativeExtensions.textureFilterAnisotropic ? limitsVk.maxSamplerAnisotropy : 0.0f;
// Vulkan natively supports non power-of-two textures
mNativeExtensions.textureNPOT = true;
@@ -118,41 +116,34 @@
// https://vulkan.lunarg.com/doc/view/1.0.30.0/linux/vkspec.chunked/ch31s02.html
mNativeCaps.maxElementIndex = std::numeric_limits<GLuint>::max() - 1;
- mNativeCaps.max3DTextureSize = mPhysicalDeviceProperties.limits.maxImageDimension3D;
- mNativeCaps.max2DTextureSize = mPhysicalDeviceProperties.limits.maxImageDimension2D;
- mNativeCaps.maxArrayTextureLayers = mPhysicalDeviceProperties.limits.maxImageArrayLayers;
- mNativeCaps.maxLODBias = mPhysicalDeviceProperties.limits.maxSamplerLodBias;
- mNativeCaps.maxCubeMapTextureSize = mPhysicalDeviceProperties.limits.maxImageDimensionCube;
+ mNativeCaps.max3DTextureSize = limitsVk.maxImageDimension3D;
+ mNativeCaps.max2DTextureSize = limitsVk.maxImageDimension2D;
+ mNativeCaps.maxArrayTextureLayers = limitsVk.maxImageArrayLayers;
+ mNativeCaps.maxLODBias = limitsVk.maxSamplerLodBias;
+ mNativeCaps.maxCubeMapTextureSize = limitsVk.maxImageDimensionCube;
mNativeCaps.maxRenderbufferSize = mNativeCaps.max2DTextureSize;
- mNativeCaps.minAliasedPointSize =
- std::max(1.0f, mPhysicalDeviceProperties.limits.pointSizeRange[0]);
- mNativeCaps.maxAliasedPointSize = mPhysicalDeviceProperties.limits.pointSizeRange[1];
+ mNativeCaps.minAliasedPointSize = std::max(1.0f, limitsVk.pointSizeRange[0]);
+ mNativeCaps.maxAliasedPointSize = limitsVk.pointSizeRange[1];
mNativeCaps.minAliasedLineWidth = 1.0f;
mNativeCaps.maxAliasedLineWidth = 1.0f;
mNativeCaps.maxDrawBuffers =
- std::min<uint32_t>(mPhysicalDeviceProperties.limits.maxColorAttachments,
- mPhysicalDeviceProperties.limits.maxFragmentOutputAttachments);
- mNativeCaps.maxFramebufferWidth = mPhysicalDeviceProperties.limits.maxFramebufferWidth;
- mNativeCaps.maxFramebufferHeight = mPhysicalDeviceProperties.limits.maxFramebufferHeight;
- mNativeCaps.maxColorAttachments = mPhysicalDeviceProperties.limits.maxColorAttachments;
- mNativeCaps.maxViewportWidth = mPhysicalDeviceProperties.limits.maxViewportDimensions[0];
- mNativeCaps.maxViewportHeight = mPhysicalDeviceProperties.limits.maxViewportDimensions[1];
- mNativeCaps.maxSampleMaskWords = mPhysicalDeviceProperties.limits.maxSampleMaskWords;
- mNativeCaps.maxColorTextureSamples =
- mPhysicalDeviceProperties.limits.sampledImageColorSampleCounts;
- mNativeCaps.maxDepthTextureSamples =
- mPhysicalDeviceProperties.limits.sampledImageDepthSampleCounts;
- mNativeCaps.maxIntegerSamples =
- mPhysicalDeviceProperties.limits.sampledImageIntegerSampleCounts;
+ std::min<uint32_t>(limitsVk.maxColorAttachments, limitsVk.maxFragmentOutputAttachments);
+ mNativeCaps.maxFramebufferWidth = limitsVk.maxFramebufferWidth;
+ mNativeCaps.maxFramebufferHeight = limitsVk.maxFramebufferHeight;
+ mNativeCaps.maxColorAttachments = limitsVk.maxColorAttachments;
+ mNativeCaps.maxViewportWidth = limitsVk.maxViewportDimensions[0];
+ mNativeCaps.maxViewportHeight = limitsVk.maxViewportDimensions[1];
+ mNativeCaps.maxSampleMaskWords = limitsVk.maxSampleMaskWords;
+ mNativeCaps.maxColorTextureSamples = limitsVk.sampledImageColorSampleCounts;
+ mNativeCaps.maxDepthTextureSamples = limitsVk.sampledImageDepthSampleCounts;
+ mNativeCaps.maxIntegerSamples = limitsVk.sampledImageIntegerSampleCounts;
- mNativeCaps.maxVertexAttributes = mPhysicalDeviceProperties.limits.maxVertexInputAttributes;
- mNativeCaps.maxVertexAttribBindings = mPhysicalDeviceProperties.limits.maxVertexInputBindings;
- mNativeCaps.maxVertexAttribRelativeOffset =
- mPhysicalDeviceProperties.limits.maxVertexInputAttributeOffset;
- mNativeCaps.maxVertexAttribStride =
- mPhysicalDeviceProperties.limits.maxVertexInputBindingStride;
+ mNativeCaps.maxVertexAttributes = limitsVk.maxVertexInputAttributes;
+ mNativeCaps.maxVertexAttribBindings = limitsVk.maxVertexInputBindings;
+ mNativeCaps.maxVertexAttribRelativeOffset = limitsVk.maxVertexInputAttributeOffset;
+ mNativeCaps.maxVertexAttribStride = limitsVk.maxVertexInputBindingStride;
mNativeCaps.maxElementsIndices = std::numeric_limits<GLint>::max();
mNativeCaps.maxElementsVertices = std::numeric_limits<GLint>::max();
@@ -174,13 +165,23 @@
mNativeCaps.fragmentMediumpInt.setTwosComplementInt(32);
mNativeCaps.fragmentLowpInt.setTwosComplementInt(32);
+ // Compute shader limits.
+ mNativeCaps.maxComputeWorkGroupCount[0] = limitsVk.maxComputeWorkGroupCount[0];
+ mNativeCaps.maxComputeWorkGroupCount[1] = limitsVk.maxComputeWorkGroupCount[1];
+ mNativeCaps.maxComputeWorkGroupCount[2] = limitsVk.maxComputeWorkGroupCount[2];
+ mNativeCaps.maxComputeWorkGroupSize[0] = limitsVk.maxComputeWorkGroupSize[0];
+ mNativeCaps.maxComputeWorkGroupSize[1] = limitsVk.maxComputeWorkGroupSize[1];
+ mNativeCaps.maxComputeWorkGroupSize[2] = limitsVk.maxComputeWorkGroupSize[2];
+ mNativeCaps.maxComputeWorkGroupInvocations = limitsVk.maxComputeWorkGroupInvocations;
+ mNativeCaps.maxComputeSharedMemorySize = limitsVk.maxComputeSharedMemorySize;
+
// TODO(lucferron): This is something we'll need to implement custom in the back-end.
// Vulkan doesn't do any waiting for you, our back-end code is going to manage sync objects,
// and we'll have to check that we've exceeded the max wait timeout. Also, this is ES 3.0 so
// we'll defer the implementation until we tackle the next version.
// mNativeCaps.maxServerWaitTimeout
- GLuint maxUniformBlockSize = mPhysicalDeviceProperties.limits.maxUniformBufferRange;
+ GLuint maxUniformBlockSize = limitsVk.maxUniformBufferRange;
// Clamp the maxUniformBlockSize to 64KB (majority of devices support up to this size
// currently), on AMD the maxUniformBufferRange is near uint32_t max.
@@ -192,10 +193,11 @@
// Uniforms are implemented using a uniform buffer, so the max number of uniforms we can
// support is the max buffer range divided by the size of a single uniform (4X float).
mNativeCaps.maxVertexUniformVectors = maxUniformVectors;
- mNativeCaps.maxShaderUniformComponents[gl::ShaderType::Vertex] = maxUniformComponents;
mNativeCaps.maxFragmentUniformVectors = maxUniformVectors;
mNativeCaps.maxFragmentInputComponents = maxUniformComponents;
+ mNativeCaps.maxShaderUniformComponents[gl::ShaderType::Vertex] = maxUniformComponents;
mNativeCaps.maxShaderUniformComponents[gl::ShaderType::Fragment] = maxUniformComponents;
+ mNativeCaps.maxShaderUniformComponents[gl::ShaderType::Compute] = maxUniformComponents;
// Every stage has 1 reserved uniform buffer for the default uniforms, and 1 for the driver
// uniforms.
@@ -205,41 +207,33 @@
kReservedDriverUniformBindingCount + kReservedDefaultUniformBindingCount;
const uint32_t maxPerStageUniformBuffers =
- mPhysicalDeviceProperties.limits.maxPerStageDescriptorUniformBuffers -
- kTotalReservedPerStageUniformBuffers;
+ limitsVk.maxPerStageDescriptorUniformBuffers - kTotalReservedPerStageUniformBuffers;
const uint32_t maxCombinedUniformBuffers =
- mPhysicalDeviceProperties.limits.maxDescriptorSetUniformBuffers -
- kTotalReservedUniformBuffers;
+ limitsVk.maxDescriptorSetUniformBuffers - kTotalReservedUniformBuffers;
mNativeCaps.maxShaderUniformBlocks[gl::ShaderType::Vertex] = maxPerStageUniformBuffers;
mNativeCaps.maxShaderUniformBlocks[gl::ShaderType::Fragment] = maxPerStageUniformBuffers;
+ mNativeCaps.maxShaderUniformBlocks[gl::ShaderType::Compute] = maxPerStageUniformBuffers;
mNativeCaps.maxCombinedUniformBlocks = maxCombinedUniformBuffers;
mNativeCaps.maxUniformBufferBindings = maxCombinedUniformBuffers;
mNativeCaps.maxUniformBlockSize = maxUniformBlockSize;
mNativeCaps.uniformBufferOffsetAlignment =
- static_cast<GLuint>(mPhysicalDeviceProperties.limits.minUniformBufferOffsetAlignment);
+ static_cast<GLuint>(limitsVk.minUniformBufferOffsetAlignment);
// Note that Vulkan currently implements textures as combined image+samplers, so the limit is
// the minimum of supported samplers and sampled images.
- const uint32_t maxPerStageTextures =
- std::min(mPhysicalDeviceProperties.limits.maxPerStageDescriptorSamplers,
- mPhysicalDeviceProperties.limits.maxPerStageDescriptorSampledImages);
+ const uint32_t maxPerStageTextures = std::min(limitsVk.maxPerStageDescriptorSamplers,
+ limitsVk.maxPerStageDescriptorSampledImages);
const uint32_t maxCombinedTextures =
- std::min(mPhysicalDeviceProperties.limits.maxDescriptorSetSamplers,
- mPhysicalDeviceProperties.limits.maxDescriptorSetSampledImages);
+ std::min(limitsVk.maxDescriptorSetSamplers, limitsVk.maxDescriptorSetSampledImages);
mNativeCaps.maxShaderTextureImageUnits[gl::ShaderType::Vertex] = maxPerStageTextures;
mNativeCaps.maxShaderTextureImageUnits[gl::ShaderType::Fragment] = maxPerStageTextures;
+ mNativeCaps.maxShaderTextureImageUnits[gl::ShaderType::Compute] = maxPerStageTextures;
mNativeCaps.maxCombinedTextureImageUnits = maxCombinedTextures;
- const uint32_t maxPerStageStorageBuffers =
- mPhysicalDeviceProperties.limits.maxPerStageDescriptorStorageBuffers;
- const uint32_t maxCombinedStorageBuffers =
- mPhysicalDeviceProperties.limits.maxDescriptorSetStorageBuffers;
- mNativeCaps.maxShaderStorageBlocks[gl::ShaderType::Vertex] =
- mPhysicalDeviceFeatures.vertexPipelineStoresAndAtomics ? maxPerStageStorageBuffers : 0;
- mNativeCaps.maxShaderStorageBlocks[gl::ShaderType::Fragment] =
- mPhysicalDeviceFeatures.fragmentStoresAndAtomics ? maxPerStageStorageBuffers : 0;
- mNativeCaps.maxCombinedShaderStorageBlocks = maxCombinedStorageBuffers;
+ uint32_t maxPerStageStorageBuffers = limitsVk.maxPerStageDescriptorStorageBuffers;
+ uint32_t maxVertexStageStorageBuffers = maxPerStageStorageBuffers;
+ uint32_t maxCombinedStorageBuffers = limitsVk.maxDescriptorSetStorageBuffers;
// A number of storage buffer slots are used in the vertex shader to emulate transform feedback.
// Note that Vulkan requires maxPerStageDescriptorStorageBuffers to be at least 4 (i.e. the same
@@ -253,17 +247,26 @@
"Limit to ES2.0 if supported SSBO count < supporting transform feedback buffer count");
if (mPhysicalDeviceFeatures.vertexPipelineStoresAndAtomics)
{
- ASSERT(maxPerStageStorageBuffers >= gl::IMPLEMENTATION_MAX_TRANSFORM_FEEDBACK_BUFFERS);
- mNativeCaps.maxShaderStorageBlocks[gl::ShaderType::Vertex] -=
- gl::IMPLEMENTATION_MAX_TRANSFORM_FEEDBACK_BUFFERS;
- mNativeCaps.maxCombinedShaderStorageBlocks -=
- gl::IMPLEMENTATION_MAX_TRANSFORM_FEEDBACK_BUFFERS;
+ ASSERT(maxVertexStageStorageBuffers >= gl::IMPLEMENTATION_MAX_TRANSFORM_FEEDBACK_BUFFERS);
+ maxVertexStageStorageBuffers -= gl::IMPLEMENTATION_MAX_TRANSFORM_FEEDBACK_BUFFERS;
+ maxCombinedStorageBuffers -= gl::IMPLEMENTATION_MAX_TRANSFORM_FEEDBACK_BUFFERS;
+
+ // Cap the per-stage limit of the other stages to the combined limit, in case the combined
+ // limit is now lower than that.
+ maxPerStageStorageBuffers = std::min(maxPerStageStorageBuffers, maxCombinedStorageBuffers);
}
+ mNativeCaps.maxShaderStorageBlocks[gl::ShaderType::Vertex] =
+ mPhysicalDeviceFeatures.vertexPipelineStoresAndAtomics ? maxVertexStageStorageBuffers : 0;
+ mNativeCaps.maxShaderStorageBlocks[gl::ShaderType::Fragment] =
+ mPhysicalDeviceFeatures.fragmentStoresAndAtomics ? maxPerStageStorageBuffers : 0;
+ mNativeCaps.maxShaderStorageBlocks[gl::ShaderType::Compute] = maxPerStageStorageBuffers;
+ mNativeCaps.maxCombinedShaderStorageBlocks = maxCombinedStorageBuffers;
+
mNativeCaps.maxShaderStorageBufferBindings = maxCombinedStorageBuffers;
- mNativeCaps.maxShaderStorageBlockSize = mPhysicalDeviceProperties.limits.maxStorageBufferRange;
+ mNativeCaps.maxShaderStorageBlockSize = limitsVk.maxStorageBufferRange;
mNativeCaps.shaderStorageBufferOffsetAlignment =
- static_cast<GLuint>(mPhysicalDeviceProperties.limits.minStorageBufferOffsetAlignment);
+ static_cast<GLuint>(limitsVk.minStorageBufferOffsetAlignment);
mNativeCaps.minProgramTexelOffset = mPhysicalDeviceProperties.limits.minTexelOffset;
mNativeCaps.maxProgramTexelOffset = mPhysicalDeviceProperties.limits.maxTexelOffset;
@@ -274,7 +277,7 @@
const uint32_t maxCombinedUniformComponents =
(maxPerStageUniformBuffers + kReservedPerStageDefaultUniformBindingCount) *
maxUniformComponents;
- for (gl::ShaderType shaderType : gl::kAllGraphicsShaderTypes)
+ for (gl::ShaderType shaderType : gl::AllShaderTypes())
{
mNativeCaps.maxCombinedShaderUniformComponents[shaderType] = maxCombinedUniformComponents;
}
@@ -294,8 +297,8 @@
// which total a minimum of 44 resources, so no underflow is possible here. Limit the total
// number of resources reported by Vulkan to 2 billion though to avoid seeing negative numbers
// in applications that take the value as signed int (including dEQP).
- const uint32_t maxPerStageResources = std::min<uint32_t>(
- std::numeric_limits<int32_t>::max(), mPhysicalDeviceProperties.limits.maxPerStageResources);
+ const uint32_t maxPerStageResources =
+ std::min<uint32_t>(std::numeric_limits<int32_t>::max(), limitsVk.maxPerStageResources);
mNativeCaps.maxCombinedShaderOutputResources =
maxPerStageResources - kReservedPerStageBindingCount;
@@ -308,7 +311,7 @@
// often crash. Reserving an additional varying just for them bringing the total to 2.
constexpr GLint kReservedVaryingCount = 2;
mNativeCaps.maxVaryingVectors =
- (mPhysicalDeviceProperties.limits.maxVertexOutputComponents / 4) - kReservedVaryingCount;
+ (limitsVk.maxVertexOutputComponents / 4) - kReservedVaryingCount;
mNativeCaps.maxVertexOutputComponents = mNativeCaps.maxVaryingVectors * 4;
mNativeCaps.maxTransformFeedbackInterleavedComponents =
@@ -318,17 +321,16 @@
mNativeCaps.maxTransformFeedbackSeparateComponents =
gl::IMPLEMENTATION_MAX_TRANSFORM_FEEDBACK_SEPARATE_COMPONENTS;
- mNativeCaps.minProgramTexelOffset = mPhysicalDeviceProperties.limits.minTexelOffset;
- mNativeCaps.maxProgramTexelOffset = mPhysicalDeviceProperties.limits.maxTexelOffset;
+ mNativeCaps.minProgramTexelOffset = limitsVk.minTexelOffset;
+ mNativeCaps.maxProgramTexelOffset = limitsVk.maxTexelOffset;
- const VkPhysicalDeviceLimits &limits = mPhysicalDeviceProperties.limits;
- const uint32_t sampleCounts = limits.framebufferColorSampleCounts &
- limits.framebufferDepthSampleCounts &
- limits.framebufferStencilSampleCounts;
+ const uint32_t sampleCounts = limitsVk.framebufferColorSampleCounts &
+ limitsVk.framebufferDepthSampleCounts &
+ limitsVk.framebufferStencilSampleCounts;
mNativeCaps.maxSamples = vk_gl::GetMaxSampleCount(sampleCounts);
- mNativeCaps.subPixelBits = mPhysicalDeviceProperties.limits.subPixelPrecisionBits;
+ mNativeCaps.subPixelBits = limitsVk.subPixelPrecisionBits;
}
namespace egl_vk
diff --git a/src/libANGLE/renderer/vulkan/vk_helpers.cpp b/src/libANGLE/renderer/vulkan/vk_helpers.cpp
index b7104ca..53b240f 100644
--- a/src/libANGLE/renderer/vulkan/vk_helpers.cpp
+++ b/src/libANGLE/renderer/vulkan/vk_helpers.cpp
@@ -1136,7 +1136,7 @@
ANGLE_TRY(streamIndices(contextVk, glIndexType, indexCount,
static_cast<const uint8_t *>(srcDataMapping) + elementArrayOffset,
bufferOut, bufferOffsetOut, indexCountOut));
- ANGLE_TRY(elementArrayBufferVk->unmapImpl(contextVk));
+ elementArrayBufferVk->unmapImpl(contextVk);
return angle::Result::Continue;
}
@@ -2628,6 +2628,11 @@
contextVk->releaseObject(getStoredQueueSerial(), &mFramebuffer);
}
+// FramebufferHelper implementation.
+DispatchHelper::DispatchHelper() : CommandGraphResource(CommandGraphResourceType::Dispatcher) {}
+
+DispatchHelper::~DispatchHelper() = default;
+
// ShaderProgramHelper implementation.
ShaderProgramHelper::ShaderProgramHelper() = default;
diff --git a/src/libANGLE/renderer/vulkan/vk_helpers.h b/src/libANGLE/renderer/vulkan/vk_helpers.h
index 5bd1d02..7f17692 100644
--- a/src/libANGLE/renderer/vulkan/vk_helpers.h
+++ b/src/libANGLE/renderer/vulkan/vk_helpers.h
@@ -970,6 +970,15 @@
Framebuffer mFramebuffer;
};
+// A special command graph resource to hold resource dependencies for dispatch calls. It's the
+// equivalent of FramebufferHelper, though it doesn't contain a Vulkan object.
+class DispatchHelper : public CommandGraphResource
+{
+ public:
+ DispatchHelper();
+ ~DispatchHelper() override;
+};
+
class ShaderProgramHelper : angle::NonCopyable
{
public:
diff --git a/src/tests/deqp_support/deqp_gles31_test_expectations.txt b/src/tests/deqp_support/deqp_gles31_test_expectations.txt
index cc5cc1e..32372f4 100644
--- a/src/tests/deqp_support/deqp_gles31_test_expectations.txt
+++ b/src/tests/deqp_support/deqp_gles31_test_expectations.txt
@@ -627,17 +627,17 @@
// GL_MIN/MAX_PROGRAM_TEXTURE_GATHER_OFFSET not set.
3605 VULKAN : dEQP-GLES31.functional.texture.gather.offset.* = FAIL
-// Compute shaders:
-3562 VULKAN : dEQP-GLES31.functional.shaders.builtin_var.compute.* = FAIL
-3562 VULKAN : dEQP-GLES31.functional.compute.* = FAIL
-3566 VULKAN : dEQP-GLES31.functional.ssbo.* = FAIL
-3561 VULKAN : dEQP-GLES31.functional.synchronization.*.ssbo* = FAIL
-3562 VULKAN : dEQP-GLES31.functional.shaders.builtin_functions.common.*compute = FAIL
-3562 VULKAN : dEQP-GLES31.functional.shaders.builtin_functions.precision*compute* = FAIL
-3563 VULKAN : dEQP-GLES31.functional.state_query.*compute* = SKIP
-3562 VULKAN : dEQP-GLES31.functional.state_query.program.compute_work_group_size_get_programiv = FAIL
-3562 VULKAN : dEQP-GLES31.functional.program_interface_query.buffer_limited_query.resource_*query = FAIL
-3562 VULKAN : dEQP-GLES31.functional.program_interface_query.program_*.resource_list.compute.empty = FAIL
+// Front-end query bugs:
+3520 VULKAN : dEQP-GLES31.functional.program_interface_query.buffer_limited_query.resource_*query = FAIL
+3520 VULKAN : dEQP-GLES31.functional.program_interface_query.program_*.resource_list.compute.empty = FAIL
+3520 VULKAN : dEQP-GLES31.functional.program_interface_query.shader_storage_block.buffer_data_size.* = FAIL
+
+// glMemoryBarrier support:
+3574 VULKAN : dEQP-GLES31.functional.compute.*barrier* = SKIP
+3574 VULKAN : dEQP-GLES31.functional.synchronization.*memory_barrier* = SKIP
+
+// Indirect dispatch:
+3601 VULKAN : dEQP-GLES31.functional.compute.*indirect* = SKIP
// Shader support:
3569 VULKAN : dEQP-GLES31.functional.shaders.builtin_functions.common.frexp.* = FAIL
@@ -655,10 +655,14 @@
3569 VULKAN : dEQP-GLES31.functional.shaders.builtin_constants.core.min_program_texel_offset = FAIL
3569 VULKAN : dEQP-GLES31.functional.shaders.uniform_block.es31.valid.* = FAIL
+// SSBO and Image qualifiers:
+3602 VULKAN : dEQP-GLES31.functional.synchronization.in_invocation.ssbo_alias_overwrite = FAIL
+
// Array of array and struct default uniforms:
3604 VULKAN : dEQP-GLES31.functional.program_interface_query.*float_struct = FAIL
+3604 VULKAN : dEQP-GLES31.functional.program_interface_query.*float_struct_struct = FAIL
3604 VULKAN : dEQP-GLES31.functional.program_interface_query.*random* = FAIL
-3604 VULKAN : dEQP-GLES31.functional.program_interface_query.program_output.* = SKIP
+3604 VULKAN : dEQP-GLES31.functional.program_interface_query.*array*array* = SKIP
// Block name matching failure:
3459 VULKAN : dEQP-GLES31.functional.shaders.linkage.es31.shader_storage_block.mismatch_with_and_without_instance_name = FAIL
@@ -674,10 +678,12 @@
// Atomic counters:
3566 VULKAN : dEQP-GLES31.functional.*atomic_counter* = FAIL
+3566 VULKAN : dEQP-GLES31.functional.ssbo.layout.* = FAIL
// Storage image:
3563 VULKAN : dEQP-GLES31.functional.state_query.*image* = FAIL
3563 VULKAN : dEQP-GLES31.functional.synchronization.*.image* = FAIL
+3563 VULKAN : dEQP-GLES31.functional.compute.*image* = FAIL
// Framebuffer parameters (FRAMEBUFFER_DEFAULT_SAMPLES):
3520 VULKAN : dEQP-GLES31.functional.state_query.framebuffer_default.framebuffer_default_samples_get_framebuffer_parameteriv = FAIL
diff --git a/src/tests/deqp_support/deqp_khr_gles31_test_expectations.txt b/src/tests/deqp_support/deqp_khr_gles31_test_expectations.txt
index a1de85c..d0aab38 100644
--- a/src/tests/deqp_support/deqp_khr_gles31_test_expectations.txt
+++ b/src/tests/deqp_support/deqp_khr_gles31_test_expectations.txt
@@ -36,12 +36,12 @@
// General Vulkan expectations
// Limits:
-// GL_MIN/MAX_PROGRAM_TEXTURE_GATHER_OFFSET not set.
-3605 VULKAN : KHR-GLES31.core.texture_gather* = FAIL
+// GL_MIN/MAX_PROGRAM_TEXTURE_GATHER_OFFSET not set. Also crashes on memory barrier support missing.
+3605 VULKAN : KHR-GLES31.core.texture_gather.* = SKIP
-// Compute shaders
-3562 VULKAN : KHR-GLES31.core.constant_expressions.* = SKIP
-3520 VULKAN : KHR-GLES31.core.compute_shader* = SKIP
+// Memory barriers
+3574 VULKAN : KHR-GLES31.core.compute_shader* = SKIP
+3574 VULKAN : KHR-GLES31.core.shader_atomic_counters.basic-glsl-built-in = SKIP
// Multisampled textures:
3565 VULKAN : KHR-GLES31.core.texture_storage_multisample.* = SKIP
@@ -67,14 +67,21 @@
3570 VULKAN : KHR-GLES31.core.sepshaderobjs* = FAIL
// Shader support:
-3569 VULKAN : KHR-GLES31.core.shader_bitfield_operation* = FAIL
3569 VULKAN : KHR-GLES31.core.shader_integer_mix.* = FAIL
3569 VULKAN : KHR-GLES31.core.shader_image* = SKIP
3569 VULKAN : KHR-GLES31.core.shader_macros* = FAIL
+3569 VULKAN : KHR-GLES31.core.shader_bitfield_operation.frexp.* = SKIP
+3569 VULKAN : KHR-GLES31.core.shader_bitfield_operation.uaddCarry.* = SKIP
+3569 VULKAN : KHR-GLES31.core.shader_bitfield_operation.usubBorrow.* = SKIP
+3569 VULKAN : KHR-GLES31.core.shader_bitfield_operation.umulExtended.* = SKIP
+3569 VULKAN : KHR-GLES31.core.shader_bitfield_operation.imulExtended.* = SKIP
/// Crash due to missing unsupported check in dEQP
3571 VULKAN : KHR-GLES31.core.shader_macros*_geometry = SKIP
3571 VULKAN : KHR-GLES31.core.shader_macros*_tess_control = SKIP
3571 VULKAN : KHR-GLES31.core.shader_macros*_tess_eval = SKIP
+3571 VULKAN : KHR-GLES31.core.constant_expressions.*geometry = SKIP
+3571 VULKAN : KHR-GLES31.core.constant_expressions.*tess_control = SKIP
+3571 VULKAN : KHR-GLES31.core.constant_expressions.*tess_eval = SKIP
3520 VULKAN : KHR-GLES31.core.vertex_attrib_binding* = SKIP
3520 VULKAN : KHR-GLES31.core.internalformat.texture2d.* = FAIL
@@ -85,7 +92,8 @@
// Draw indirect:
3564 VULKAN : KHR-GLES31.core.draw_indirect.* = SKIP
-3520 VULKAN : KHR-GLES31.core.explicit_uniform_location.* = FAIL
+// Explicit uniform locations:
+3597 VULKAN : KHR-GLES31.core.explicit_uniform_location.* = SKIP
3520 VULKAN : KHR-GLES31.core.program_interface_query.* = SKIP
@@ -97,6 +105,10 @@
// Blend equations:
3586 VULKAN : KHR-GLES31.core.blend_equation_advanced.* = SKIP
+// Storage image:
+3563 VULKAN : KHR-GLES31.core.layout_binding.sampler2D_layout_binding_texture_ComputeShader = FAIL
+3563 VULKAN : KHR-GLES31.core.layout_binding.block_layout_binding_block_ComputeShader = FAIL
+
3520 VULKAN : KHR-GLES31.core.internalformat.copy_tex_image* = FAIL
3520 VULKAN : KHR-GLES31.core.internalformat.renderbuffer* = FAIL
diff --git a/src/tests/gl_tests/ShaderStorageBufferTest.cpp b/src/tests/gl_tests/ShaderStorageBufferTest.cpp
index ef5dc31..bcfd234 100644
--- a/src/tests/gl_tests/ShaderStorageBufferTest.cpp
+++ b/src/tests/gl_tests/ShaderStorageBufferTest.cpp
@@ -315,6 +315,9 @@
// Tests modifying an existing shader storage buffer
TEST_P(ShaderStorageBufferTest31, ShaderStorageBufferReadWriteSame)
{
+ // Vulkan doesn't support memory barriers yet. http://anglebug.com/3574
+ ANGLE_SKIP_TEST_IF(IsVulkan());
+
constexpr char kComputeShaderSource[] =
R"(#version 310 es
layout(local_size_x=1, local_size_y=1, local_size_z=1) in;
@@ -410,6 +413,9 @@
// Tests reading and writing to a shader storage buffer bound at an offset.
TEST_P(ShaderStorageBufferTest31, ShaderStorageBufferReadWriteOffset)
{
+ // Vulkan doesn't support memory barriers yet. http://anglebug.com/3574
+ ANGLE_SKIP_TEST_IF(IsVulkan());
+
constexpr char kCS[] = R"(#version 310 es
layout(local_size_x=1, local_size_y=1, local_size_z=1) in;
@@ -1593,8 +1599,10 @@
// http://anglebug.com/1951
ANGLE_SKIP_TEST_IF(IsIntel() && IsLinux());
- // Seems to fail on Windows NVIDIA GL when tests are run without interruption.
+ // Seems to fail on Windows NVIDIA GL when tests are run without interruption. It also happens
+ // on Vulkan. http://anglebug.com/3694
ANGLE_SKIP_TEST_IF(IsWindows() && IsNVIDIA() && IsOpenGL());
+ ANGLE_SKIP_TEST_IF(IsWindows() && IsVulkan());
constexpr char kComputeShaderSource[] = R"(#version 310 es
layout (local_size_x=1) in;
@@ -2072,6 +2080,10 @@
EXPECT_GL_NO_ERROR();
}
-ANGLE_INSTANTIATE_TEST(ShaderStorageBufferTest31, ES31_OPENGL(), ES31_OPENGLES(), ES31_D3D11());
+ANGLE_INSTANTIATE_TEST(ShaderStorageBufferTest31,
+ ES31_OPENGL(),
+ ES31_OPENGLES(),
+ ES31_D3D11(),
+ ES31_VULKAN());
} // namespace