Vulkan: Store ProgramVk shaders in ShaderInfo.

This encapsulates most of the logic for a Program into a helper class.
Now we can store multiple instances of the Program's back-end to
implement different shader behaviour at draw time.

This will be useful for shader patching for OpenGL line segment raster.

Bug: angleproject:2598
Change-Id: I800a737088574e28f3a4ec23b91c0cb2647e4e12
Reviewed-on: https://chromium-review.googlesource.com/1127302
Reviewed-by: Jamie Madill <jmadill@chromium.org>
Reviewed-by: Geoff Lang <geofflang@chromium.org>
Commit-Queue: Jamie Madill <jmadill@chromium.org>
diff --git a/src/libANGLE/renderer/vulkan/ProgramVk.cpp b/src/libANGLE/renderer/vulkan/ProgramVk.cpp
index 07be0bd..15fa0ed 100644
--- a/src/libANGLE/renderer/vulkan/ProgramVk.cpp
+++ b/src/libANGLE/renderer/vulkan/ProgramVk.cpp
@@ -26,13 +26,11 @@
 
 constexpr size_t kUniformBlockDynamicBufferMinSize = 256 * 128;
 
-void InitDefaultUniformBlock(const gl::Context *context,
+void InitDefaultUniformBlock(const std::vector<sh::Uniform> &uniforms,
                              gl::Shader *shader,
                              sh::BlockLayoutMap *blockLayoutMapOut,
                              size_t *blockSizeOut)
 {
-    const auto &uniforms = shader->getUniforms(context);
-
     if (uniforms.empty())
     {
         *blockSizeOut = 0;
@@ -132,37 +130,70 @@
 }
 }  // anonymous namespace
 
+// ProgramVk::ShaderInfo implementation.
+ProgramVk::ShaderInfo::ShaderInfo()
+{
+}
+
+ProgramVk::ShaderInfo::~ShaderInfo() = default;
+
+angle::Result ProgramVk::ShaderInfo::getShaders(
+    ContextVk *contextVk,
+    const std::string &vertexSource,
+    const std::string &fragmentSource,
+    const vk::ShaderAndSerial **vertexShaderAndSerialOut,
+    const vk::ShaderAndSerial **fragmentShaderAndSerialOut)
+{
+    if (!valid())
+    {
+        std::vector<uint32_t> vertexCode;
+        std::vector<uint32_t> fragmentCode;
+        ANGLE_TRY(GlslangWrapper::GetShaderCode(contextVk, contextVk->getCaps(), vertexSource,
+                                                fragmentSource, &vertexCode, &fragmentCode));
+
+        ANGLE_TRY(vk::InitShaderAndSerial(contextVk, &mVertexShaderAndSerial, vertexCode.data(),
+                                          vertexCode.size() * sizeof(uint32_t)));
+        ANGLE_TRY(vk::InitShaderAndSerial(contextVk, &mFragmentShaderAndSerial, fragmentCode.data(),
+                                          fragmentCode.size() * sizeof(uint32_t)));
+    }
+
+    *fragmentShaderAndSerialOut = &mFragmentShaderAndSerial;
+    *vertexShaderAndSerialOut   = &mVertexShaderAndSerial;
+    return angle::Result::Continue();
+}
+
+void ProgramVk::ShaderInfo::destroy(VkDevice device)
+{
+    mVertexShaderAndSerial.destroy(device);
+    mFragmentShaderAndSerial.destroy(device);
+}
+
+bool ProgramVk::ShaderInfo::valid() const
+{
+    return mVertexShaderAndSerial.valid();
+}
+
+// ProgramVk implementation.
 ProgramVk::DefaultUniformBlock::DefaultUniformBlock()
     : storage(VK_BUFFER_USAGE_UNIFORM_BUFFER_BIT | VK_BUFFER_USAGE_TRANSFER_SRC_BIT,
               kUniformBlockDynamicBufferMinSize),
-      uniformData(),
-      uniformsDirty(false),
-      uniformLayout()
+      uniformsDirty(false)
 {
 }
 
-ProgramVk::DefaultUniformBlock::~DefaultUniformBlock()
-{
-}
+ProgramVk::DefaultUniformBlock::~DefaultUniformBlock() = default;
 
 ProgramVk::ProgramVk(const gl::ProgramState &state)
-    : ProgramImpl(state),
-      mDefaultUniformBlocks(),
-      mUniformBlocksOffsets(),
-      mUsedDescriptorSetRange(),
-      mDirtyTextures(true)
+    : ProgramImpl(state), mUniformBlocksOffsets{}, mDirtyTextures(false)
 {
-    mUniformBlocksOffsets.fill(0);
     mUsedDescriptorSetRange.invalidate();
 }
 
-ProgramVk::~ProgramVk()
-{
-}
+ProgramVk::~ProgramVk() = default;
 
-gl::Error ProgramVk::destroy(const gl::Context *contextImpl)
+gl::Error ProgramVk::destroy(const gl::Context *context)
 {
-    ContextVk *contextVk = vk::GetImpl(contextImpl);
+    ContextVk *contextVk = vk::GetImpl(context);
     return reset(contextVk);
 }
 
@@ -182,13 +213,13 @@
         uniformBlock.storage.release(renderer);
     }
 
