D3D: Split applyTextures into each back-end.

This paves the way for a dirty bits implementation for Textures, and
also will allow more precise ordering of the state update in D3D11.

The latter becomes important when moving the shader application.
Because the texture update can affect the built-in driver uniforms,
we neeed to update textures before we update the uniforms.

BUG=angleproject:1156
BUG=angleproject:1387

Change-Id: I995e095517c598d8672c6400d08a430da0e8339b
Reviewed-on: https://chromium-review.googlesource.com/580361
Reviewed-by: Geoff Lang <geofflang@chromium.org>
Commit-Queue: Jamie Madill <jmadill@chromium.org>
diff --git a/src/libANGLE/renderer/d3d/RendererD3D.cpp b/src/libANGLE/renderer/d3d/RendererD3D.cpp
index 9f624b6..c30dfbd 100644
--- a/src/libANGLE/renderer/d3d/RendererD3D.cpp
+++ b/src/libANGLE/renderer/d3d/RendererD3D.cpp
@@ -59,88 +59,6 @@
     mIncompleteTextures.clear();
 }
 
-// For each Direct3D sampler of either the pixel or vertex stage,
-// looks up the corresponding OpenGL texture image unit and texture type,
-// and sets the texture and its addressing/filtering state (or NULL when inactive).
-// Sampler mapping needs to be up-to-date on the program object before this is called.
-gl::Error RendererD3D::applyTextures(const gl::Context *context,
-                                     gl::SamplerType shaderType,
-                                     const FramebufferTextureArray &framebufferTextures,
-                                     size_t framebufferTextureCount)
-{
-    const auto &glState    = context->getGLState();
-    const auto &caps       = context->getCaps();
-    ProgramD3D *programD3D = GetImplAs<ProgramD3D>(glState.getProgram());
-
-    ASSERT(!programD3D->isSamplerMappingDirty());
-
-    // TODO(jmadill): Use the Program's sampler bindings.
-
-    unsigned int samplerRange = programD3D->getUsedSamplerRange(shaderType);
-    for (unsigned int samplerIndex = 0; samplerIndex < samplerRange; samplerIndex++)
-    {
-        GLenum textureType = programD3D->getSamplerTextureType(shaderType, samplerIndex);
-        GLint textureUnit  = programD3D->getSamplerMapping(shaderType, samplerIndex, caps);
-        if (textureUnit != -1)
-        {
-            gl::Texture *texture = glState.getSamplerTexture(textureUnit, textureType);
-            ASSERT(texture);
-
-            gl::Sampler *samplerObject = glState.getSampler(textureUnit);
-
-            const gl::SamplerState &samplerState =
-                samplerObject ? samplerObject->getSamplerState() : texture->getSamplerState();
-
-            // TODO: std::binary_search may become unavailable using older versions of GCC
-            if (texture->getTextureState().isSamplerComplete(samplerState,
-                                                             context->getContextState()) &&
-                !std::binary_search(framebufferTextures.begin(),
-                                    framebufferTextures.begin() + framebufferTextureCount, texture))
-            {
-                ANGLE_TRY(
-                    setSamplerState(context, shaderType, samplerIndex, texture, samplerState));
-                ANGLE_TRY(setTexture(context, shaderType, samplerIndex, texture));
-            }
-            else
-            {
-                // Texture is not sampler complete or it is in use by the framebuffer.  Bind the
-                // incomplete texture.
-                gl::Texture *incompleteTexture = getIncompleteTexture(context, textureType);
-
-                ANGLE_TRY(setSamplerState(context, shaderType, samplerIndex, incompleteTexture,
-                                          incompleteTexture->getSamplerState()));
-                ANGLE_TRY(setTexture(context, shaderType, samplerIndex, incompleteTexture));
-            }
-        }
-        else
-        {
-            // No texture bound to this slot even though it is used by the shader, bind a NULL
-            // texture
-            ANGLE_TRY(setTexture(context, shaderType, samplerIndex, nullptr));
-        }
-    }
-
-    // Set all the remaining textures to NULL
-    size_t samplerCount = (shaderType == gl::SAMPLER_PIXEL) ? caps.maxTextureImageUnits
-                                                            : caps.maxVertexTextureImageUnits;
-    clearTextures(context, shaderType, samplerRange, samplerCount);
-
-    return gl::NoError();
-}
-
-gl::Error RendererD3D::applyTextures(const gl::Context *context)
-{
-    FramebufferTextureArray framebufferTextures;
-    size_t framebufferSerialCount =
-        getBoundFramebufferTextures(context->getContextState(), &framebufferTextures);
-
-    ANGLE_TRY(
-        applyTextures(context, gl::SAMPLER_VERTEX, framebufferTextures, framebufferSerialCount));
-    ANGLE_TRY(
-        applyTextures(context, gl::SAMPLER_PIXEL, framebufferTextures, framebufferSerialCount));
-    return gl::NoError();
-}
-
 bool RendererD3D::skipDraw(const gl::ContextState &data, GLenum drawMode)
 {
     const gl::State &state = data.getState();
diff --git a/src/libANGLE/renderer/d3d/RendererD3D.h b/src/libANGLE/renderer/d3d/RendererD3D.h
index a4edbdc..20febc0 100644
--- a/src/libANGLE/renderer/d3d/RendererD3D.h
+++ b/src/libANGLE/renderer/d3d/RendererD3D.h
@@ -104,6 +104,8 @@
 };
 
 using AttribIndexArray = std::array<int, gl::MAX_VERTEX_ATTRIBS>;
+using FramebufferTextureArray =
+    std::array<gl::Texture *, gl::IMPLEMENTATION_MAX_FRAMEBUFFER_ATTACHMENTS>;
 
 class RendererD3D : public BufferFactoryD3D
 {
@@ -147,16 +149,6 @@
                                            HANDLE shareHandle,
                                            const egl::AttributeMap &attribs) const = 0;
 
-    virtual gl::Error setSamplerState(const gl::Context *context,
-                                      gl::SamplerType type,
-                                      int index,
-                                      gl::Texture *texture,
-                                      const gl::SamplerState &sampler) = 0;
-    virtual gl::Error setTexture(const gl::Context *context,
-                                 gl::SamplerType type,
-                                 int index,
-                                 gl::Texture *texture) = 0;
-
     virtual gl::Error setUniformBuffers(const gl::ContextState &data,
                                         const std::vector<GLint> &vertexUniformBuffers,
                                         const std::vector<GLint> &fragmentUniformBuffers) = 0;
@@ -301,12 +293,6 @@
     GLint getGPUDisjoint();
     GLint64 getTimestamp();
 
-    // In D3D11, faster than calling setTexture a jillion times
-    virtual gl::Error clearTextures(const gl::Context *context,
-                                    gl::SamplerType samplerType,
-                                    size_t rangeStart,
-                                    size_t rangeEnd) = 0;
-
     virtual gl::Error clearRenderTarget(RenderTargetD3D *renderTarget,
                                         const gl::ColorF &clearValues) = 0;
 
@@ -336,6 +322,11 @@
 
     bool isRobustResourceInitEnabled() const;
 
+    size_t getBoundFramebufferTextures(const gl::ContextState &data,
+                                       FramebufferTextureArray *outTextureArray);
+
+    gl::Texture *getIncompleteTexture(const gl::Context *context, GLenum type);
+
   protected:
     virtual bool getLUID(LUID *adapterLuid) const = 0;
     virtual void generateCaps(gl::Caps *outCaps,
@@ -347,7 +338,6 @@
 
     // dirtyPointer is a special value that will make the comparison with any valid pointer fail and force the renderer to re-apply the state.
 
-    gl::Error applyTextures(const gl::Context *context);
     bool skipDraw(const gl::ContextState &data, GLenum drawMode);
     gl::Error markTransformFeedbackUsage(const gl::ContextState &data);
 
@@ -358,17 +348,6 @@
   private:
     void ensureCapsInitialized() const;
 
-    typedef std::array<gl::Texture*, gl::IMPLEMENTATION_MAX_FRAMEBUFFER_ATTACHMENTS> FramebufferTextureArray;
-
-    gl::Error applyTextures(const gl::Context *context,
-                            gl::SamplerType shaderType,
-                            const FramebufferTextureArray &framebufferTextures,
-                            size_t framebufferTextureCount);
-
-    size_t getBoundFramebufferTextures(const gl::ContextState &data,
-                                       FramebufferTextureArray *outTextureArray);
-    gl::Texture *getIncompleteTexture(const gl::Context *context, GLenum type);
-
     virtual angle::WorkaroundsD3D generateWorkarounds() const = 0;
 
     mutable bool mCapsInitialized;
diff --git a/src/libANGLE/renderer/d3d/d3d11/Renderer11.cpp b/src/libANGLE/renderer/d3d/d3d11/Renderer11.cpp
index 45e7706..0404a0b 100644
--- a/src/libANGLE/renderer/d3d/d3d11/Renderer11.cpp
+++ b/src/libANGLE/renderer/d3d/d3d11/Renderer11.cpp
@@ -360,22 +360,6 @@
     }
 }
 
