Pass texture base level to shaders on D3D11

The base level is passed to shaders in a uniform block created
specifically for passing sampler metadata. This is done on feature levels
above 9_3, which treat samplers as indices to sampler arrays in shaders.

BUG=angleproject:596
TEST=angle_end2end_tests

Change-Id: I846f2fc195ab1fd884052824ffd3c1d65083c0fb
Reviewed-on: https://chromium-review.googlesource.com/322122
Reviewed-by: Zhenyao Mo <zmo@chromium.org>
Commit-Queue: Olli Etuaho <oetuaho@nvidia.com>
diff --git a/src/libANGLE/renderer/d3d/ProgramD3D.cpp b/src/libANGLE/renderer/d3d/ProgramD3D.cpp
index bde3409..bb10266 100644
--- a/src/libANGLE/renderer/d3d/ProgramD3D.cpp
+++ b/src/libANGLE/renderer/d3d/ProgramD3D.cpp
@@ -22,6 +22,7 @@
 #include "libANGLE/renderer/d3d/ShaderExecutableD3D.h"
 #include "libANGLE/renderer/d3d/VaryingPacking.h"
 #include "libANGLE/renderer/d3d/VertexDataManager.h"
+#include "libANGLE/renderer/d3d/d3d11/renderer11_utils.h"
 
 namespace rx
 {
@@ -487,6 +488,88 @@
     return mFragmentShader;
 }
 
+// SamplerMetadataD3D11 implementation
+
+SamplerMetadataD3D11::SamplerMetadataD3D11()
+    : mSamplerCount(0), mType(gl::SAMPLER_VERTEX), mDirty(false), mSamplerMetadataBuffer(nullptr)
+{
+}
+
+SamplerMetadataD3D11::~SamplerMetadataD3D11()
+{
+    reset();
+}
+
+void SamplerMetadataD3D11::reset()
+{
+    mDirty = false;
+    SafeRelease(mSamplerMetadataBuffer);
+}
+
+void SamplerMetadataD3D11::initData(unsigned int samplerCount, gl::SamplerType type)
+{
+    mSamplerMetadata.resize(samplerCount);
+    mSamplerCount = samplerCount;
+    mType         = type;
+}
+
+void SamplerMetadataD3D11::update(unsigned int samplerIndex, unsigned int baseLevel)
+{
+    if (mSamplerMetadata[samplerIndex].baseLevel[0] != static_cast<int>(baseLevel))
+    {
+        mSamplerMetadata[samplerIndex].baseLevel[0] = static_cast<int>(baseLevel);
+        mDirty                                      = true;
+    }
+}
+
+bool SamplerMetadataD3D11::initBuffer(ID3D11Device *device, ID3D11DeviceContext *deviceContext)
+{
+    D3D11_BUFFER_DESC constantBufferDescription = {0};
+    d3d11::InitConstantBufferDesc(&constantBufferDescription,
+                                  sizeof(dx_SamplerMetadata) * mSamplerCount);
+    HRESULT result =
+        device->CreateBuffer(&constantBufferDescription, nullptr, &mSamplerMetadataBuffer);
+    ASSERT(SUCCEEDED(result));
+    if (FAILED(result))
+    {
+        return false;
+    }
+    if (mType == gl::SAMPLER_VERTEX)
+    {
+        deviceContext->VSSetConstantBuffers(d3d11::RESERVED_CONSTANT_BUFFER_SLOT_SAMPLER_METADATA,
+                                            1, &mSamplerMetadataBuffer);
+    }
+    else
+    {
+        deviceContext->PSSetConstantBuffers(d3d11::RESERVED_CONSTANT_BUFFER_SLOT_SAMPLER_METADATA,
+                                            1, &mSamplerMetadataBuffer);
+    }
+    mDirty = true;
+    return true;
+}
+
+gl::Error SamplerMetadataD3D11::apply(ID3D11Device *device, ID3D11DeviceContext *deviceContext)
+{
+    if (!mSamplerMetadataBuffer && mSamplerCount > 0)
+    {
+        if (!initBuffer(device, deviceContext))
+        {
+            return gl::Error(GL_OUT_OF_MEMORY, "Failed to create shader constant buffer");
+        }
+    }
+    if (mDirty)
+    {
+        ASSERT(mSamplerMetadataBuffer);
+        if (mSamplerMetadataBuffer)
+        {
+            deviceContext->UpdateSubresource(mSamplerMetadataBuffer, 0, nullptr,
+                                             mSamplerMetadata.data(), 16, 0);
+            mDirty = false;
+        }
+    }
+    return gl::Error(GL_NO_ERROR);
+}
+
 // ProgramD3D Implementation
 
 ProgramD3D::VertexExecutable::VertexExecutable(const gl::InputLayout &inputLayout,
@@ -654,7 +737,29 @@
     return GL_TEXTURE_2D;
 }
 