+    // TODO(jmadill): Line rasterization emulation shaders. http://anglebug.com/2598
+    mDefaultShaderInfo.destroy(device);
+
     Serial currentSerial = renderer->getCurrentQueueSerial();
     renderer->releaseObject(currentSerial, &mEmptyUniformBlockStorage.memory);
     renderer->releaseObject(currentSerial, &mEmptyUniformBlockStorage.buffer);
 
-    mDefaultVertexShaderAndSerial.destroy(device);
-    mDefaultFragmentShaderAndSerial.destroy(device);
-
     mDescriptorSets.clear();
     mUsedDescriptorSetRange.invalidate();
     mDirtyTextures = false;
@@ -196,7 +227,7 @@
     return angle::Result::Continue();
 }
 
-gl::LinkResult ProgramVk::load(const gl::Context *contextImpl,
+gl::LinkResult ProgramVk::load(const gl::Context *context,
                                gl::InfoLog &infoLog,
                                gl::BinaryInputStream *stream)
 {
@@ -228,25 +259,7 @@
 
     ANGLE_TRY(reset(contextVk));
 
-    std::string vertexSource;
-    std::string fragmentSource;
-    GlslangWrapper::GetShaderSource(glContext, mState, resources, &vertexSource, &fragmentSource);
-
-    std::vector<uint32_t> vertexCode;
-    std::vector<uint32_t> fragmentCode;
-    bool linkSuccess = false;
-    ANGLE_TRY_RESULT(GlslangWrapper::GetShaderCode(glContext->getCaps(), vertexSource,
-                                                   fragmentSource, &vertexCode, &fragmentCode),
-                     linkSuccess);
-    if (!linkSuccess)
-    {
-        return false;
-    }
-
-    ANGLE_TRY(vk::InitShaderAndSerial(contextVk, &mDefaultVertexShaderAndSerial, vertexCode.data(),
-                                      vertexCode.size() * sizeof(uint32_t)));
-    ANGLE_TRY(vk::InitShaderAndSerial(contextVk, &mDefaultFragmentShaderAndSerial,
-                                      fragmentCode.data(), fragmentCode.size() * sizeof(uint32_t)));
+    GlslangWrapper::GetShaderSource(glContext, mState, resources, &mVertexSource, &mFragmentSource);
 
     ANGLE_TRY(initDefaultUniformBlocks(glContext));
 
@@ -298,10 +311,28 @@
     ANGLE_TRY(renderer->getPipelineLayout(contextVk, pipelineLayoutDesc, mDescriptorSetLayouts,
                                           &mPipelineLayout));
 
+    if (!mState.getUniforms().empty())
+    {
+        const gl::RangeUI &samplerRange = mState.getSamplerUniformRange();
+
+        if (mState.getUniforms().size() > samplerRange.length())
+        {
+            // Ensure the descriptor set range includes the uniform buffers at position 0.
+            mUsedDescriptorSetRange.extend(kUniformsDescriptorSetIndex);
+        }
+
+        if (!samplerRange.empty())
+        {
+            // Ensure the descriptor set range includes the textures at position 1.
+            mUsedDescriptorSetRange.extend(kTextureDescriptorSetIndex);
+            mDirtyTextures = true;
+        }
+    }
+
     return true;
 }
 
-gl::Error ProgramVk::initDefaultUniformBlocks(const gl::Context *glContext)
+angle::Result ProgramVk::initDefaultUniformBlocks(const gl::Context *glContext)
 {
     ContextVk *contextVk = vk::GetImpl(glContext);
     RendererVk *renderer = contextVk->getRenderer();
@@ -314,18 +345,18 @@
     for (vk::ShaderType shaderType : vk::AllShaderTypes())
     {
         gl::ShaderType glShaderType = static_cast<gl::ShaderType>(shaderType);
-        InitDefaultUniformBlock(glContext, mState.getAttachedShader(glShaderType),
-                                &layoutMap[shaderType], &requiredBufferSize[shaderType]);
+        gl::Shader *shader                       = mState.getAttachedShader(glShaderType);
+        const std::vector<sh::Uniform> &uniforms = shader->getUniforms(glContext);
+        InitDefaultUniformBlock(uniforms, shader, &layoutMap[shaderType],
+                                &requiredBufferSize[shaderType]);
     }
 
     // Init the default block layout info.
-    const auto &locations = mState.getUniformLocations();
     const auto &uniforms  = mState.getUniforms();
-    for (size_t locationIndex = 0; locationIndex < locations.size(); ++locationIndex)
+    for (const gl::VariableLocation &location : mState.getUniformLocations())
     {
         vk::ShaderMap<sh::BlockMemberInfo> layoutInfo;
 
-        const auto &location = locations[locationIndex];
         if (location.used() && !location.ignored)
         {
             const auto &uniform = uniforms[location.index];
@@ -371,7 +402,7 @@
             if (!mDefaultUniformBlocks[shaderType].uniformData.resize(
                     requiredBufferSize[shaderType]))
             {
-                return gl::OutOfMemory() << "Memory allocation failure.";
+                ANGLE_VK_CHECK(contextVk, false, VK_ERROR_OUT_OF_HOST_MEMORY);
             }
             size_t minAlignment = static_cast<size_t>(
                 renderer->getPhysicalDeviceProperties().limits.minUniformBufferOffsetAlignment);
@@ -413,12 +444,9 @@
             ANGLE_TRY(AllocateBufferMemory(contextVk, flags, &mEmptyUniformBlockStorage.buffer,
                                            &mEmptyUniformBlockStorage.memory));
         }
-
-        // Ensure the descriptor set range includes the uniform buffers at position 0.
-        mUsedDescriptorSetRange.extend(0);
     }
 
-    return gl::NoError();
+    return angle::Result::Continue();
 }
 
 GLboolean ProgramVk::validate(const gl::Caps &caps, gl::InfoLog *infoLog)
@@ -699,18 +727,20 @@
     UNIMPLEMENTED();
 }
 