-int GetWrapBits(GLenum wrap)
-{
-    switch (wrap)
-    {
-        case GL_CLAMP_TO_EDGE:
-            return 0x1;
-        case GL_REPEAT:
-            return 0x2;
-        case GL_MIRRORED_REPEAT:
-            return 0x3;
-        default:
-            UNREACHABLE();
-            return 0;
-    }
-}
-
 const uint32_t ScratchMemoryBufferLifetime = 1000;
 
 }  // anonymous namespace
@@ -817,20 +801,6 @@
 
     mStateManager.initialize(rendererCaps);
 
-    mForceSetVertexSamplerStates.resize(rendererCaps.maxVertexTextureImageUnits);
-    mCurVertexSamplerStates.resize(rendererCaps.maxVertexTextureImageUnits);
-    mSamplerMetadataVS.initData(rendererCaps.maxVertexTextureImageUnits);
-
-    mForceSetPixelSamplerStates.resize(rendererCaps.maxTextureImageUnits);
-    mCurPixelSamplerStates.resize(rendererCaps.maxTextureImageUnits);
-    mSamplerMetadataPS.initData(rendererCaps.maxTextureImageUnits);
-
-    mForceSetComputeSamplerStates.resize(rendererCaps.maxComputeTextureImageUnits);
-    mCurComputeSamplerStates.resize(rendererCaps.maxComputeTextureImageUnits);
-    mSamplerMetadataCS.initData(rendererCaps.maxComputeTextureImageUnits);
-
-    mStateManager.initialize(rendererCaps);
-
     // No context is available here, use the proxy context in the display.
     markAllStateDirty(mDisplay->getProxyContext());
 
@@ -1404,134 +1374,6 @@
     ANGLE_TRY(generateSwizzles(context, gl::SAMPLER_PIXEL));
     return gl::NoError();
 }
-gl::Error Renderer11::setSamplerState(const gl::Context *context,
-                                      gl::SamplerType type,
-                                      int index,
-                                      gl::Texture *texture,
-                                      const gl::SamplerState &samplerState)
-{
-#if !defined(NDEBUG)
-    // Make sure to add the level offset for our tiny compressed texture workaround
-    TextureD3D *textureD3D = GetImplAs<TextureD3D>(texture);
-
-    TextureStorage *storage = nullptr;
-    ANGLE_TRY(textureD3D->getNativeTexture(context, &storage));
-
-    // Storage should exist, texture should be complete
-    ASSERT(storage);
-#endif  // !defined(NDEBUG)
-
-    // Sampler metadata that's passed to shaders in uniforms is stored separately from rest of the
-    // sampler state since having it in contiguous memory makes it possible to memcpy to a constant
-    // buffer, and it doesn't affect the state set by PSSetSamplers/VSSetSamplers.
-    SamplerMetadataD3D11 *metadata = nullptr;
-
-    if (type == gl::SAMPLER_PIXEL)
-    {
-        ASSERT(static_cast<unsigned int>(index) < getNativeCaps().maxTextureImageUnits);
-
-        if (mForceSetPixelSamplerStates[index] ||
-            memcmp(&samplerState, &mCurPixelSamplerStates[index], sizeof(gl::SamplerState)) != 0)
-        {
-            ID3D11SamplerState *dxSamplerState = nullptr;
-            ANGLE_TRY(mStateCache.getSamplerState(this, samplerState, &dxSamplerState));
-
-            ASSERT(dxSamplerState != nullptr);
-            mDeviceContext->PSSetSamplers(index, 1, &dxSamplerState);
-
-            mCurPixelSamplerStates[index] = samplerState;
-        }
-
-        mForceSetPixelSamplerStates[index] = false;
-
-        metadata = &mSamplerMetadataPS;
-    }
-    else if (type == gl::SAMPLER_VERTEX)
-    {
-        ASSERT(static_cast<unsigned int>(index) < getNativeCaps().maxVertexTextureImageUnits);
-
-        if (mForceSetVertexSamplerStates[index] ||
-            memcmp(&samplerState, &mCurVertexSamplerStates[index], sizeof(gl::SamplerState)) != 0)
-        {
-            ID3D11SamplerState *dxSamplerState = nullptr;
-            ANGLE_TRY(mStateCache.getSamplerState(this, samplerState, &dxSamplerState));
-
-            ASSERT(dxSamplerState != nullptr);
-            mDeviceContext->VSSetSamplers(index, 1, &dxSamplerState);
-
-            mCurVertexSamplerStates[index] = samplerState;
-        }
-
-        mForceSetVertexSamplerStates[index] = false;
-
-        metadata = &mSamplerMetadataVS;
-    }
-    else if (type == gl::SAMPLER_COMPUTE)
-    {
-        ASSERT(static_cast<unsigned int>(index) < getNativeCaps().maxComputeTextureImageUnits);
-
-        if (mForceSetComputeSamplerStates[index] ||
-            memcmp(&samplerState, &mCurComputeSamplerStates[index], sizeof(gl::SamplerState)) != 0)
-        {
-            ID3D11SamplerState *dxSamplerState = nullptr;
-            ANGLE_TRY(mStateCache.getSamplerState(this, samplerState, &dxSamplerState));
-
-            ASSERT(dxSamplerState != nullptr);
-            mDeviceContext->CSSetSamplers(index, 1, &dxSamplerState);
-
-            mCurComputeSamplerStates[index] = samplerState;
-        }
-
-        mForceSetComputeSamplerStates[index] = false;
-
-        metadata = &mSamplerMetadataCS;
-    }
-    else
-        UNREACHABLE();
-
-    ASSERT(metadata != nullptr);
-    metadata->update(index, *texture);
-
-    return gl::NoError();
-}
-
-gl::Error Renderer11::setTexture(const gl::Context *context,
-                                 gl::SamplerType type,
-                                 int index,
-                                 gl::Texture *texture)
-{
-    const d3d11::SharedSRV *textureSRV = nullptr;
-
-    if (texture)
-    {
-        TextureD3D *textureImpl = GetImplAs<TextureD3D>(texture);
-
-        TextureStorage *texStorage = nullptr;
-        ANGLE_TRY(textureImpl->getNativeTexture(context, &texStorage));
-
-        // Texture should be complete and have a storage
-        ASSERT(texStorage);
-
-        TextureStorage11 *storage11 = GetAs<TextureStorage11>(texStorage);
-
-        ANGLE_TRY(storage11->getSRV(context, texture->getTextureState(), &textureSRV));
-
-        // If we get an invalid SRV here, something went wrong in the texture class and we're
-        // unexpectedly missing the shader resource view.
-        ASSERT(textureSRV->valid());
-
-        textureImpl->resetDirty();
-    }
-
-    ASSERT((type == gl::SAMPLER_PIXEL &&
-            static_cast<unsigned int>(index) < getNativeCaps().maxTextureImageUnits) ||
-           (type == gl::SAMPLER_VERTEX &&
-            static_cast<unsigned int>(index) < getNativeCaps().maxVertexTextureImageUnits));
-
-    mStateManager.setShaderResource(type, index, textureSRV->get());
-
-    return gl::NoError();
-}
 
 gl::Error Renderer11::setUniformBuffers(const gl::ContextState &data,
                                         const std::vector<GLint> &vertexUniformBuffers,
@@ -2398,6 +2240,7 @@
     return programD3D->applyUniforms(drawMode);
 }
 
+// TODO(jmadill): Move to StateManager11.
 gl::Error Renderer11::applyUniforms(const ProgramD3D &programD3D,
                                     GLenum drawMode,
                                     const std::vector<D3DUniform *> &uniformArray)
@@ -2505,12 +2348,15 @@
         mCurrentPixelConstantBuffer = reinterpret_cast<uintptr_t>(appliedPixelConstants);
     }
 