-GLint ProgramD3D::getUsedSamplerRange(gl::SamplerType type) const
+void ProgramD3D::setSamplerMetadata(gl::SamplerType type,
+                                    unsigned int samplerIndex,
+                                    unsigned int baseLevel)
+{
+    SamplerMetadataD3D11 *metadata = nullptr;
+    switch (type)
+    {
+        case gl::SAMPLER_PIXEL:
+            metadata = &mSamplerMetadataPS;
+            break;
+        case gl::SAMPLER_VERTEX:
+            metadata = &mSamplerMetadataVS;
+            break;
+        default:
+            UNREACHABLE();
+            break;
+    }
+    ASSERT(metadata != nullptr);
+    ASSERT(samplerIndex < getUsedSamplerRange(type));
+    metadata->update(samplerIndex, baseLevel);
+}
+
+GLuint ProgramD3D::getUsedSamplerRange(gl::SamplerType type) const
 {
     switch (type)
     {
@@ -664,7 +769,7 @@
             return mUsedVertexSamplerRange;
         default:
             UNREACHABLE();
-            return 0;
+            return 0u;
     }
 }
 
@@ -958,6 +1063,9 @@
     initializeUniformStorage();
     initAttributesByLayout();
 
+    mSamplerMetadataPS.initData(getUsedSamplerRange(gl::SAMPLER_PIXEL), gl::SAMPLER_PIXEL);
+    mSamplerMetadataVS.initData(getUsedSamplerRange(gl::SAMPLER_VERTEX), gl::SAMPLER_VERTEX);
+
     return LinkResult(true, gl::Error(GL_NO_ERROR));
 }
 
@@ -1440,6 +1548,9 @@
 
     defineUniformsAndAssignRegisters();
 
+    mSamplerMetadataPS.initData(getUsedSamplerRange(gl::SAMPLER_PIXEL), gl::SAMPLER_PIXEL);
+    mSamplerMetadataVS.initData(getUsedSamplerRange(gl::SAMPLER_VERTEX), gl::SAMPLER_VERTEX);
+
     gatherTransformFeedbackVaryings(varyingPacking);
 
     LinkResult result = compileProgramExecutables(data, infoLog);
@@ -1564,6 +1675,11 @@
         d3dUniform->dirty = false;
     }
 
+    error = mRenderer->applySamplerMetadata(&mSamplerMetadataVS, mUsedVertexSamplerRange,
+                                            gl::SAMPLER_VERTEX);
+    error = mRenderer->applySamplerMetadata(&mSamplerMetadataPS, mUsedPixelSamplerRange,
+                                            gl::SAMPLER_PIXEL);
+
     return gl::Error(GL_NO_ERROR);
 }
 
@@ -2140,6 +2256,9 @@
     mSamplersPS.clear();
     mSamplersVS.clear();
 
+    mSamplerMetadataPS.reset();
+    mSamplerMetadataVS.reset();
+
     mUsedVertexSamplerRange = 0;
     mUsedPixelSamplerRange  = 0;
     mDirtySamplerMapping    = true;