-gl::Error ProgramVk::initShaders(const ContextVk *contextVk,
-                                 const gl::DrawCallParams &drawCallParams,
-                                 const vk::ShaderAndSerial **vertexShaderAndSerialOut,
-                                 const vk::ShaderAndSerial **fragmentShaderAndSerialOut)
+angle::Result ProgramVk::initShaders(ContextVk *contextVk,
+                                     const gl::DrawCallParams &drawCallParams,
+                                     const vk::ShaderAndSerial **vertexShaderAndSerialOut,
+                                     const vk::ShaderAndSerial **fragmentShaderAndSerialOut,
+                                     const vk::PipelineLayout **pipelineLayoutOut)
 {
-    // TODO(jmadill): Move more init into this method. http://anglebug.com/2598
     // TODO(jmadill): Line rasterization emulation shaders. http://anglebug.com/2598
-    ASSERT(mDefaultVertexShaderAndSerial.valid());
-    ASSERT(mDefaultFragmentShaderAndSerial.valid());
-    *vertexShaderAndSerialOut   = &mDefaultVertexShaderAndSerial;
-    *fragmentShaderAndSerialOut = &mDefaultFragmentShaderAndSerial;
-    return gl::NoError();
+    ANGLE_TRY(mDefaultShaderInfo.getShaders(contextVk, mVertexSource, mFragmentSource,
+                                            vertexShaderAndSerialOut, fragmentShaderAndSerialOut));
+    ASSERT(mDefaultShaderInfo.valid());
+
+    *pipelineLayoutOut = &mPipelineLayout.get();
+
+    return angle::Result::Continue();
 }
 
 angle::Result ProgramVk::allocateDescriptorSet(ContextVk *contextVk, uint32_t descriptorSetIndex)
@@ -755,10 +785,7 @@
         return angle::Result::Continue();
     }
 
-    ASSERT(mUsedDescriptorSetRange.contains(0));
-
     // Update buffer memory by immediate mapping. This immediate update only works once.