+    auto *samplerMetadataVS = mStateManager.getVertexSamplerMetadata();
+    auto *samplerMetadataPS = mStateManager.getPixelSamplerMetadata();
+
     if (!mDriverConstantBufferVS.valid())
     {
         D3D11_BUFFER_DESC constantBufferDescription = {0};
         d3d11::InitConstantBufferDesc(
             &constantBufferDescription,
-            sizeof(dx_VertexConstants11) + mSamplerMetadataVS.sizeBytes());
+            sizeof(dx_VertexConstants11) + samplerMetadataVS->sizeBytes());
         ANGLE_TRY(allocateResource(constantBufferDescription, &mDriverConstantBufferVS));
 
         ID3D11Buffer *driverVSConstants = mDriverConstantBufferVS.get();
@@ -2522,7 +2368,7 @@
     {
         D3D11_BUFFER_DESC constantBufferDescription = {0};
         d3d11::InitConstantBufferDesc(&constantBufferDescription,
-                                      sizeof(dx_PixelConstants11) + mSamplerMetadataPS.sizeBytes());
+                                      sizeof(dx_PixelConstants11) + samplerMetadataPS->sizeBytes());
         ANGLE_TRY(allocateResource(constantBufferDescription, &mDriverConstantBufferPS));
 
         ID3D11Buffer *driverVSConstants = mDriverConstantBufferPS.get();
@@ -2533,15 +2379,15 @@
     // Sampler metadata and driver constants need to coexist in the same constant buffer to conserve
     // constant buffer slots. We update both in the constant buffer if needed.
     const dx_VertexConstants11 &vertexConstants = mStateManager.getVertexConstants();
-    size_t samplerMetadataReferencedBytesVS     = sizeof(SamplerMetadataD3D11::dx_SamplerMetadata) *
+    size_t samplerMetadataReferencedBytesVS     = sizeof(SamplerMetadata11::dx_SamplerMetadata) *
                                               programD3D.getUsedSamplerRange(gl::SAMPLER_VERTEX);
-    applyDriverConstantsIfNeeded(&mAppliedVertexConstants, vertexConstants, &mSamplerMetadataVS,
+    applyDriverConstantsIfNeeded(&mAppliedVertexConstants, vertexConstants, samplerMetadataVS,
                                  samplerMetadataReferencedBytesVS, mDriverConstantBufferVS);
 
     const dx_PixelConstants11 &pixelConstants = mStateManager.getPixelConstants();
-    size_t samplerMetadataReferencedBytesPS   = sizeof(SamplerMetadataD3D11::dx_SamplerMetadata) *
+    size_t samplerMetadataReferencedBytesPS   = sizeof(SamplerMetadata11::dx_SamplerMetadata) *
                                               programD3D.getUsedSamplerRange(gl::SAMPLER_PIXEL);
-    applyDriverConstantsIfNeeded(&mAppliedPixelConstants, pixelConstants, &mSamplerMetadataPS,
+    applyDriverConstantsIfNeeded(&mAppliedPixelConstants, pixelConstants, samplerMetadataPS,
                                  samplerMetadataReferencedBytesPS, mDriverConstantBufferPS);
 
     // GSSetConstantBuffers triggers device removal on 9_3, so we should only call it if necessary
@@ -2560,115 +2406,10 @@
     return gl::NoError();
 }
 
-// SamplerMetadataD3D11 implementation
-
-Renderer11::SamplerMetadataD3D11::SamplerMetadataD3D11() : mDirty(true)
-{
-}
-
-Renderer11::SamplerMetadataD3D11::~SamplerMetadataD3D11()
-{
-}
-
-void Renderer11::SamplerMetadataD3D11::initData(unsigned int samplerCount)
-{
-    mSamplerMetadata.resize(samplerCount);
-}
-
-void Renderer11::SamplerMetadataD3D11::update(unsigned int samplerIndex, const gl::Texture &texture)
-{
-    unsigned int baseLevel = texture.getTextureState().getEffectiveBaseLevel();
-    GLenum sizedFormat =
-        texture.getFormat(texture.getTarget(), baseLevel).info->sizedInternalFormat;
-    if (mSamplerMetadata[samplerIndex].baseLevel != static_cast<int>(baseLevel))
-    {
-        mSamplerMetadata[samplerIndex].baseLevel = static_cast<int>(baseLevel);
-        mDirty                                   = true;
-    }
-
-    // Some metadata is needed only for integer textures. We avoid updating the constant buffer
-    // unnecessarily by changing the data only in case the texture is an integer texture and
-    // the values have changed.
-    bool needIntegerTextureMetadata = false;
-    // internalFormatBits == 0 means a 32-bit texture in the case of integer textures.
-    int internalFormatBits = 0;
-    switch (sizedFormat)
-    {
-        case GL_RGBA32I:
-        case GL_RGBA32UI:
-        case GL_RGB32I:
-        case GL_RGB32UI:
-        case GL_RG32I:
-        case GL_RG32UI:
-        case GL_R32I:
-        case GL_R32UI:
-            needIntegerTextureMetadata = true;
-            break;
-        case GL_RGBA16I:
-        case GL_RGBA16UI:
-        case GL_RGB16I:
-        case GL_RGB16UI:
-        case GL_RG16I:
-        case GL_RG16UI:
-        case GL_R16I:
-        case GL_R16UI:
-            needIntegerTextureMetadata = true;
-            internalFormatBits         = 16;
-            break;
-        case GL_RGBA8I:
-        case GL_RGBA8UI:
-        case GL_RGB8I:
-        case GL_RGB8UI:
-        case GL_RG8I:
-        case GL_RG8UI:
-        case GL_R8I:
-        case GL_R8UI:
-            needIntegerTextureMetadata = true;
-            internalFormatBits         = 8;
-            break;
-        case GL_RGB10_A2UI:
-            needIntegerTextureMetadata = true;
-            internalFormatBits         = 10;
-            break;
-        default:
-            break;
-    }
-    if (needIntegerTextureMetadata)
-    {
-        if (mSamplerMetadata[samplerIndex].internalFormatBits != internalFormatBits)
-        {
-            mSamplerMetadata[samplerIndex].internalFormatBits = internalFormatBits;
-            mDirty                                            = true;
-        }
-        // Pack the wrap values into one integer so we can fit all the metadata in one 4-integer
-        // vector.
-        GLenum wrapS  = texture.getWrapS();
-        GLenum wrapT  = texture.getWrapT();
-        GLenum wrapR  = texture.getWrapR();
-        int wrapModes = GetWrapBits(wrapS) | (GetWrapBits(wrapT) << 2) | (GetWrapBits(wrapR) << 4);
-        if (mSamplerMetadata[samplerIndex].wrapModes != wrapModes)
-        {
-            mSamplerMetadata[samplerIndex].wrapModes = wrapModes;
-            mDirty                                   = true;
-        }
-    }
-}
-
-const Renderer11::SamplerMetadataD3D11::dx_SamplerMetadata *
-Renderer11::SamplerMetadataD3D11::getData() const
-{
-    return mSamplerMetadata.data();
-}
-
-size_t Renderer11::SamplerMetadataD3D11::sizeBytes() const
-{
-    return sizeof(SamplerMetadataD3D11::dx_SamplerMetadata) * mSamplerMetadata.size();
-}
-
 template <class TShaderConstants>
 void Renderer11::applyDriverConstantsIfNeeded(TShaderConstants *appliedConstants,
                                               const TShaderConstants &constants,
-                                              SamplerMetadataD3D11 *samplerMetadata,
+                                              SamplerMetadata11 *samplerMetadata,
                                               size_t samplerMetadataReferencedBytes,
                                               const d3d11::Buffer &driverConstantBuffer)
 {
@@ -2696,19 +2437,19 @@
 template void Renderer11::applyDriverConstantsIfNeeded<dx_VertexConstants11>(
     dx_VertexConstants11 *appliedConstants,
     const dx_VertexConstants11 &constants,
-    SamplerMetadataD3D11 *samplerMetadata,
+    SamplerMetadata11 *samplerMetadata,
     size_t samplerMetadataReferencedBytes,
     const d3d11::Buffer &driverConstantBuffer);
 template void Renderer11::applyDriverConstantsIfNeeded<dx_PixelConstants11>(
     dx_PixelConstants11 *appliedConstants,
     const dx_PixelConstants11 &constants,
-    SamplerMetadataD3D11 *samplerMetadata,
+    SamplerMetadata11 *samplerMetadata,
     size_t samplerMetadataReferencedBytes,
     const d3d11::Buffer &driverConstantBuffer);
 template void Renderer11::applyDriverConstantsIfNeeded<dx_ComputeConstants11>(
     dx_ComputeConstants11 *appliedConstants,
     const dx_ComputeConstants11 &constants,
-    SamplerMetadataD3D11 *samplerMetadata,
+    SamplerMetadata11 *samplerMetadata,
     size_t samplerMetadataReferencedBytes,
     const d3d11::Buffer &driverConstantBuffer);
 
@@ -2716,21 +2457,6 @@
 {
     TRACE_EVENT0("gpu.angle", "Renderer11::markAllStateDirty");
 
-    for (size_t vsamplerId = 0; vsamplerId < mForceSetVertexSamplerStates.size(); ++vsamplerId)
-    {
-        mForceSetVertexSamplerStates[vsamplerId] = true;
-    }
-
-    for (size_t fsamplerId = 0; fsamplerId < mForceSetPixelSamplerStates.size(); ++fsamplerId)
-    {
-        mForceSetPixelSamplerStates[fsamplerId] = true;
-    }
-
-    for (size_t csamplerId = 0; csamplerId < mForceSetComputeSamplerStates.size(); ++csamplerId)
-    {
-        mForceSetComputeSamplerStates[csamplerId] = true;
-    }
-
     mStateManager.invalidateEverything(context);
 
     mAppliedIB       = nullptr;
@@ -4523,14 +4249,6 @@
     return d3d11::GenerateWorkarounds(mRenderer11DeviceCaps, mAdapterDescription);
 }
 
-gl::Error Renderer11::clearTextures(const gl::Context *context,
-                                    gl::SamplerType samplerType,
-                                    size_t rangeStart,
-                                    size_t rangeEnd)
-{
-    return mStateManager.clearTextures(samplerType, rangeStart, rangeEnd);
-}
-
 egl::Error Renderer11::getEGLDevice(DeviceImpl **device)
 {
     if (mEGLDevice == nullptr)
@@ -4595,7 +4313,6 @@
     size_t vertexCount = indexInfo.indexRange.vertexCount();
     ANGLE_TRY(applyVertexBuffer(context, mode, static_cast<GLsizei>(indexInfo.indexRange.start),
                                 static_cast<GLsizei>(vertexCount), instances, &indexInfo));
-    ANGLE_TRY(applyTextures(context));
     ANGLE_TRY(applyShaders(context, mode));
     ANGLE_TRY(programD3D->applyUniformBuffers(data));
 
@@ -4631,7 +4348,6 @@
     ANGLE_TRY(mStateManager.updateState(context, mode));
     ANGLE_TRY(applyTransformFeedbackBuffers(data));
     ANGLE_TRY(applyVertexBuffer(context, mode, first, count, instances, nullptr));
-    ANGLE_TRY(applyTextures(context));
     ANGLE_TRY(applyShaders(context, mode));
     ANGLE_TRY(programD3D->applyUniformBuffers(data));
 
@@ -4666,7 +4382,6 @@
     ANGLE_TRY(mStateManager.updateState(context, mode));
     ANGLE_TRY(applyTransformFeedbackBuffers(contextState));
     ASSERT(!glState.isTransformFeedbackActiveUnpaused());
-    ANGLE_TRY(applyTextures(context));
     ANGLE_TRY(applyShaders(context, mode));
     ANGLE_TRY(programD3D->applyUniformBuffers(contextState));
 
@@ -4706,11 +4421,11 @@
     return mAnnotator;
 }
 
-gl::Error Renderer11::applyComputeShader(const gl::ContextState &data)
+gl::Error Renderer11::applyComputeShader(const gl::Context *context)
 {
     ANGLE_TRY(ensureHLSLCompilerInitialized());
 
-    const auto &glState    = data.getState();
+    const auto &glState    = context->getGLState();
     ProgramD3D *programD3D = GetImplAs<ProgramD3D>(glState.getProgram());
 
     ShaderExecutableD3D *computeExe = nullptr;
@@ -4727,18 +4442,16 @@
                                       GLuint numGroupsY,
                                       GLuint numGroupsZ)
 {
-    const auto &data     = context->getContextState();
-    gl::Program *program = data.getState().getProgram();
+    gl::Program *program = context->getGLState().getProgram();
     ASSERT(program != nullptr);
     ProgramD3D *programD3D = GetImplAs<ProgramD3D>(program);
 
-    mStateManager.setComputeConstants(numGroupsX, numGroupsY, numGroupsZ);
-
     programD3D->updateSamplerMapping();
-
     ANGLE_TRY(generateSwizzles(context, gl::SAMPLER_COMPUTE));
-    ANGLE_TRY(applyTextures(context));
-    ANGLE_TRY(applyComputeShader(data));
+
+    ANGLE_TRY(mStateManager.updateStateForCompute(context, numGroupsX, numGroupsY, numGroupsZ));
+
+    ANGLE_TRY(applyComputeShader(context));
     // TODO(Xinghua): applyUniformBuffers for compute shader.
     mDeviceContext->Dispatch(numGroupsX, numGroupsY, numGroupsZ);
 
@@ -4805,12 +4518,14 @@
         mCurrentComputeConstantBuffer = reinterpret_cast<uintptr_t>(computeConstantBuffer);
     }
 
+    auto *samplerMetadataCS = mStateManager.getComputeSamplerMetadata();
+
     if (!mDriverConstantBufferCS.valid())
     {
         D3D11_BUFFER_DESC constantBufferDescription = {0};
         d3d11::InitConstantBufferDesc(
             &constantBufferDescription,
-            sizeof(dx_ComputeConstants11) + mSamplerMetadataCS.sizeBytes());
+            sizeof(dx_ComputeConstants11) + samplerMetadataCS->sizeBytes());
         ANGLE_TRY(allocateResource(constantBufferDescription, &mDriverConstantBufferCS));
         ID3D11Buffer *buffer = mDriverConstantBufferCS.get();
         mDeviceContext->CSSetConstantBuffers(d3d11::RESERVED_CONSTANT_BUFFER_SLOT_DRIVER, 1,
@@ -4818,9 +4533,9 @@
     }
 
     const dx_ComputeConstants11 &computeConstants = mStateManager.getComputeConstants();
-    size_t samplerMetadataReferencedBytesCS = sizeof(SamplerMetadataD3D11::dx_SamplerMetadata) *
+    size_t samplerMetadataReferencedBytesCS       = sizeof(SamplerMetadata11::dx_SamplerMetadata) *
                                               programD3D.getUsedSamplerRange(gl::SAMPLER_COMPUTE);
-    applyDriverConstantsIfNeeded(&mAppliedComputeConstants, computeConstants, &mSamplerMetadataCS,
+    applyDriverConstantsIfNeeded(&mAppliedComputeConstants, computeConstants, samplerMetadataCS,
                                  samplerMetadataReferencedBytesCS, mDriverConstantBufferCS);
 
     return gl::NoError();
diff --git a/src/libANGLE/renderer/d3d/d3d11/Renderer11.h b/src/libANGLE/renderer/d3d/d3d11/Renderer11.h
index b70cb93..a02b3b3 100644
--- a/src/libANGLE/renderer/d3d/d3d11/Renderer11.h
+++ b/src/libANGLE/renderer/d3d/d3d11/Renderer11.h
@@ -138,16 +138,6 @@
                                    HANDLE shareHandle,
                                    const egl::AttributeMap &attribs) const override;
 
-    gl::Error setSamplerState(const gl::Context *context,
-                              gl::SamplerType type,
-                              int index,
-                              gl::Texture *texture,
-                              const gl::SamplerState &sampler) override;
-    gl::Error setTexture(const gl::Context *context,
-                         gl::SamplerType type,
-                         int index,
-                         gl::Texture *texture) override;
-
     gl::Error setUniformBuffers(const gl::ContextState &data,
                                 const std::vector<GLint> &vertexUniformBuffers,
                                 const std::vector<GLint> &fragmentUniformBuffers) override;
@@ -435,7 +425,7 @@
                               GLuint numGroupsZ);
     gl::Error applyComputeUniforms(const ProgramD3D &programD3D,
                                    const std::vector<D3DUniform *> &uniformArray) override;
-    gl::Error applyComputeShader(const gl::ContextState &data);
+    gl::Error applyComputeShader(const gl::Context *context);
 
     gl::ErrorOrResult<TextureHelper11> createStagingTexture(ResourceType textureType,
                                                             const d3d11::Format &formatSet,
@@ -481,12 +471,6 @@
     gl::Error clearRenderTarget(RenderTargetD3D *renderTarget,
                                 const gl::ColorF &clearValues) override;
 
-  protected:
-    gl::Error clearTextures(const gl::Context *context,
-                            gl::SamplerType samplerType,
-                            size_t rangeStart,
-                            size_t rangeEnd) override;
-
   private:
     gl::Error drawArraysImpl(const gl::Context *context,
                              GLenum mode,
@@ -543,39 +527,10 @@
 
     void updateHistograms();
 
-    class SamplerMetadataD3D11 final : angle::NonCopyable
-    {
-      public:
-        SamplerMetadataD3D11();
-        ~SamplerMetadataD3D11();
-
-        struct dx_SamplerMetadata
-        {
-            int baseLevel;
-            int internalFormatBits;
-            int wrapModes;
-            int padding;  // This just pads the struct to 16 bytes
-        };
-        static_assert(sizeof(dx_SamplerMetadata) == 16u,
-                      "Sampler metadata struct must be one 4-vec / 16 bytes.");
-
-        void initData(unsigned int samplerCount);
-        void update(unsigned int samplerIndex, const gl::Texture &texture);
-
-        const dx_SamplerMetadata *getData() const;
-        size_t sizeBytes() const;
-        bool isDirty() const { return mDirty; }
-        void markClean() { mDirty = false; }
-
-      private:
-        std::vector<dx_SamplerMetadata> mSamplerMetadata;
-        bool mDirty;
-    };
-
     template <class TShaderConstants>
     void applyDriverConstantsIfNeeded(TShaderConstants *appliedConstants,
                                       const TShaderConstants &constants,
-                                      SamplerMetadataD3D11 *samplerMetadata,
+                                      SamplerMetadata11 *samplerMetadata,
                                       size_t samplerMetadataReferencedBytes,
                                       const d3d11::Buffer &driverConstantBuffer);
 
@@ -610,16 +565,6 @@
 
     RenderStateCache mStateCache;
 
-    // Currently applied sampler states
-    std::vector<bool> mForceSetVertexSamplerStates;
-    std::vector<gl::SamplerState> mCurVertexSamplerStates;
-
-    std::vector<bool> mForceSetPixelSamplerStates;
-    std::vector<gl::SamplerState> mCurPixelSamplerStates;
-
-    std::vector<bool> mForceSetComputeSamplerStates;
-    std::vector<gl::SamplerState> mCurComputeSamplerStates;
-
     StateManager11 mStateManager;
 
     // Currently applied index buffer
@@ -633,7 +578,6 @@
 
     dx_VertexConstants11 mAppliedVertexConstants;
     d3d11::Buffer mDriverConstantBufferVS;
-    SamplerMetadataD3D11 mSamplerMetadataVS;
     uintptr_t mCurrentVertexConstantBuffer;
     unsigned int mCurrentConstantBufferVS[gl::IMPLEMENTATION_MAX_VERTEX_SHADER_UNIFORM_BUFFERS];
     GLintptr mCurrentConstantBufferVSOffset[gl::IMPLEMENTATION_MAX_VERTEX_SHADER_UNIFORM_BUFFERS];
@@ -641,7 +585,6 @@
 
     dx_PixelConstants11 mAppliedPixelConstants;
     d3d11::Buffer mDriverConstantBufferPS;
-    SamplerMetadataD3D11 mSamplerMetadataPS;
     uintptr_t mCurrentPixelConstantBuffer;
     unsigned int mCurrentConstantBufferPS[gl::IMPLEMENTATION_MAX_FRAGMENT_SHADER_UNIFORM_BUFFERS];
     GLintptr mCurrentConstantBufferPSOffset[gl::IMPLEMENTATION_MAX_FRAGMENT_SHADER_UNIFORM_BUFFERS];
@@ -649,7 +592,6 @@
 
     dx_ComputeConstants11 mAppliedComputeConstants;
     d3d11::Buffer mDriverConstantBufferCS;
-    SamplerMetadataD3D11 mSamplerMetadataCS;
     uintptr_t mCurrentComputeConstantBuffer;
 
     uintptr_t mCurrentGeometryConstantBuffer;
diff --git a/src/libANGLE/renderer/d3d/d3d11/StateManager11.cpp b/src/libANGLE/renderer/d3d/d3d11/StateManager11.cpp
index 5beb647..4f254c6 100644
--- a/src/libANGLE/renderer/d3d/d3d11/StateManager11.cpp
+++ b/src/libANGLE/renderer/d3d/d3d11/StateManager11.cpp
@@ -13,9 +13,11 @@
 #include "libANGLE/Context.h"
 #include "libANGLE/Query.h"
 #include "libANGLE/VertexArray.h"
+#include "libANGLE/renderer/d3d/TextureD3D.h"
 #include "libANGLE/renderer/d3d/d3d11/Framebuffer11.h"
 #include "libANGLE/renderer/d3d/d3d11/RenderTarget11.h"
 #include "libANGLE/renderer/d3d/d3d11/Renderer11.h"
+#include "libANGLE/renderer/d3d/d3d11/TextureStorage11.h"
 
 namespace rx
 {
@@ -97,8 +99,26 @@
     return resource;
 }
 
+int GetWrapBits(GLenum wrap)
+{
+    switch (wrap)
+    {
+        case GL_CLAMP_TO_EDGE:
+            return 0x1;
+        case GL_REPEAT:
+            return 0x2;
+        case GL_MIRRORED_REPEAT:
+            return 0x3;
+        default:
+            UNREACHABLE();
+            return 0;
+    }
+}
+
 }  // anonymous namespace
 
+// StateManager11::SRVCache Implementation.
+
 void StateManager11::SRVCache::update(size_t resourceIndex, ID3D11ShaderResourceView *srv)
 {
     ASSERT(resourceIndex < mCurrentSRVs.size());
@@ -136,6 +156,110 @@
     mHighestUsedSRV = 0;
 }
 
+// SamplerMetadataD3D11 implementation
+
+SamplerMetadata11::SamplerMetadata11() : mDirty(true)
+{
+}
+
+SamplerMetadata11::~SamplerMetadata11()
+{
+}
+
+void SamplerMetadata11::initData(unsigned int samplerCount)
+{
+    mSamplerMetadata.resize(samplerCount);
+}
+
+void SamplerMetadata11::update(unsigned int samplerIndex, const gl::Texture &texture)
+{
+    unsigned int baseLevel = texture.getTextureState().getEffectiveBaseLevel();
+    GLenum sizedFormat =
+        texture.getFormat(texture.getTarget(), baseLevel).info->sizedInternalFormat;
+    if (mSamplerMetadata[samplerIndex].baseLevel != static_cast<int>(baseLevel))
+    {
+        mSamplerMetadata[samplerIndex].baseLevel = static_cast<int>(baseLevel);
+        mDirty                                   = true;
+    }
+
+    // Some metadata is needed only for integer textures. We avoid updating the constant buffer
+    // unnecessarily by changing the data only in case the texture is an integer texture and
+    // the values have changed.
+    bool needIntegerTextureMetadata = false;
+    // internalFormatBits == 0 means a 32-bit texture in the case of integer textures.
+    int internalFormatBits = 0;
+    switch (sizedFormat)
+    {
+        case GL_RGBA32I:
+        case GL_RGBA32UI:
+        case GL_RGB32I:
+        case GL_RGB32UI:
+        case GL_RG32I:
+        case GL_RG32UI:
+        case GL_R32I:
+        case GL_R32UI:
+            needIntegerTextureMetadata = true;
+            break;
+        case GL_RGBA16I:
+        case GL_RGBA16UI:
+        case GL_RGB16I:
+        case GL_RGB16UI:
+        case GL_RG16I:
+        case GL_RG16UI:
+        case GL_R16I:
+        case GL_R16UI:
+            needIntegerTextureMetadata = true;
+            internalFormatBits         = 16;
+            break;
+        case GL_RGBA8I:
+        case GL_RGBA8UI:
+        case GL_RGB8I:
+        case GL_RGB8UI:
+        case GL_RG8I:
+        case GL_RG8UI:
+        case GL_R8I:
+        case GL_R8UI:
+            needIntegerTextureMetadata = true;
+            internalFormatBits         = 8;
+            break;
+        case GL_RGB10_A2UI:
+            needIntegerTextureMetadata = true;
+            internalFormatBits         = 10;
+            break;
+        default:
+            break;
+    }
+    if (needIntegerTextureMetadata)
+    {
+        if (mSamplerMetadata[samplerIndex].internalFormatBits != internalFormatBits)
+        {
+            mSamplerMetadata[samplerIndex].internalFormatBits = internalFormatBits;
+            mDirty                                            = true;
+        }
+        // Pack the wrap values into one integer so we can fit all the metadata in one 4-integer
+        // vector.
+        GLenum wrapS  = texture.getWrapS();
+        GLenum wrapT  = texture.getWrapT();
+        GLenum wrapR  = texture.getWrapR();
+        int wrapModes = GetWrapBits(wrapS) | (GetWrapBits(wrapT) << 2) | (GetWrapBits(wrapR) << 4);
+        if (mSamplerMetadata[samplerIndex].wrapModes != wrapModes)
+        {
+            mSamplerMetadata[samplerIndex].wrapModes = wrapModes;
+            mDirty                                   = true;
+        }
+    }
+}
+
+const SamplerMetadata11::dx_SamplerMetadata *SamplerMetadata11::getData() const
+{
+    return mSamplerMetadata.data();
+}
+
+size_t SamplerMetadata11::sizeBytes() const
+{
+    return sizeof(dx_SamplerMetadata) * mSamplerMetadata.size();
+}
+
 static const GLenum QueryTypes[] = {GL_ANY_SAMPLES_PASSED, GL_ANY_SAMPLES_PASSED_CONSERVATIVE,
                                     GL_TRANSFORM_FEEDBACK_PRIMITIVES_WRITTEN, GL_TIME_ELAPSED_EXT,
                                     GL_COMMANDS_COMPLETED_CHROMIUM};
@@ -252,11 +376,19 @@
     }
 }
 