-    // TODO(jmadill): Handle inserting updates into the command stream, or use dynamic buffers.
     bool anyNewBufferAllocated = false;
     for (vk::ShaderType shaderType : vk::AllShaderTypes())
     {
@@ -797,9 +824,9 @@
 
     for (vk::ShaderType shaderType : vk::AllShaderTypes())
     {
-        auto &uniformBlock = mDefaultUniformBlocks[shaderType];
-        auto &bufferInfo   = descriptorBufferInfo[shaderType];
-        auto &writeInfo    = writeDescriptorInfo[shaderType];
+        DefaultUniformBlock &uniformBlock  = mDefaultUniformBlocks[shaderType];
+        VkDescriptorBufferInfo &bufferInfo = descriptorBufferInfo[shaderType];
+        VkWriteDescriptorSet &writeInfo    = writeDescriptorInfo[shaderType];
 
         if (!uniformBlock.uniformData.empty())
         {
@@ -832,34 +859,6 @@
     return angle::Result::Continue();
 }
 
-const std::vector<VkDescriptorSet> &ProgramVk::getDescriptorSets() const
-{
-    return mDescriptorSets;
-}
-
-const uint32_t *ProgramVk::getDynamicOffsets()
-{
-    // If we have no descriptor set being used, we do not need to specify any offsets when binding
-    // the descriptor sets.
-    if (!mUsedDescriptorSetRange.contains(0))
-        return nullptr;
-
-    return mUniformBlocksOffsets.data();
-}
-
-uint32_t ProgramVk::getDynamicOffsetsCount()
-{
-    if (!mUsedDescriptorSetRange.contains(0))
-        return 0;
-
-    return static_cast<uint32_t>(mUniformBlocksOffsets.size());
-}
-
-const gl::RangeUI &ProgramVk::getUsedDescriptorSetRange() const
-{
-    return mUsedDescriptorSetRange;
-}
-
 angle::Result ProgramVk::updateTexturesDescriptorSet(ContextVk *contextVk)
 {
     if (mState.getSamplerBindings().empty() || !mDirtyTextures)
@@ -929,11 +928,6 @@
     mDirtyTextures = true;
 }
 
-const vk::PipelineLayout &ProgramVk::getPipelineLayout() const
-{
-    return mPipelineLayout.get();
-}
-
 void ProgramVk::setDefaultUniformBlocksMinSizeForTesting(size_t minSize)
 {
     for (DefaultUniformBlock &block : mDefaultUniformBlocks)
@@ -941,4 +935,43 @@
         block.storage.setMinimumSizeForTesting(minSize);
     }
 }
+
+angle::Result ProgramVk::updateDescriptorSets(ContextVk *contextVk,
+                                              const gl::DrawCallParams &drawCallParams,
+                                              VkDescriptorSet driverUniformsDescriptorSet,
+                                              vk::CommandBuffer *commandBuffer)
+{
+    // TODO(jmadill): Line rasterization emulation shaders. http://anglebug.com/2598
+    // Can probably use better dirty bits here.
+    ANGLE_TRY(updateUniforms(contextVk));
+    ANGLE_TRY(updateTexturesDescriptorSet(contextVk));
+
+    commandBuffer->bindDescriptorSets(VK_PIPELINE_BIND_POINT_GRAPHICS, mPipelineLayout.get(),
+                                      kDriverUniformsDescriptorSetIndex, 1,
+                                      &driverUniformsDescriptorSet, 0, nullptr);
+
+    if (mUsedDescriptorSetRange.empty())
+        return angle::Result::Continue();
+
+    ASSERT(!mDescriptorSets.empty());
+
+    unsigned int low = mUsedDescriptorSetRange.low();
+
+    // No uniforms descriptor set means no need to specify dynamic buffer offsets.
+    if (mUsedDescriptorSetRange.contains(kUniformsDescriptorSetIndex))
+    {
+        commandBuffer->bindDescriptorSets(
+            VK_PIPELINE_BIND_POINT_GRAPHICS, mPipelineLayout.get(), low,
+            mUsedDescriptorSetRange.length(), &mDescriptorSets[low],
+            static_cast<uint32_t>(mUniformBlocksOffsets.size()), mUniformBlocksOffsets.data());
+    }
+    else
+    {
+        commandBuffer->bindDescriptorSets(VK_PIPELINE_BIND_POINT_GRAPHICS, mPipelineLayout.get(),
+                                          low, mUsedDescriptorSetRange.length(),
+                                          &mDescriptorSets[low], 0, nullptr);
+    }
+
+    return angle::Result::Continue();
+}
 }  // namespace rx