-void StateManager11::setComputeConstants(GLuint numGroupsX, GLuint numGroupsY, GLuint numGroupsZ)
+gl::Error StateManager11::updateStateForCompute(const gl::Context *context,
+                                                GLuint numGroupsX,
+                                                GLuint numGroupsY,
+                                                GLuint numGroupsZ)
 {
     mComputeConstants.numWorkGroups[0] = numGroupsX;
     mComputeConstants.numWorkGroups[1] = numGroupsY;
     mComputeConstants.numWorkGroups[2] = numGroupsZ;
+
+    // TODO(jmadill): More complete implementation.
+    ANGLE_TRY(syncTextures(context));
+
+    return gl::NoError();
 }
 
 void StateManager11::syncState(const gl::Context *context, const gl::State::DirtyBits &dirtyBits)
@@ -853,6 +985,10 @@
     mAppliedGeometryShader.dirty();
     mAppliedPixelShader.dirty();
     mAppliedComputeShader.dirty();
+
+    std::fill(mForceSetVertexSamplerStates.begin(), mForceSetVertexSamplerStates.end(), true);
+    std::fill(mForceSetPixelSamplerStates.begin(), mForceSetPixelSamplerStates.end(), true);
+    std::fill(mForceSetComputeSamplerStates.begin(), mForceSetComputeSamplerStates.end(), true);
 }
 
 void StateManager11::invalidateVertexBuffer()
@@ -1020,6 +1156,18 @@
     mNullSRVs.resize(caps.maxTextureImageUnits, nullptr);
 
     mCurrentValueAttribs.resize(caps.maxVertexAttributes);
+
+    mForceSetVertexSamplerStates.resize(caps.maxVertexTextureImageUnits);
+    mForceSetPixelSamplerStates.resize(caps.maxTextureImageUnits);
+    mForceSetComputeSamplerStates.resize(caps.maxComputeTextureImageUnits);
+
+    mCurVertexSamplerStates.resize(caps.maxVertexTextureImageUnits);
+    mCurPixelSamplerStates.resize(caps.maxTextureImageUnits);
+    mCurComputeSamplerStates.resize(caps.maxComputeTextureImageUnits);
+
+    mSamplerMetadataVS.initData(caps.maxVertexTextureImageUnits);
+    mSamplerMetadataPS.initData(caps.maxTextureImageUnits);
+    mSamplerMetadataCS.initData(caps.maxComputeTextureImageUnits);
 }
 
 void StateManager11::deinitialize()
@@ -1284,6 +1432,9 @@
         }
     }
 
+    // TODO(jmadill): Use dirty bits.
+    ANGLE_TRY(syncTextures(context));
+
     // Check that we haven't set any dirty bits in the flushing of the dirty bits loop.
     ASSERT(mInternalDirtyBits.none());
 
@@ -1356,4 +1507,218 @@
     }
 }
 
+// For each Direct3D sampler of either the pixel or vertex stage,
+// looks up the corresponding OpenGL texture image unit and texture type,
+// and sets the texture and its addressing/filtering state (or NULL when inactive).
+// Sampler mapping needs to be up-to-date on the program object before this is called.
+gl::Error StateManager11::applyTextures(const gl::Context *context,
+                                        gl::SamplerType shaderType,
+                                        const FramebufferTextureArray &framebufferTextures,
+                                        size_t framebufferTextureCount)
+{
+    const auto &glState    = context->getGLState();
+    const auto &caps       = context->getCaps();
+    ProgramD3D *programD3D = GetImplAs<ProgramD3D>(glState.getProgram());
+
+    ASSERT(!programD3D->isSamplerMappingDirty());
+
+    // TODO(jmadill): Use the Program's sampler bindings.
+
+    unsigned int samplerRange = programD3D->getUsedSamplerRange(shaderType);
+    for (unsigned int samplerIndex = 0; samplerIndex < samplerRange; samplerIndex++)
+    {
+        GLenum textureType = programD3D->getSamplerTextureType(shaderType, samplerIndex);
+        GLint textureUnit  = programD3D->getSamplerMapping(shaderType, samplerIndex, caps);
+        if (textureUnit != -1)
+        {
+            gl::Texture *texture = glState.getSamplerTexture(textureUnit, textureType);
+            ASSERT(texture);
+
+            gl::Sampler *samplerObject = glState.getSampler(textureUnit);
+
+            const gl::SamplerState &samplerState =
+                samplerObject ? samplerObject->getSamplerState() : texture->getSamplerState();
+
+            // TODO: std::binary_search may become unavailable using older versions of GCC
+            if (texture->getTextureState().isSamplerComplete(samplerState,
+                                                             context->getContextState()) &&
+                !std::binary_search(framebufferTextures.begin(),
+                                    framebufferTextures.begin() + framebufferTextureCount, texture))
+            {
+                ANGLE_TRY(
+                    setSamplerState(context, shaderType, samplerIndex, texture, samplerState));
+                ANGLE_TRY(setTexture(context, shaderType, samplerIndex, texture));
+            }
+            else
+            {
+                // Texture is not sampler complete or it is in use by the framebuffer.  Bind the
+                // incomplete texture.
+                gl::Texture *incompleteTexture =
+                    mRenderer->getIncompleteTexture(context, textureType);
+
+                ANGLE_TRY(setSamplerState(context, shaderType, samplerIndex, incompleteTexture,
+                                          incompleteTexture->getSamplerState()));
+                ANGLE_TRY(setTexture(context, shaderType, samplerIndex, incompleteTexture));
+            }
+        }
+        else
+        {
+            // No texture bound to this slot even though it is used by the shader, bind a NULL
+            // texture
+            ANGLE_TRY(setTexture(context, shaderType, samplerIndex, nullptr));
+        }
+    }
+
+    // Set all the remaining textures to NULL
+    size_t samplerCount = (shaderType == gl::SAMPLER_PIXEL) ? caps.maxTextureImageUnits
+                                                            : caps.maxVertexTextureImageUnits;
+    clearTextures(shaderType, samplerRange, samplerCount);
+
+    return gl::NoError();
+}
+
+gl::Error StateManager11::syncTextures(const gl::Context *context)
+{
+    FramebufferTextureArray framebufferTextures;
+    size_t framebufferSerialCount =
+        mRenderer->getBoundFramebufferTextures(context->getContextState(), &framebufferTextures);
+
+    ANGLE_TRY(
+        applyTextures(context, gl::SAMPLER_VERTEX, framebufferTextures, framebufferSerialCount));
+    ANGLE_TRY(
+        applyTextures(context, gl::SAMPLER_PIXEL, framebufferTextures, framebufferSerialCount));
+    return gl::NoError();
+}
+
+gl::Error StateManager11::setSamplerState(const gl::Context *context,
+                                          gl::SamplerType type,
+                                          int index,
+                                          gl::Texture *texture,
+                                          const gl::SamplerState &samplerState)
+{
+#if !defined(NDEBUG)
+    // Storage should exist, texture should be complete. Only verified in Debug.
+    TextureD3D *textureD3D  = GetImplAs<TextureD3D>(texture);
+    TextureStorage *storage = nullptr;
+    ANGLE_TRY(textureD3D->getNativeTexture(context, &storage));
+    ASSERT(storage);
+#endif  // !defined(NDEBUG)
+
+    // Sampler metadata that's passed to shaders in uniforms is stored separately from rest of the
+    // sampler state since having it in contiguous memory makes it possible to memcpy to a constant
+    // buffer, and it doesn't affect the state set by PSSetSamplers/VSSetSamplers.
+    SamplerMetadata11 *metadata = nullptr;
+
+    auto *deviceContext = mRenderer->getDeviceContext();
+
+    if (type == gl::SAMPLER_PIXEL)
+    {
+        ASSERT(static_cast<unsigned int>(index) < mRenderer->getNativeCaps().maxTextureImageUnits);
+
+        if (mForceSetPixelSamplerStates[index] ||
+            memcmp(&samplerState, &mCurPixelSamplerStates[index], sizeof(gl::SamplerState)) != 0)
+        {
+            ID3D11SamplerState *dxSamplerState = nullptr;
+            ANGLE_TRY(mRenderer->getSamplerState(samplerState, &dxSamplerState));
+
+            ASSERT(dxSamplerState != nullptr);
+            deviceContext->PSSetSamplers(index, 1, &dxSamplerState);
+
+            mCurPixelSamplerStates[index] = samplerState;
+        }
+
+        mForceSetPixelSamplerStates[index] = false;
+
+        metadata = &mSamplerMetadataPS;
+    }
+    else if (type == gl::SAMPLER_VERTEX)
+    {
+        ASSERT(static_cast<unsigned int>(index) <
+               mRenderer->getNativeCaps().maxVertexTextureImageUnits);
+
+        if (mForceSetVertexSamplerStates[index] ||
+            memcmp(&samplerState, &mCurVertexSamplerStates[index], sizeof(gl::SamplerState)) != 0)
+        {
+            ID3D11SamplerState *dxSamplerState = nullptr;
+            ANGLE_TRY(mRenderer->getSamplerState(samplerState, &dxSamplerState));
+
+            ASSERT(dxSamplerState != nullptr);
+            deviceContext->VSSetSamplers(index, 1, &dxSamplerState);
+
+            mCurVertexSamplerStates[index] = samplerState;
+        }
+
+        mForceSetVertexSamplerStates[index] = false;
+
+        metadata = &mSamplerMetadataVS;
+    }
+    else if (type == gl::SAMPLER_COMPUTE)
+    {
+        ASSERT(static_cast<unsigned int>(index) <
+               mRenderer->getNativeCaps().maxComputeTextureImageUnits);
+
+        if (mForceSetComputeSamplerStates[index] ||
+            memcmp(&samplerState, &mCurComputeSamplerStates[index], sizeof(gl::SamplerState)) != 0)
+        {
+            ID3D11SamplerState *dxSamplerState = nullptr;
+            ANGLE_TRY(mRenderer->getSamplerState(samplerState, &dxSamplerState));
+
+            ASSERT(dxSamplerState != nullptr);
+            deviceContext->CSSetSamplers(index, 1, &dxSamplerState);
+
+            mCurComputeSamplerStates[index] = samplerState;
+        }
+
+        mForceSetComputeSamplerStates[index] = false;
+
+        metadata = &mSamplerMetadataCS;
+    }
+    else
+        UNREACHABLE();
+
+    ASSERT(metadata != nullptr);
+    metadata->update(index, *texture);
+
+    return gl::NoError();
+}
+
+gl::Error StateManager11::setTexture(const gl::Context *context,
+                                     gl::SamplerType type,
+                                     int index,
+                                     gl::Texture *texture)
+{
+    const d3d11::SharedSRV *textureSRV = nullptr;
+
+    if (texture)
+    {
+        TextureD3D *textureImpl = GetImplAs<TextureD3D>(texture);
+
+        TextureStorage *texStorage = nullptr;
+        ANGLE_TRY(textureImpl->getNativeTexture(context, &texStorage));
+
+        // Texture should be complete and have a storage
+        ASSERT(texStorage);
+
+        TextureStorage11 *storage11 = GetAs<TextureStorage11>(texStorage);
+
+        ANGLE_TRY(storage11->getSRV(context, texture->getTextureState(), &textureSRV));
+
+        // If we get an invalid SRV here, something went wrong in the texture class and we're
+        // unexpectedly missing the shader resource view.
+        ASSERT(textureSRV->valid());
+
+        textureImpl->resetDirty();
+    }
+
+    ASSERT(
+        (type == gl::SAMPLER_PIXEL &&
+         static_cast<unsigned int>(index) < mRenderer->getNativeCaps().maxTextureImageUnits) ||
+        (type == gl::SAMPLER_VERTEX &&
+         static_cast<unsigned int>(index) < mRenderer->getNativeCaps().maxVertexTextureImageUnits));
+
+    setShaderResource(type, index, textureSRV->get());
+
+    return gl::NoError();
+}
+
 }  // namespace rx
diff --git a/src/libANGLE/renderer/d3d/d3d11/StateManager11.h b/src/libANGLE/renderer/d3d/d3d11/StateManager11.h
index bc0f476..7d277a5 100644
--- a/src/libANGLE/renderer/d3d/d3d11/StateManager11.h
+++ b/src/libANGLE/renderer/d3d/d3d11/StateManager11.h
@@ -47,6 +47,35 @@
     unsigned int padding;  // This just pads the struct to 16 bytes
 };
 
+class SamplerMetadata11 final : angle::NonCopyable
+{
+  public:
+    SamplerMetadata11();
+    ~SamplerMetadata11();
+
+    struct dx_SamplerMetadata
+    {
+        int baseLevel;
+        int internalFormatBits;
+        int wrapModes;
+        int padding;  // This just pads the struct to 16 bytes
+    };
+    static_assert(sizeof(dx_SamplerMetadata) == 16u,
+                  "Sampler metadata struct must be one 4-vec / 16 bytes.");
+
+    void initData(unsigned int samplerCount);
+    void update(unsigned int samplerIndex, const gl::Texture &texture);
+
+    const dx_SamplerMetadata *getData() const;
+    size_t sizeBytes() const;
+    bool isDirty() const { return mDirty; }
+    void markClean() { mDirty = false; }
+
+  private:
+    std::vector<dx_SamplerMetadata> mSamplerMetadata;
+    bool mDirty;
+};
+
 class StateManager11 final : angle::NonCopyable
 {
   public:
@@ -58,18 +87,25 @@
 
     void syncState(const gl::Context *context, const gl::State::DirtyBits &dirtyBits);
 
+    // TODO(jmadill): Don't expose these.
     const dx_VertexConstants11 &getVertexConstants() const { return mVertexConstants; }
     const dx_PixelConstants11 &getPixelConstants() const { return mPixelConstants; }
     const dx_ComputeConstants11 &getComputeConstants() const { return mComputeConstants; }
 
-    void setComputeConstants(GLuint numGroupsX, GLuint numGroupsY, GLuint numGroupsZ);
+    SamplerMetadata11 *getVertexSamplerMetadata() { return &mSamplerMetadataVS; }
+    SamplerMetadata11 *getPixelSamplerMetadata() { return &mSamplerMetadataPS; }
+    SamplerMetadata11 *getComputeSamplerMetadata() { return &mSamplerMetadataCS; }
+
+    gl::Error updateStateForCompute(const gl::Context *context,
+                                    GLuint numGroupsX,
+                                    GLuint numGroupsY,
+                                    GLuint numGroupsZ);
 
     void updateStencilSizeIfChanged(bool depthStencilInitialized, unsigned int stencilSize);
 
     void setShaderResource(gl::SamplerType shaderType,
                            UINT resourceSlot,
                            ID3D11ShaderResourceView *srv);
-    gl::Error clearTextures(gl::SamplerType samplerType, size_t rangeStart, size_t rangeEnd);
 
     // Checks are done on a framebuffer state change to trigger other state changes.
     // The Context is allowed to be nullptr for these methods, when called in EGL init code.
@@ -144,6 +180,25 @@
 
     gl::Error syncFramebuffer(const gl::Context *context, gl::Framebuffer *framebuffer);
 
+    gl::Error syncTextures(const gl::Context *context);
+    gl::Error applyTextures(const gl::Context *context,
+                            gl::SamplerType shaderType,
+                            const FramebufferTextureArray &framebufferTextures,
+                            size_t framebufferTextureCount);
+
+    gl::Error setSamplerState(const gl::Context *context,
+                              gl::SamplerType type,
+                              int index,
+                              gl::Texture *texture,
+                              const gl::SamplerState &sampler);
+    gl::Error setTexture(const gl::Context *context,
+                         gl::SamplerType type,
+                         int index,
+                         gl::Texture *texture);
+
+    // Faster than calling setTexture a jillion times
+    gl::Error clearTextures(gl::SamplerType samplerType, size_t rangeStart, size_t rangeEnd);
+
     enum DirtyBitType
     {
         DIRTY_BIT_RENDER_TARGET,
@@ -263,6 +318,20 @@
     ResourceSerial mAppliedGeometryShader;
     ResourceSerial mAppliedPixelShader;
     ResourceSerial mAppliedComputeShader;
+
+    // Currently applied sampler states
+    std::vector<bool> mForceSetVertexSamplerStates;
+    std::vector<gl::SamplerState> mCurVertexSamplerStates;
+
+    std::vector<bool> mForceSetPixelSamplerStates;
+    std::vector<gl::SamplerState> mCurPixelSamplerStates;
+
+    std::vector<bool> mForceSetComputeSamplerStates;
+    std::vector<gl::SamplerState> mCurComputeSamplerStates;
+
+    SamplerMetadata11 mSamplerMetadataVS;
+    SamplerMetadata11 mSamplerMetadataPS;
+    SamplerMetadata11 mSamplerMetadataCS;
 };
 
 }  // namespace rx
diff --git a/src/libANGLE/renderer/d3d/d3d9/Renderer9.cpp b/src/libANGLE/renderer/d3d/d3d9/Renderer9.cpp
index b8b75c8..8d1e052 100644
--- a/src/libANGLE/renderer/d3d/d3d9/Renderer9.cpp
+++ b/src/libANGLE/renderer/d3d/d3d9/Renderer9.cpp
@@ -3082,20 +3082,6 @@
     return d3d9::GenerateWorkarounds();
 }
 
-gl::Error Renderer9::clearTextures(const gl::Context *context,
-                                   gl::SamplerType samplerType,
-                                   size_t rangeStart,
-                                   size_t rangeEnd)
-{
-    // TODO(jmadill): faster way?
-    for (size_t samplerIndex = rangeStart; samplerIndex < rangeEnd; samplerIndex++)
-    {
-        ANGLE_TRY(setTexture(context, samplerType, static_cast<int>(samplerIndex), nullptr));
-    }
-
-    return gl::NoError();
-}
-
 egl::Error Renderer9::getEGLDevice(DeviceImpl **device)
 {
     if (mEGLDevice == nullptr)
@@ -3233,4 +3219,91 @@
     return gl::InternalError() << "clearRenderTarget is not implemented on D3D9";
 }
 
+// For each Direct3D sampler of either the pixel or vertex stage,
+// looks up the corresponding OpenGL texture image unit and texture type,
+// and sets the texture and its addressing/filtering state (or NULL when inactive).
+// Sampler mapping needs to be up-to-date on the program object before this is called.
+gl::Error Renderer9::applyTextures(const gl::Context *context,
+                                   gl::SamplerType shaderType,
+                                   const FramebufferTextureArray &framebufferTextures,
+                                   size_t framebufferTextureCount)
+{
+    const auto &glState    = context->getGLState();
+    const auto &caps       = context->getCaps();
+    ProgramD3D *programD3D = GetImplAs<ProgramD3D>(glState.getProgram());
+
+    ASSERT(!programD3D->isSamplerMappingDirty());
+
+    // TODO(jmadill): Use the Program's sampler bindings.
+
+    unsigned int samplerRange = programD3D->getUsedSamplerRange(shaderType);
+    for (unsigned int samplerIndex = 0; samplerIndex < samplerRange; samplerIndex++)
+    {
+        GLenum textureType = programD3D->getSamplerTextureType(shaderType, samplerIndex);
+        GLint textureUnit  = programD3D->getSamplerMapping(shaderType, samplerIndex, caps);
+        if (textureUnit != -1)
+        {
+            gl::Texture *texture = glState.getSamplerTexture(textureUnit, textureType);
+            ASSERT(texture);
+
+            gl::Sampler *samplerObject = glState.getSampler(textureUnit);
+
+            const gl::SamplerState &samplerState =
+                samplerObject ? samplerObject->getSamplerState() : texture->getSamplerState();
+
+            // TODO: std::binary_search may become unavailable using older versions of GCC
+            if (texture->getTextureState().isSamplerComplete(samplerState,
+                                                             context->getContextState()) &&
+                !std::binary_search(framebufferTextures.begin(),
+                                    framebufferTextures.begin() + framebufferTextureCount, texture))
+            {
+                ANGLE_TRY(
+                    setSamplerState(context, shaderType, samplerIndex, texture, samplerState));
+                ANGLE_TRY(setTexture(context, shaderType, samplerIndex, texture));
+            }
+            else
+            {
+                // Texture is not sampler complete or it is in use by the framebuffer.  Bind the
+                // incomplete texture.
+                gl::Texture *incompleteTexture = getIncompleteTexture(context, textureType);
+
+                ANGLE_TRY(setSamplerState(context, shaderType, samplerIndex, incompleteTexture,
+                                          incompleteTexture->getSamplerState()));
+                ANGLE_TRY(setTexture(context, shaderType, samplerIndex, incompleteTexture));
+            }
+        }
+        else
+        {
+            // No texture bound to this slot even though it is used by the shader, bind a NULL
+            // texture
+            ANGLE_TRY(setTexture(context, shaderType, samplerIndex, nullptr));
+        }
+    }
+
+    // Set all the remaining textures to NULL
+    size_t samplerCount = (shaderType == gl::SAMPLER_PIXEL) ? caps.maxTextureImageUnits
+                                                            : caps.maxVertexTextureImageUnits;
+
+    // TODO(jmadill): faster way?
+    for (size_t samplerIndex = samplerRange; samplerIndex < samplerCount; samplerIndex++)
+    {
+        ANGLE_TRY(setTexture(context, shaderType, static_cast<int>(samplerIndex), nullptr));
+    }
+
+    return gl::NoError();
+}
+
+gl::Error Renderer9::applyTextures(const gl::Context *context)
+{
+    FramebufferTextureArray framebufferTextures;
+    size_t framebufferSerialCount =
+        getBoundFramebufferTextures(context->getContextState(), &framebufferTextures);
+
+    ANGLE_TRY(
+        applyTextures(context, gl::SAMPLER_VERTEX, framebufferTextures, framebufferSerialCount));
+    ANGLE_TRY(
+        applyTextures(context, gl::SAMPLER_PIXEL, framebufferTextures, framebufferSerialCount));
+    return gl::NoError();
+}
+
 }  // namespace rx
diff --git a/src/libANGLE/renderer/d3d/d3d9/Renderer9.h b/src/libANGLE/renderer/d3d/d3d9/Renderer9.h
index e22239c..c92f2b1 100644
--- a/src/libANGLE/renderer/d3d/d3d9/Renderer9.h
+++ b/src/libANGLE/renderer/d3d/d3d9/Renderer9.h
@@ -123,11 +123,11 @@
                               gl::SamplerType type,
                               int index,
                               gl::Texture *texture,
-                              const gl::SamplerState &sampler) override;
+                              const gl::SamplerState &sampler);
     gl::Error setTexture(const gl::Context *context,
                          gl::SamplerType type,
                          int index,
-                         gl::Texture *texture) override;
+                         gl::Texture *texture);
 
     gl::Error setUniformBuffers(const gl::ContextState &data,
                                 const std::vector<GLint> &vertexUniformBuffers,
@@ -398,12 +398,6 @@
     gl::Error clearRenderTarget(RenderTargetD3D *renderTarget,
                                 const gl::ColorF &clearValues) override;
 
-  protected:
-    gl::Error clearTextures(const gl::Context *context,
-                            gl::SamplerType samplerType,
-                            size_t rangeStart,
-                            size_t rangeEnd) override;
-
   private:
     gl::Error drawArraysImpl(const gl::ContextState &data,
                              GLenum mode,
@@ -420,6 +414,12 @@
 
     gl::Error applyShaders(const gl::Context *context, GLenum drawMode);
 
+    gl::Error applyTextures(const gl::Context *context);
+    gl::Error applyTextures(const gl::Context *context,
+                            gl::SamplerType shaderType,
+                            const FramebufferTextureArray &framebufferTextures,
+                            size_t framebufferTextureCount);
+
     void generateCaps(gl::Caps *outCaps,
                       gl::TextureCapsMap *outTextureCaps,
                       gl::Extensions *outExtensions,