Implement window rectangles in vulkan

Bug: skia:
Change-Id: I32c079b90a5503c797dfc073a093f940cd8c550a
Reviewed-on: https://skia-review.googlesource.com/65423
Commit-Queue: Chris Dalton <csmartdalton@google.com>
Reviewed-by: Greg Daniel <egdaniel@google.com>
Reviewed-by: Brian Salomon <bsalomon@google.com>
diff --git a/gm/windowrectangles.cpp b/gm/windowrectangles.cpp
index b4f6562..034b30f 100644
--- a/gm/windowrectangles.cpp
+++ b/gm/windowrectangles.cpp
@@ -256,7 +256,7 @@
         0>()
     );
 
-    rtc->priv().clearStencilClip(GrFixedClip::Disabled(), false);
+    rtc->priv().clearStencilClip(GrFixedClip::Disabled(), false, true);
 
     for (int y = 0; y < kDeviceRect.height(); y += kMaskCheckerSize) {
         for (int x = (y & 1) == flip ? 0 : kMaskCheckerSize;
diff --git a/include/gpu/GrCaps.h b/include/gpu/GrCaps.h
index 0b4c2e7..5579557 100644
--- a/include/gpu/GrCaps.h
+++ b/include/gpu/GrCaps.h
@@ -126,6 +126,14 @@
     bool reuseScratchTextures() const { return fReuseScratchTextures; }
     bool reuseScratchBuffers() const { return fReuseScratchBuffers; }
 
+    enum class WindowRectsSupport {
+        kNone,
+        kDrawOnly,
+        kDrawAndClear
+    };
+
+    WindowRectsSupport windowRectsSupport() const { return fWindowRectsSupport; }
+
     /// maximum number of attribute values per vertex
     int maxVertexAttributes() const { return fMaxVertexAttributes; }
 
@@ -234,6 +242,8 @@
     uint32_t fMapBufferFlags;
     int fBufferMapThreshold;
 
+    WindowRectsSupport fWindowRectsSupport;
+
     int fMaxRenderTargetSize;
     int fMaxVertexAttributes;
     int fMaxTextureSize;
diff --git a/include/gpu/vk/GrVkBackendContext.h b/include/gpu/vk/GrVkBackendContext.h
index 68791ba..4b93313 100644
--- a/include/gpu/vk/GrVkBackendContext.h
+++ b/include/gpu/vk/GrVkBackendContext.h
@@ -14,13 +14,15 @@
 #include "vk/GrVkInterface.h"
 
 enum GrVkExtensionFlags {
-    kEXT_debug_report_GrVkExtensionFlag    = 0x0001,
-    kNV_glsl_shader_GrVkExtensionFlag      = 0x0002,
-    kKHR_surface_GrVkExtensionFlag         = 0x0004,
-    kKHR_swapchain_GrVkExtensionFlag       = 0x0008,
-    kKHR_win32_surface_GrVkExtensionFlag   = 0x0010,
-    kKHR_android_surface_GrVkExtensionFlag = 0x0020,
-    kKHR_xcb_surface_GrVkExtensionFlag     = 0x0040,
+    kKHR_get_physical_device_properties2_GrVkExtensionFlag = 0x0001,
+    kEXT_debug_report_GrVkExtensionFlag                    = 0x0002,
+    kNV_glsl_shader_GrVkExtensionFlag                      = 0x0004,
+    kKHR_surface_GrVkExtensionFlag                         = 0x0008,
+    kEXT_discard_rectangles_GrVkExtensionFlag              = 0x0010,
+    kKHR_swapchain_GrVkExtensionFlag                       = 0x0020,
+    kKHR_win32_surface_GrVkExtensionFlag                   = 0x0040,
+    kKHR_android_surface_GrVkExtensionFlag                 = 0x0080,
+    kKHR_xcb_surface_GrVkExtensionFlag                     = 0x0100,
 };
 
 enum GrVkFeatureFlags {
diff --git a/include/gpu/vk/GrVkDefines.h b/include/gpu/vk/GrVkDefines.h
index 0bc6fb0..5873f99 100644
--- a/include/gpu/vk/GrVkDefines.h
+++ b/include/gpu/vk/GrVkDefines.h
@@ -38,6 +38,130 @@
 #error "Vulkan header version is too low"
 #endif
 
-#endif
+#ifndef VK_KHR_get_physical_device_properties2
 
-#endif
+// Installed Vulkan SDK is too old to define VK_KHR_get_physical_device_properties2: define it here.
+#define VK_KHR_get_physical_device_properties2 1
+#define VK_KHR_GET_PHYSICAL_DEVICE_PROPERTIES_2_SPEC_VERSION 1
+#define VK_KHR_GET_PHYSICAL_DEVICE_PROPERTIES_2_EXTENSION_NAME "VK_KHR_get_physical_device_properties2"
+
+typedef struct VkPhysicalDeviceFeatures2KHR {
+    VkStructureType             sType;
+    void*                       pNext;
+    VkPhysicalDeviceFeatures    features;
+} VkPhysicalDeviceFeatures2KHR;
+
+typedef struct VkPhysicalDeviceProperties2KHR {
+    VkStructureType               sType;
+    void*                         pNext;
+    VkPhysicalDeviceProperties    properties;
+} VkPhysicalDeviceProperties2KHR;
+
+typedef struct VkFormatProperties2KHR {
+    VkStructureType       sType;
+    void*                 pNext;
+    VkFormatProperties    formatProperties;
+} VkFormatProperties2KHR;
+
+typedef struct VkImageFormatProperties2KHR {
+    VkStructureType            sType;
+    void*                      pNext;
+    VkImageFormatProperties    imageFormatProperties;
+} VkImageFormatProperties2KHR;
+
+typedef struct VkPhysicalDeviceImageFormatInfo2KHR {
+    VkStructureType       sType;
+    const void*           pNext;
+    VkFormat              format;
+    VkImageType           type;
+    VkImageTiling         tiling;
+    VkImageUsageFlags     usage;
+    VkImageCreateFlags    flags;
+} VkPhysicalDeviceImageFormatInfo2KHR;
+
+typedef struct VkQueueFamilyProperties2KHR {
+    VkStructureType            sType;
+    void*                      pNext;
+    VkQueueFamilyProperties    queueFamilyProperties;
+} VkQueueFamilyProperties2KHR;
+
+typedef struct VkPhysicalDeviceMemoryProperties2KHR {
+    VkStructureType                     sType;
+    void*                               pNext;
+    VkPhysicalDeviceMemoryProperties    memoryProperties;
+} VkPhysicalDeviceMemoryProperties2KHR;
+
+typedef struct VkSparseImageFormatProperties2KHR {
+    VkStructureType                  sType;
+    void*                            pNext;
+    VkSparseImageFormatProperties    properties;
+} VkSparseImageFormatProperties2KHR;
+
+typedef struct VkPhysicalDeviceSparseImageFormatInfo2KHR {
+    VkStructureType          sType;
+    const void*              pNext;
+    VkFormat                 format;
+    VkImageType              type;
+    VkSampleCountFlagBits    samples;
+    VkImageUsageFlags        usage;
+    VkImageTiling            tiling;
+} VkPhysicalDeviceSparseImageFormatInfo2KHR;
+
+
+typedef void (VKAPI_PTR *PFN_vkGetPhysicalDeviceFeatures2KHR)(VkPhysicalDevice physicalDevice, VkPhysicalDeviceFeatures2KHR* pFeatures);
+typedef void (VKAPI_PTR *PFN_vkGetPhysicalDeviceProperties2KHR)(VkPhysicalDevice physicalDevice, VkPhysicalDeviceProperties2KHR* pProperties);
+typedef void (VKAPI_PTR *PFN_vkGetPhysicalDeviceFormatProperties2KHR)(VkPhysicalDevice physicalDevice, VkFormat format, VkFormatProperties2KHR* pFormatProperties);
+typedef VkResult (VKAPI_PTR *PFN_vkGetPhysicalDeviceImageFormatProperties2KHR)(VkPhysicalDevice physicalDevice, const VkPhysicalDeviceImageFormatInfo2KHR* pImageFormatInfo, VkImageFormatProperties2KHR* pImageFormatProperties);
+typedef void (VKAPI_PTR *PFN_vkGetPhysicalDeviceQueueFamilyProperties2KHR)(VkPhysicalDevice physicalDevice, uint32_t* pQueueFamilyPropertyCount, VkQueueFamilyProperties2KHR* pQueueFamilyProperties);
+typedef void (VKAPI_PTR *PFN_vkGetPhysicalDeviceMemoryProperties2KHR)(VkPhysicalDevice physicalDevice, VkPhysicalDeviceMemoryProperties2KHR* pMemoryProperties);
+typedef void (VKAPI_PTR *PFN_vkGetPhysicalDeviceSparseImageFormatProperties2KHR)(VkPhysicalDevice physicalDevice, const VkPhysicalDeviceSparseImageFormatInfo2KHR* pFormatInfo, uint32_t* pPropertyCount, VkSparseImageFormatProperties2KHR* pProperties);
+
+static constexpr VkStructureType VK_STRUCTURE_TYPE_PHYSICAL_DEVICE_PROPERTIES_2_KHR = (VkStructureType) 1000059001;
+
+#endif // !VK_KHR_get_physical_device_properties2
+
+#ifndef VK_EXT_discard_rectangles
+
+// Installed Vulkan SDK is too old to define discard rectangles: define them here.
+#define VK_EXT_discard_rectangles 1
+#define VK_EXT_DISCARD_RECTANGLES_SPEC_VERSION 1
+#define VK_EXT_DISCARD_RECTANGLES_EXTENSION_NAME "VK_EXT_discard_rectangles"
+
+typedef enum VkDiscardRectangleModeEXT {
+    VK_DISCARD_RECTANGLE_MODE_INCLUSIVE_EXT = 0,
+    VK_DISCARD_RECTANGLE_MODE_EXCLUSIVE_EXT = 1,
+    VK_DISCARD_RECTANGLE_MODE_BEGIN_RANGE_EXT = VK_DISCARD_RECTANGLE_MODE_INCLUSIVE_EXT,
+    VK_DISCARD_RECTANGLE_MODE_END_RANGE_EXT = VK_DISCARD_RECTANGLE_MODE_EXCLUSIVE_EXT,
+    VK_DISCARD_RECTANGLE_MODE_RANGE_SIZE_EXT = (VK_DISCARD_RECTANGLE_MODE_EXCLUSIVE_EXT - VK_DISCARD_RECTANGLE_MODE_INCLUSIVE_EXT + 1),
+    VK_DISCARD_RECTANGLE_MODE_MAX_ENUM_EXT = 0x7FFFFFFF
+} VkDiscardRectangleModeEXT;
+
+typedef VkFlags VkPipelineDiscardRectangleStateCreateFlagsEXT;
+
+typedef struct VkPhysicalDeviceDiscardRectanglePropertiesEXT {
+    VkStructureType    sType;
+    const void*        pNext;
+    uint32_t           maxDiscardRectangles;
+} VkPhysicalDeviceDiscardRectanglePropertiesEXT;
+
+typedef struct VkPipelineDiscardRectangleStateCreateInfoEXT {
+    VkStructureType                                  sType;
+    const void*                                      pNext;
+    VkPipelineDiscardRectangleStateCreateFlagsEXT    flags;
+    VkDiscardRectangleModeEXT                        discardRectangleMode;
+    uint32_t                                         discardRectangleCount;
+    const VkRect2D*                                  pDiscardRectangles;
+} VkPipelineDiscardRectangleStateCreateInfoEXT;
+
+
+typedef void (VKAPI_PTR *PFN_vkCmdSetDiscardRectangleEXT)(VkCommandBuffer commandBuffer, uint32_t firstDiscardRectangle, uint32_t discardRectangleCount, const VkRect2D* pDiscardRectangles);
+
+static constexpr VkStructureType VK_STRUCTURE_TYPE_PHYSICAL_DEVICE_DISCARD_RECTANGLE_PROPERTIES_EXT = (VkStructureType) 1000099000;
+static constexpr VkStructureType VK_STRUCTURE_TYPE_PIPELINE_DISCARD_RECTANGLE_STATE_CREATE_INFO_EXT = (VkStructureType) 1000099001;
+static constexpr VkDynamicState VK_DYNAMIC_STATE_DISCARD_RECTANGLE_EXT = (VkDynamicState) 1000099000;
+
+#endif // !VK_EXT_discard_rectangles
+
+#endif // SK_VULKAN
+
+#endif // GrVkDefines_DEFINED
diff --git a/include/gpu/vk/GrVkInterface.h b/include/gpu/vk/GrVkInterface.h
index 05ce561..a56df31 100644
--- a/include/gpu/vk/GrVkInterface.h
+++ b/include/gpu/vk/GrVkInterface.h
@@ -203,9 +203,13 @@
         VkPtr<PFN_vkCmdEndRenderPass> fCmdEndRenderPass;
         VkPtr<PFN_vkCmdExecuteCommands> fCmdExecuteCommands;
 
+        VkPtr<PFN_vkGetPhysicalDeviceProperties2KHR> fGetPhysicalDeviceProperties2KHR;
+
         VkPtr<PFN_vkCreateDebugReportCallbackEXT> fCreateDebugReportCallbackEXT;
         VkPtr<PFN_vkDebugReportMessageEXT> fDebugReportMessageEXT;
         VkPtr<PFN_vkDestroyDebugReportCallbackEXT> fDestroyDebugReportCallbackEXT;
+
+        VkPtr<PFN_vkCmdSetDiscardRectangleEXT> fCmdSetDiscardRectangleEXT;
     } fFunctions;
 
 };
diff --git a/src/gpu/GrCaps.cpp b/src/gpu/GrCaps.cpp
index 2e25845..9705320 100644
--- a/src/gpu/GrCaps.cpp
+++ b/src/gpu/GrCaps.cpp
@@ -61,6 +61,8 @@
 
     fMapBufferFlags = kNone_MapFlags;
 
+    fWindowRectsSupport = WindowRectsSupport::kNone;
+
     fMaxVertexAttributes = 0;
     fMaxRenderTargetSize = 1;
     fMaxTextureSize = 1;
@@ -194,6 +196,14 @@
     SkASSERT(!this->isConfigRenderable(kUnknown_GrPixelConfig, true));
     SkASSERT(!this->isConfigTexturable(kUnknown_GrPixelConfig));
 
+    const char* windowRectsSupportName = "<invalid>";
+    switch (fWindowRectsSupport) {
+        case WindowRectsSupport::kNone: windowRectsSupportName = "None"; break;
+        case WindowRectsSupport::kDrawOnly: windowRectsSupportName = "DrawOnly"; break;
+        case WindowRectsSupport::kDrawAndClear: windowRectsSupportName = "DrawAndClear"; break;
+    }
+    writer->appendString("Window Rectangles Support", windowRectsSupportName);
+
     writer->beginArray("configs");
 
     for (size_t i = 1; i < kGrPixelConfigCnt; ++i) {
diff --git a/src/gpu/GrGpuCommandBuffer.h b/src/gpu/GrGpuCommandBuffer.h
index 6963d23..92d5204 100644
--- a/src/gpu/GrGpuCommandBuffer.h
+++ b/src/gpu/GrGpuCommandBuffer.h
@@ -108,7 +108,7 @@
     virtual void inlineUpload(GrOpFlushState*, GrDeferredTextureUploadFn&) = 0;
 
     /**
-     * Clear the owned render target. Ignores the draw state and clip.
+     * Clear the owned render target.
      */
     void clear(const GrFixedClip&, GrColor);
 
diff --git a/src/gpu/GrReducedClip.cpp b/src/gpu/GrReducedClip.cpp
index bc3286b..d69a054 100644
--- a/src/gpu/GrReducedClip.cpp
+++ b/src/gpu/GrReducedClip.cpp
@@ -744,7 +744,7 @@
     }
 
     bool initialState = InitialState::kAllIn == this->initialState();
-    renderTargetContext->priv().clearStencilClip(stencilClip.fixedClip(), initialState);
+    renderTargetContext->priv().clearStencilClip(stencilClip.fixedClip(), initialState, true);
 
     // walk through each clip element and perform its set op with the existing clip.
     for (ElementList::Iter iter(fMaskElements); iter.get(); iter.next()) {
diff --git a/src/gpu/GrRenderTarget.cpp b/src/gpu/GrRenderTarget.cpp
index 585a512..a361190 100644
--- a/src/gpu/GrRenderTarget.cpp
+++ b/src/gpu/GrRenderTarget.cpp
@@ -28,7 +28,7 @@
     SkASSERT(desc.fFlags & kRenderTarget_GrSurfaceFlag);
     SkASSERT(!(fFlags & GrRenderTargetFlags::kMixedSampled) || fSampleCnt > 0);
     SkASSERT(!(fFlags & GrRenderTargetFlags::kWindowRectsSupport) ||
-             gpu->caps()->maxWindowRectangles() > 0);
+             GrCaps::WindowRectsSupport::kNone != gpu->caps()->windowRectsSupport());
     fResolveRect.setLargestInverted();
 }
 
diff --git a/src/gpu/GrRenderTargetContext.cpp b/src/gpu/GrRenderTargetContext.cpp
index e1ce24c..faa0dbb 100644
--- a/src/gpu/GrRenderTargetContext.cpp
+++ b/src/gpu/GrRenderTargetContext.cpp
@@ -345,7 +345,8 @@
     if (isFull) {
         this->getRTOpList()->fullClear(*this->caps(), color);
     } else {
-        std::unique_ptr<GrOp> op(GrClearOp::Make(clip, color, this->asSurfaceProxy()));
+        std::unique_ptr<GrOp> op(GrClearOp::Make(clip, color, canIgnoreClip, this->asSurfaceProxy(),
+                                                 *fContext->caps()));
         if (!op) {
             return;
         }
@@ -605,7 +606,8 @@
                                                     *fRenderTargetContext->fContext->caps());
 }
 
-void GrRenderTargetContextPriv::clearStencilClip(const GrFixedClip& clip, bool insideStencilMask) {
+void GrRenderTargetContextPriv::clearStencilClip(const GrFixedClip& clip, bool insideStencilMask,
+                                                 bool canIgnoreClip) {
     ASSERT_SINGLE_OWNER_PRIV
     RETURN_IF_ABANDONED_PRIV
     SkDEBUGCODE(fRenderTargetContext->validate();)
@@ -615,8 +617,9 @@
     AutoCheckFlush acf(fRenderTargetContext->drawingManager());
 
     std::unique_ptr<GrOp> op(GrClearStencilClipOp::Make(
-                                                 clip, insideStencilMask,
-                                                 fRenderTargetContext->fRenderTargetProxy.get()));
+                                                 clip, insideStencilMask, canIgnoreClip,
+                                                 fRenderTargetContext->fRenderTargetProxy.get(),
+                                                 *fRenderTargetContext->fContext->caps()));
     if (!op) {
         return;
     }
diff --git a/src/gpu/GrRenderTargetContextPriv.h b/src/gpu/GrRenderTargetContextPriv.h
index 2cfd5c8..a7f0070 100644
--- a/src/gpu/GrRenderTargetContextPriv.h
+++ b/src/gpu/GrRenderTargetContextPriv.h
@@ -44,7 +44,7 @@
 
     void clear(const GrFixedClip&, const GrColor, bool canIgnoreClip);
 
-    void clearStencilClip(const GrFixedClip&, bool insideStencilMask);
+    void clearStencilClip(const GrFixedClip&, bool insideStencilMask, bool canIgnoreClip);
 
     /*
      * Some portions of the code, which use approximate-match rendertargets (i.e., ImageFilters),
diff --git a/src/gpu/GrRenderTargetOpList.cpp b/src/gpu/GrRenderTargetOpList.cpp
index 09486c7..dc1176c 100644
--- a/src/gpu/GrRenderTargetOpList.cpp
+++ b/src/gpu/GrRenderTargetOpList.cpp
@@ -229,7 +229,8 @@
         return;
     }
 
-    std::unique_ptr<GrClearOp> op(GrClearOp::Make(GrFixedClip::Disabled(), color, fTarget.get()));
+    std::unique_ptr<GrClearOp> op(GrClearOp::Make(GrFixedClip::Disabled(), color, true,
+                                                  fTarget.get(), caps));
     if (!op) {
         return;
     }
diff --git a/src/gpu/GrRenderTargetProxy.cpp b/src/gpu/GrRenderTargetProxy.cpp
index 9453ce8..ebe7d72 100644
--- a/src/gpu/GrRenderTargetProxy.cpp
+++ b/src/gpu/GrRenderTargetProxy.cpp
@@ -29,7 +29,7 @@
     if (caps.usesMixedSamples() && fSampleCnt > 0) {
         fRenderTargetFlags |= GrRenderTargetFlags::kMixedSampled;
     }
-    if (caps.maxWindowRectangles() > 0) {
+    if (GrCaps::WindowRectsSupport::kNone != caps.windowRectsSupport()) {
         fRenderTargetFlags |= GrRenderTargetFlags::kWindowRectsSupport;
     }
 }
diff --git a/src/gpu/gl/GrGLCaps.cpp b/src/gpu/gl/GrGLCaps.cpp
index 9a454ef..340e42a 100644
--- a/src/gpu/gl/GrGLCaps.cpp
+++ b/src/gpu/gl/GrGLCaps.cpp
@@ -559,6 +559,7 @@
 #endif
 
     if (ctxInfo.hasExtension("GL_EXT_window_rectangles")) {
+        fWindowRectsSupport = WindowRectsSupport::kDrawAndClear;
         GR_GL_GetIntegerv(gli, GR_GL_MAX_WINDOW_RECTANGLES, &fMaxWindowRectangles);
     }
 
diff --git a/src/gpu/gl/GrGLGpu.cpp b/src/gpu/gl/GrGLGpu.cpp
index 6b194f9..79448e0 100644
--- a/src/gpu/gl/GrGLGpu.cpp
+++ b/src/gpu/gl/GrGLGpu.cpp
@@ -1749,9 +1749,12 @@
 #ifndef USE_NSIGHT
     typedef GrWindowRectsState::Mode Mode;
     SkASSERT(!windowState.enabled() || rt->renderFBOID()); // Window rects can't be used on-screen.
+    SkASSERT(!windowState.enabled() ||
+             GrCaps::WindowRectsSupport::kDrawAndClear == this->caps()->windowRectsSupport());
     SkASSERT(windowState.numWindows() <= this->caps()->maxWindowRectangles());
+    SkASSERT(this->caps()->maxWindowRectangles() <= GrWindowRectangles::kMaxWindows);
 
-    if (!this->caps()->maxWindowRectangles() ||
+    if (GrCaps::WindowRectsSupport::kNone == this->caps()->windowRectsSupport() ||
         fHWWindowRectsState.knownEqualTo(origin, rt->getViewport(), windowState)) {
         return;
     }
@@ -1776,7 +1779,8 @@
 
 void GrGLGpu::disableWindowRectangles() {
 #ifndef USE_NSIGHT
-    if (!this->caps()->maxWindowRectangles() || fHWWindowRectsState.knownDisabled()) {
+    if (GrCaps::WindowRectsSupport::kNone == this->caps()->windowRectsSupport() ||
+        fHWWindowRectsState.knownDisabled()) {
         return;
     }
     GL_CALL(WindowRectangles(GR_GL_EXCLUSIVE, 0, nullptr));
diff --git a/src/gpu/gl/GrGLRenderTarget.cpp b/src/gpu/gl/GrGLRenderTarget.cpp
index 3aa632c..ef69f3e 100644
--- a/src/gpu/gl/GrGLRenderTarget.cpp
+++ b/src/gpu/gl/GrGLRenderTarget.cpp
@@ -43,7 +43,7 @@
         SkASSERT(glCaps.usesMixedSamples() && idDesc.fRTFBOID); // FBO 0 can't be mixed sampled.
         flags |= GrRenderTargetFlags::kMixedSampled;
     }
-    if (glCaps.maxWindowRectangles() > 0 && idDesc.fRTFBOID) {
+    if (GrCaps::WindowRectsSupport::kNone != glCaps.windowRectsSupport() && idDesc.fRTFBOID) {
         flags |= GrRenderTargetFlags::kWindowRectsSupport;
     }
     return flags;
diff --git a/src/gpu/ops/GrClearOp.cpp b/src/gpu/ops/GrClearOp.cpp
index 702edcc..9c1805a 100644
--- a/src/gpu/ops/GrClearOp.cpp
+++ b/src/gpu/ops/GrClearOp.cpp
@@ -7,15 +7,21 @@
 
 #include "GrClearOp.h"
 
+#include "GrCaps.h"
 #include "GrGpuCommandBuffer.h"
 #include "GrOpFlushState.h"
 #include "GrResourceProvider.h"
 
-GrClearOp::GrClearOp(const GrFixedClip& clip, GrColor color, GrSurfaceProxy* proxy)
-        : INHERITED(ClassID())
+GrClearOp::GrClearOp(const GrFixedClip& clip, GrColor color, bool canIgnoreClip,
+                     GrSurfaceProxy* proxy, const GrCaps& caps) : INHERITED(ClassID())
         , fClip(clip)
         , fColor(color) {
     const SkIRect rtRect = SkIRect::MakeWH(proxy->width(), proxy->height());
+    if (fClip.hasWindowRectangles() &&
+        GrCaps::WindowRectsSupport::kDrawAndClear != caps.windowRectsSupport() &&
+        canIgnoreClip) {
+        fClip.disableWindowRectangles();
+    }
     if (fClip.scissorEnabled()) {
         // Don't let scissors extend outside the RT. This may improve op combining.
         if (!fClip.intersect(rtRect)) {
diff --git a/src/gpu/ops/GrClearOp.h b/src/gpu/ops/GrClearOp.h
index 56ecb13..4f6ee62 100644
--- a/src/gpu/ops/GrClearOp.h
+++ b/src/gpu/ops/GrClearOp.h
@@ -18,13 +18,15 @@
     DEFINE_OP_CLASS_ID
 
     static std::unique_ptr<GrClearOp> Make(const GrFixedClip& clip, GrColor color,
-                                           GrSurfaceProxy* dstProxy) {
+                                           bool canIgnoreClip, GrSurfaceProxy* dstProxy,
+                                           const GrCaps& caps) {
         const SkIRect rect = SkIRect::MakeWH(dstProxy->width(), dstProxy->height());
         if (clip.scissorEnabled() && !SkIRect::Intersects(clip.scissorRect(), rect)) {
             return nullptr;
         }
 
-        return std::unique_ptr<GrClearOp>(new GrClearOp(clip, color, dstProxy));
+        return std::unique_ptr<GrClearOp>(
+                new GrClearOp(clip, color, canIgnoreClip, dstProxy, caps));
     }
 
     static std::unique_ptr<GrClearOp> Make(const SkIRect& rect, GrColor color,
@@ -54,7 +56,8 @@
     void setColor(GrColor color) { fColor = color; }
 
 private:
-    GrClearOp(const GrFixedClip& clip, GrColor color, GrSurfaceProxy* proxy);
+    GrClearOp(const GrFixedClip& clip, GrColor color, bool canIgnoreClip, GrSurfaceProxy* proxy,
+              const GrCaps&);
 
     GrClearOp(const SkIRect& rect, GrColor color, bool fullScreen)
         : INHERITED(ClassID())
diff --git a/src/gpu/ops/GrClearStencilClipOp.h b/src/gpu/ops/GrClearStencilClipOp.h
index ffd2fd9..9f23a66 100644
--- a/src/gpu/ops/GrClearStencilClipOp.h
+++ b/src/gpu/ops/GrClearStencilClipOp.h
@@ -9,6 +9,7 @@
 #define GrClearStencilClipOp_DEFINED
 
 #include "GrFixedClip.h"
+#include "GrCaps.h"
 #include "GrGpuCommandBuffer.h"
 #include "GrOp.h"
 #include "GrOpFlushState.h"
@@ -19,8 +20,10 @@
     DEFINE_OP_CLASS_ID
 
     static std::unique_ptr<GrOp> Make(const GrFixedClip& clip, bool insideStencilMask,
-                                      GrRenderTargetProxy* proxy) {
-        return std::unique_ptr<GrOp>(new GrClearStencilClipOp(clip, insideStencilMask, proxy));
+                                      bool canIgnoreClip, GrRenderTargetProxy* proxy,
+                                      const GrCaps& caps) {
+        return std::unique_ptr<GrOp>(
+                new GrClearStencilClipOp(clip, insideStencilMask, canIgnoreClip, proxy, caps));
     }
 
     const char* name() const override { return "ClearStencilClip"; }
@@ -39,11 +42,16 @@
     }
 
 private:
-    GrClearStencilClipOp(const GrFixedClip& clip, bool insideStencilMask,
-                         GrRenderTargetProxy* proxy)
+    GrClearStencilClipOp(const GrFixedClip& clip, bool insideStencilMask, bool canIgnoreClip,
+                         GrRenderTargetProxy* proxy, const GrCaps& caps)
             : INHERITED(ClassID())
-            , fClip(clip)
-            , fInsideStencilMask(insideStencilMask) {
+            , fInsideStencilMask(insideStencilMask)
+            , fClip(clip) {
+        if (fClip.hasWindowRectangles() &&
+            GrCaps::WindowRectsSupport::kDrawAndClear != caps.windowRectsSupport() &&
+            canIgnoreClip) {
+            fClip.disableWindowRectangles();
+        }
         const SkRect& bounds = fClip.scissorEnabled()
                                             ? SkRect::Make(fClip.scissorRect())
                                             : SkRect::MakeIWH(proxy->width(), proxy->height());
@@ -59,8 +67,8 @@
         state->rtCommandBuffer()->clearStencilClip(fClip, fInsideStencilMask);
     }
 
-    const GrFixedClip fClip;
-    const bool        fInsideStencilMask;
+    const bool    fInsideStencilMask;
+    GrFixedClip   fClip;
 
     typedef GrOp INHERITED;
 };
diff --git a/src/gpu/vk/GrVkBackendContext.cpp b/src/gpu/vk/GrVkBackendContext.cpp
index e473178..a602a50 100644
--- a/src/gpu/vk/GrVkBackendContext.cpp
+++ b/src/gpu/vk/GrVkBackendContext.cpp
@@ -76,6 +76,10 @@
     SkTArray<const char*> instanceLayerNames;
     SkTArray<const char*> instanceExtensionNames;
     uint32_t extensionFlags = 0;
+    if (extensions.hasInstanceExtension(VK_KHR_GET_PHYSICAL_DEVICE_PROPERTIES_2_EXTENSION_NAME)) {
+        instanceExtensionNames.push_back(VK_KHR_GET_PHYSICAL_DEVICE_PROPERTIES_2_EXTENSION_NAME);
+        extensionFlags |= kKHR_get_physical_device_properties2_GrVkExtensionFlag;
+    }
 #ifdef SK_ENABLE_VK_LAYERS
     for (size_t i = 0; i < SK_ARRAY_COUNT(kDebugLayerNames); ++i) {
         if (extensions.hasInstanceLayer(kDebugLayerNames[i])) {
@@ -87,7 +91,6 @@
         extensionFlags |= kEXT_debug_report_GrVkExtensionFlag;
     }
 #endif
-
     if (extensions.hasInstanceExtension(VK_KHR_SURFACE_EXTENSION_NAME)) {
         instanceExtensionNames.push_back(VK_KHR_SURFACE_EXTENSION_NAME);
         extensionFlags |= kKHR_surface_GrVkExtensionFlag;
@@ -223,6 +226,10 @@
         }
     }
 #endif
+    if (extensions.hasDeviceExtension(VK_EXT_DISCARD_RECTANGLES_EXTENSION_NAME)) {
+        deviceExtensionNames.push_back(VK_EXT_DISCARD_RECTANGLES_EXTENSION_NAME);
+        extensionFlags |= kEXT_discard_rectangles_GrVkExtensionFlag;
+    }
     if (extensions.hasDeviceExtension(VK_KHR_SWAPCHAIN_EXTENSION_NAME)) {
         deviceExtensionNames.push_back(VK_KHR_SWAPCHAIN_EXTENSION_NAME);
         extensionFlags |= kKHR_swapchain_GrVkExtensionFlag;
diff --git a/src/gpu/vk/GrVkCaps.cpp b/src/gpu/vk/GrVkCaps.cpp
index 2b24205..9f635e2 100644
--- a/src/gpu/vk/GrVkCaps.cpp
+++ b/src/gpu/vk/GrVkCaps.cpp
@@ -75,9 +75,30 @@
 
 void GrVkCaps::init(const GrContextOptions& contextOptions, const GrVkInterface* vkInterface,
                     VkPhysicalDevice physDev, uint32_t featureFlags, uint32_t extensionFlags) {
+    VkPhysicalDeviceProperties2KHR khrProperties;
+    if (SkToBool(extensionFlags & kKHR_get_physical_device_properties2_GrVkExtensionFlag)) {
+        khrProperties.sType = VK_STRUCTURE_TYPE_PHYSICAL_DEVICE_PROPERTIES_2_KHR;
+        khrProperties.pNext = nullptr;
 
-    VkPhysicalDeviceProperties properties;
-    GR_VK_CALL(vkInterface, GetPhysicalDeviceProperties(physDev, &properties));
+        VkPhysicalDeviceDiscardRectanglePropertiesEXT discardRectsProperties;
+        if (SkToBool(extensionFlags & kEXT_discard_rectangles_GrVkExtensionFlag)) {
+            discardRectsProperties.sType =
+                    VK_STRUCTURE_TYPE_PHYSICAL_DEVICE_DISCARD_RECTANGLE_PROPERTIES_EXT;
+            discardRectsProperties.pNext = khrProperties.pNext;
+            khrProperties.pNext = &discardRectsProperties;
+        }
+
+        GR_VK_CALL(vkInterface, GetPhysicalDeviceProperties2KHR(physDev, &khrProperties));
+
+        if (SkToBool(extensionFlags & kEXT_discard_rectangles_GrVkExtensionFlag)) {
+            fWindowRectsSupport = WindowRectsSupport::kDrawOnly;
+            fMaxWindowRectangles = discardRectsProperties.maxDiscardRectangles;
+        }
+    } else {
+        GR_VK_CALL(vkInterface, GetPhysicalDeviceProperties(physDev, &khrProperties.properties));
+    }
+
+    const VkPhysicalDeviceProperties& properties = khrProperties.properties;
 
     VkPhysicalDeviceMemoryProperties memoryProperties;
     GR_VK_CALL(vkInterface, GetPhysicalDeviceMemoryProperties(physDev, &memoryProperties));
diff --git a/src/gpu/vk/GrVkCommandBuffer.cpp b/src/gpu/vk/GrVkCommandBuffer.cpp
index 8a17a4f..ba19168 100644
--- a/src/gpu/vk/GrVkCommandBuffer.cpp
+++ b/src/gpu/vk/GrVkCommandBuffer.cpp
@@ -34,6 +34,11 @@
     memset(&fCachedScissor, 0, sizeof(VkRect2D));
     fCachedScissor.offset.x = -1; // Scissor offset must be greater that 0 to be valid
 
+    memset(&fCachedDiscardRectangles, 0, sizeof(fCachedDiscardRectangles));
+    for (int i = 0; i < GrWindowRectangles::kMaxWindows; ++i) {
+        fCachedDiscardRectangles[i].offset.x = -1; // Negative offsets are invalid.
+    }
+
     for (int i = 0; i < 4; ++i) {
         fCachedBlendConstant[i] = -1.0;
     }
@@ -323,6 +328,24 @@
     }
 }
 
+void GrVkCommandBuffer::setDiscardRectangles(const GrVkGpu* gpu,
+                                             uint32_t firstDiscardRectangle,
+                                             uint32_t discardRectangleCount,
+                                             const VkRect2D* discardRectangles) {
+    SkASSERT(fIsActive);
+    SkASSERT(firstDiscardRectangle + discardRectangleCount <= gpu->vkCaps().maxWindowRectangles());
+    SkASSERT(gpu->vkCaps().maxWindowRectangles() <= GrWindowRectangles::kMaxWindows);
+    if (memcmp(discardRectangles, &fCachedDiscardRectangles[firstDiscardRectangle],
+               discardRectangleCount * sizeof(VkRect2D))) {
+        GR_VK_CALL(gpu->vkInterface(), CmdSetDiscardRectangleEXT(fCmdBuffer,
+                                                                 firstDiscardRectangle,
+                                                                 discardRectangleCount,
+                                                                 discardRectangles));
+        memcpy(&fCachedDiscardRectangles[firstDiscardRectangle], discardRectangles,
+               discardRectangleCount * sizeof(VkRect2D));
+    }
+}
+
 void GrVkCommandBuffer::setBlendConstants(const GrVkGpu* gpu,
                                           const float blendConstants[4]) {
     SkASSERT(fIsActive);
diff --git a/src/gpu/vk/GrVkCommandBuffer.h b/src/gpu/vk/GrVkCommandBuffer.h
index 7d16242..cf6dc2a 100644
--- a/src/gpu/vk/GrVkCommandBuffer.h
+++ b/src/gpu/vk/GrVkCommandBuffer.h
@@ -80,6 +80,11 @@
                     uint32_t scissorCount,
                     const VkRect2D* scissors);
 
+    void setDiscardRectangles(const GrVkGpu* gpu,
+                              uint32_t firstDiscardRectangle,
+                              uint32_t discardRectangleCount,
+                              const VkRect2D* discardRectangles);
+
     void setBlendConstants(const GrVkGpu* gpu, const float blendConstants[4]);
 
     // Commands that only work inside of a render pass
@@ -167,6 +172,7 @@
     // Cached values used for dynamic state updates
     VkViewport fCachedViewport;
     VkRect2D   fCachedScissor;
+    VkRect2D   fCachedDiscardRectangles[GrWindowRectangles::kMaxWindows];
     float      fCachedBlendConstant[4];
 };
 
diff --git a/src/gpu/vk/GrVkGpuCommandBuffer.cpp b/src/gpu/vk/GrVkGpuCommandBuffer.cpp
index 52dfede..a14ce8c 100644
--- a/src/gpu/vk/GrVkGpuCommandBuffer.cpp
+++ b/src/gpu/vk/GrVkGpuCommandBuffer.cpp
@@ -251,10 +251,11 @@
 }
 
 void GrVkGpuRTCommandBuffer::onClearStencilClip(const GrFixedClip& clip, bool insideStencilMask) {
-    SkASSERT(!clip.hasWindowRectangles());
-
     CommandBufferInfo& cbInfo = fCommandBufferInfos[fCurrentCmdInfo];
 
+    // We ignore window rectangles as they are not supported by Vulkan during clear.
+    SkASSERT(!clip.hasWindowRectangles());
+
     GrStencilAttachment* sb = fRenderTarget->renderTargetPriv().getStencilAttachment();
     // this should only be called internally when we know we have a
     // stencil buffer.
@@ -313,7 +314,7 @@
 void GrVkGpuRTCommandBuffer::onClear(const GrFixedClip& clip, GrColor color) {
     GrVkRenderTarget* vkRT = static_cast<GrVkRenderTarget*>(fRenderTarget);
 
-    // parent class should never let us get here with no RT
+    // We ignore window rectangles as they are not supported by Vulkan during clear.
     SkASSERT(!clip.hasWindowRectangles());
 
     CommandBufferInfo& cbInfo = fCommandBufferInfos[fCurrentCmdInfo];
@@ -527,6 +528,7 @@
 
     GrRenderTarget* rt = pipeline.renderTarget();
 
+    GrVkPipeline::SetDynamicViewportState(fGpu, cbInfo.currentCmdBuf(), rt);
     if (!pipeline.getScissorState().enabled()) {
         GrVkPipeline::SetDynamicScissorRectState(fGpu, cbInfo.currentCmdBuf(),
                                                  rt, pipeline.proxy()->origin(),
@@ -536,7 +538,13 @@
                                                  rt, pipeline.proxy()->origin(),
                                                  pipeline.getScissorState().rect());
     }
-    GrVkPipeline::SetDynamicViewportState(fGpu, cbInfo.currentCmdBuf(), rt);
+    if (pipeline.getWindowRectsState().enabled()) {
+        // No need to check hasDynamicState -- window rectangles aren't currently included in
+        // GrPipeline::DynamicState.
+        GrVkPipeline::SetDynamicDiscardRectanglesState(fGpu, cbInfo.currentCmdBuf(),
+                                                       rt, pipeline.proxy()->origin(),
+                                                       pipeline.getWindowRectsState().windows());
+    }
     GrVkPipeline::SetDynamicBlendConstantState(fGpu, cbInfo.currentCmdBuf(), rt->config(),
                                                pipeline.getXferProcessor());
 
diff --git a/src/gpu/vk/GrVkInterface.cpp b/src/gpu/vk/GrVkInterface.cpp
index dedc264..04589cd 100644
--- a/src/gpu/vk/GrVkInterface.cpp
+++ b/src/gpu/vk/GrVkInterface.cpp
@@ -59,6 +59,11 @@
     ACQUIRE_PROC(EnumerateDeviceExtensionProperties, instance, VK_NULL_HANDLE);
     ACQUIRE_PROC(EnumerateDeviceLayerProperties, instance, VK_NULL_HANDLE);
 
+    if (extensionFlags & kKHR_get_physical_device_properties2_GrVkExtensionFlag) {
+        // Also Instance Proc.
+        ACQUIRE_PROC(GetPhysicalDeviceProperties2KHR, instance, VK_NULL_HANDLE);
+    }
+
     if (extensionFlags & kEXT_debug_report_GrVkExtensionFlag) {
         // Also instance Procs.
         ACQUIRE_PROC(CreateDebugReportCallbackEXT, instance, VK_NULL_HANDLE);
@@ -186,6 +191,11 @@
     ACQUIRE_PROC(CmdNextSubpass, VK_NULL_HANDLE, device);
     ACQUIRE_PROC(CmdEndRenderPass, VK_NULL_HANDLE, device);
     ACQUIRE_PROC(CmdExecuteCommands, VK_NULL_HANDLE, device);
+
+    if (extensionFlags & kEXT_discard_rectangles_GrVkExtensionFlag) {
+        // Also Device Proc.
+        ACQUIRE_PROC(CmdSetDiscardRectangleEXT, VK_NULL_HANDLE, device);
+    }
 }
 
 #ifdef SK_DEBUG
@@ -338,6 +348,12 @@
         RETURN_FALSE_INTERFACE
     }
 
+    if (extensionFlags & kKHR_get_physical_device_properties2_GrVkExtensionFlag) {
+        if (nullptr == fFunctions.fGetPhysicalDeviceProperties2KHR) {
+            RETURN_FALSE_INTERFACE
+        }
+    }
+
     if (extensionFlags & kEXT_debug_report_GrVkExtensionFlag) {
         if (nullptr == fFunctions.fCreateDebugReportCallbackEXT ||
             nullptr == fFunctions.fDebugReportMessageEXT ||
@@ -345,6 +361,13 @@
             RETURN_FALSE_INTERFACE
         }
     }
+
+    if (extensionFlags & kEXT_discard_rectangles_GrVkExtensionFlag) {
+        if (nullptr == fFunctions.fCmdSetDiscardRectangleEXT) {
+            RETURN_FALSE_INTERFACE
+        }
+    }
+
     return true;
 }
 
diff --git a/src/gpu/vk/GrVkPipeline.cpp b/src/gpu/vk/GrVkPipeline.cpp
index a247078..5891f4c 100644
--- a/src/gpu/vk/GrVkPipeline.cpp
+++ b/src/gpu/vk/GrVkPipeline.cpp
@@ -239,6 +239,21 @@
     SkASSERT(viewportInfo->viewportCount == viewportInfo->scissorCount);
 }
 
+static void setup_discard_rectangles_state(const GrWindowRectsState& windowState,
+                                           const GrCaps* caps,
+                                           VkPipelineDiscardRectangleStateCreateInfoEXT* info) {
+    SkASSERT(windowState.numWindows() <= caps->maxWindowRectangles());
+    memset(info, 0, sizeof(VkPipelineDiscardRectangleStateCreateInfoEXT));
+    info->sType = VK_STRUCTURE_TYPE_PIPELINE_DISCARD_RECTANGLE_STATE_CREATE_INFO_EXT;
+    info->pNext = nullptr;
+    info->flags = 0;
+    info->discardRectangleMode =
+            GrWindowRectsState::Mode::kExclusive == windowState.mode() ?
+            VK_DISCARD_RECTANGLE_MODE_EXCLUSIVE_EXT : VK_DISCARD_RECTANGLE_MODE_INCLUSIVE_EXT;
+    info->discardRectangleCount = windowState.numWindows();
+    info->pDiscardRectangles = nullptr; // This is set dynamically
+}
+
 static void setup_multisample_state(const GrPipeline& pipeline,
                                     const GrPrimitiveProcessor& primProc,
                                     const GrCaps* caps,
@@ -409,17 +424,21 @@
     rasterInfo->lineWidth = 1.0f;
 }
 
-static void setup_dynamic_state(VkPipelineDynamicStateCreateInfo* dynamicInfo,
-                                VkDynamicState* dynamicStates) {
+static void setup_dynamic_state(const GrPipeline& pipeline,
+                                VkPipelineDynamicStateCreateInfo* dynamicInfo,
+                                SkSTArray<4, VkDynamicState>* dynamicStates) {
     memset(dynamicInfo, 0, sizeof(VkPipelineDynamicStateCreateInfo));
     dynamicInfo->sType = VK_STRUCTURE_TYPE_PIPELINE_DYNAMIC_STATE_CREATE_INFO;
     dynamicInfo->pNext = VK_NULL_HANDLE;
     dynamicInfo->flags = 0;
-    dynamicStates[0] = VK_DYNAMIC_STATE_VIEWPORT;
-    dynamicStates[1] = VK_DYNAMIC_STATE_SCISSOR;
-    dynamicStates[2] = VK_DYNAMIC_STATE_BLEND_CONSTANTS;
-    dynamicInfo->dynamicStateCount = 3;
-    dynamicInfo->pDynamicStates = dynamicStates;
+    dynamicStates->push_back(VK_DYNAMIC_STATE_VIEWPORT);
+    dynamicStates->push_back(VK_DYNAMIC_STATE_SCISSOR);
+    if (pipeline.getWindowRectsState().enabled()) {
+        dynamicStates->push_back(VK_DYNAMIC_STATE_DISCARD_RECTANGLE_EXT);
+    }
+    dynamicStates->push_back(VK_DYNAMIC_STATE_BLEND_CONSTANTS);
+    dynamicInfo->dynamicStateCount = dynamicStates->count();
+    dynamicInfo->pDynamicStates = dynamicStates->begin();
 }
 
 GrVkPipeline* GrVkPipeline::Create(GrVkGpu* gpu, const GrPipeline& pipeline,
@@ -458,9 +477,9 @@
     VkPipelineRasterizationStateCreateInfo rasterInfo;
     setup_raster_state(pipeline, gpu->caps(), &rasterInfo);
 
-    VkDynamicState dynamicStates[3];
+    SkSTArray<4, VkDynamicState> dynamicStates;
     VkPipelineDynamicStateCreateInfo dynamicInfo;
-    setup_dynamic_state(&dynamicInfo, dynamicStates);
+    setup_dynamic_state(pipeline, &dynamicInfo, &dynamicStates);
 
     VkGraphicsPipelineCreateInfo pipelineCreateInfo;
     memset(&pipelineCreateInfo, 0, sizeof(VkGraphicsPipelineCreateInfo));
@@ -484,6 +503,15 @@
     pipelineCreateInfo.basePipelineHandle = VK_NULL_HANDLE;
     pipelineCreateInfo.basePipelineIndex = -1;
 
+    VkPipelineDiscardRectangleStateCreateInfoEXT discardRectanglesInfo;
+    if (pipeline.getWindowRectsState().enabled()) {
+        SkASSERT(GrCaps::WindowRectsSupport::kNone != gpu->caps()->windowRectsSupport());
+        setup_discard_rectangles_state(pipeline.getWindowRectsState(), gpu->caps(),
+                                       &discardRectanglesInfo);
+        discardRectanglesInfo.pNext = pipelineCreateInfo.pNext;
+        pipelineCreateInfo.pNext = &discardRectanglesInfo;
+    }
+
     VkPipeline vkPipeline;
     VkResult err = GR_VK_CALL(gpu->vkInterface(), CreateGraphicsPipelines(gpu->device(),
                                                                           cache, 1,
@@ -500,31 +528,6 @@
     GR_VK_CALL(gpu->vkInterface(), DestroyPipeline(gpu->device(), fPipeline, nullptr));
 }
 
-void GrVkPipeline::SetDynamicScissorRectState(GrVkGpu* gpu,
-                                              GrVkCommandBuffer* cmdBuffer,
-                                              const GrRenderTarget* renderTarget,
-                                              GrSurfaceOrigin rtOrigin,
-                                              SkIRect scissorRect) {
-    if (!scissorRect.intersect(SkIRect::MakeWH(renderTarget->width(), renderTarget->height()))) {
-        scissorRect.setEmpty();
-    }
-
-    VkRect2D scissor;
-    scissor.offset.x = scissorRect.fLeft;
-    scissor.extent.width = scissorRect.width();
-    if (kTopLeft_GrSurfaceOrigin == rtOrigin) {
-        scissor.offset.y = scissorRect.fTop;
-    } else {
-        SkASSERT(kBottomLeft_GrSurfaceOrigin == rtOrigin);
-        scissor.offset.y = renderTarget->height() - scissorRect.fBottom;
-    }
-    scissor.extent.height = scissorRect.height();
-
-    SkASSERT(scissor.offset.x >= 0);
-    SkASSERT(scissor.offset.y >= 0);
-    cmdBuffer->setScissor(gpu, 0, 1, &scissor);
-}
-
 void GrVkPipeline::SetDynamicViewportState(GrVkGpu* gpu,
                                            GrVkCommandBuffer* cmdBuffer,
                                            const GrRenderTarget* renderTarget) {
@@ -539,6 +542,48 @@
     cmdBuffer->setViewport(gpu, 0, 1, &viewport);
 }
 
+inline static void skrect_to_vkrect(SkIRect skrect,
+                                    const GrRenderTarget* renderTarget,
+                                    GrSurfaceOrigin rtOrigin,
+                                    VkRect2D* vkrect) {
+    if (!skrect.intersect(SkIRect::MakeWH(renderTarget->width(), renderTarget->height()))) {
+        skrect.setEmpty();
+    }
+
+    vkrect->offset.x = skrect.fLeft;
+    vkrect->extent.width = skrect.width();
+    if (kTopLeft_GrSurfaceOrigin == rtOrigin) {
+        vkrect->offset.y = skrect.fTop;
+    } else {
+        SkASSERT(kBottomLeft_GrSurfaceOrigin == rtOrigin);
+        vkrect->offset.y = renderTarget->height() - skrect.fBottom;
+    }
+    vkrect->extent.height = skrect.height();
+}
+
+void GrVkPipeline::SetDynamicScissorRectState(GrVkGpu* gpu,
+                                              GrVkCommandBuffer* cmdBuffer,
+                                              const GrRenderTarget* renderTarget,
+                                              GrSurfaceOrigin rtOrigin,
+                                              SkIRect scissorRect) {
+    VkRect2D scissor;
+    skrect_to_vkrect(scissorRect, renderTarget, rtOrigin, &scissor);
+    cmdBuffer->setScissor(gpu, 0, 1, &scissor);
+}
+
+void GrVkPipeline::SetDynamicDiscardRectanglesState(GrVkGpu* gpu,
+                                                    GrVkCommandBuffer* cmdBuffer,
+                                                    const GrRenderTarget* renderTarget,
+                                                    GrSurfaceOrigin rtOrigin,
+                                                    const GrWindowRectangles& windowRectangles) {
+    const SkIRect* skrects = windowRectangles.data();
+    VkRect2D vkrects[GrWindowRectangles::kMaxWindows];
+    for (int i = 0; i < windowRectangles.count(); ++i) {
+        skrect_to_vkrect(skrects[i], renderTarget, rtOrigin, &vkrects[i]);
+    }
+    cmdBuffer->setDiscardRectangles(gpu, 0, windowRectangles.count(), vkrects);
+}
+
 void GrVkPipeline::SetDynamicBlendConstantState(GrVkGpu* gpu,
                                                 GrVkCommandBuffer* cmdBuffer,
                                                 GrPixelConfig pixelConfig,
diff --git a/src/gpu/vk/GrVkPipeline.h b/src/gpu/vk/GrVkPipeline.h
index 88c3d5f..ad77037 100644
--- a/src/gpu/vk/GrVkPipeline.h
+++ b/src/gpu/vk/GrVkPipeline.h
@@ -22,6 +22,7 @@
 class GrVkCommandBuffer;
 class GrVkGpu;
 class GrVkRenderPass;
+class GrWindowRectangles;
 struct SkIRect;
 
 class GrVkPipeline : public GrVkResource {
@@ -39,9 +40,12 @@
 
     VkPipeline pipeline() const { return fPipeline; }
 
+    static void SetDynamicViewportState(GrVkGpu*, GrVkCommandBuffer*, const GrRenderTarget*);
     static void SetDynamicScissorRectState(GrVkGpu*, GrVkCommandBuffer*, const GrRenderTarget*,
                                            GrSurfaceOrigin, SkIRect);
-    static void SetDynamicViewportState(GrVkGpu*, GrVkCommandBuffer*, const GrRenderTarget*);
+    static void SetDynamicDiscardRectanglesState(GrVkGpu*, GrVkCommandBuffer*,
+                                                 const GrRenderTarget*, GrSurfaceOrigin,
+                                                 const GrWindowRectangles&);
     static void SetDynamicBlendConstantState(GrVkGpu*, GrVkCommandBuffer*, GrPixelConfig,
                                              const GrXferProcessor&);
 
diff --git a/src/gpu/vk/GrVkPipelineState.cpp b/src/gpu/vk/GrVkPipelineState.cpp
index 1719775..5256c6d 100644
--- a/src/gpu/vk/GrVkPipelineState.cpp
+++ b/src/gpu/vk/GrVkPipelineState.cpp
@@ -571,9 +571,9 @@
                                     const GrPipeline& pipeline,
                                     const GrStencilSettings& stencil,
                                     GrPrimitiveType primitiveType,
-                                    const GrShaderCaps& caps) {
+                                    const GrCaps& caps) {
     if (!INHERITED::Build(desc, primProc, primitiveType == GrPrimitiveType::kPoints, pipeline,
-                          caps)) {
+                          *caps.shaderCaps())) {
         return false;
     }
 
@@ -587,5 +587,12 @@
 
     b.add32((uint32_t)primitiveType);
 
+    if (GrCaps::WindowRectsSupport::kNone != caps.windowRectsSupport()) {
+        const GrWindowRectsState& windowState = pipeline.getWindowRectsState();
+        uint32_t mode = (0u - (uint32_t)windowState.mode());
+        SkASSERT(0u == mode || ~0u == mode);
+        b.add32((uint32_t)windowState.numWindows() ^ mode);
+    }
+
     return true;
 }
diff --git a/src/gpu/vk/GrVkPipelineState.h b/src/gpu/vk/GrVkPipelineState.h
index 2794b99..0eb822a 100644
--- a/src/gpu/vk/GrVkPipelineState.h
+++ b/src/gpu/vk/GrVkPipelineState.h
@@ -77,7 +77,7 @@
                           const GrPipeline&,
                           const GrStencilSettings&,
                           GrPrimitiveType primitiveType,
-                          const GrShaderCaps&);
+                          const GrCaps&);
     private:
         typedef GrProgramDesc INHERITED;
     };
diff --git a/src/gpu/vk/GrVkPipelineStateCache.cpp b/src/gpu/vk/GrVkPipelineStateCache.cpp
index caffe05..37dc4a6 100644
--- a/src/gpu/vk/GrVkPipelineStateCache.cpp
+++ b/src/gpu/vk/GrVkPipelineStateCache.cpp
@@ -93,7 +93,7 @@
     // Get GrVkProgramDesc
     GrVkPipelineState::Desc desc;
     if (!GrVkPipelineState::Desc::Build(&desc, primProc, pipeline, stencil,
-                                        primitiveType, *fGpu->caps()->shaderCaps())) {
+                                        primitiveType, *fGpu->caps())) {
         GrCapsDebugf(fGpu->caps(), "Failed to build vk program descriptor!\n");
         return nullptr;
     }
diff --git a/src/gpu/vk/GrVkRenderTarget.cpp b/src/gpu/vk/GrVkRenderTarget.cpp
index 27cb119..d250ec6 100644
--- a/src/gpu/vk/GrVkRenderTarget.cpp
+++ b/src/gpu/vk/GrVkRenderTarget.cpp
@@ -8,6 +8,7 @@
 #include "GrVkRenderTarget.h"
 
 #include "GrRenderTargetPriv.h"
+#include "GrVkCaps.h"
 #include "GrVkCommandBuffer.h"
 #include "GrVkFramebuffer.h"
 #include "GrVkGpu.h"
@@ -32,7 +33,7 @@
     : GrSurface(gpu, desc)
     , GrVkImage(info, ownership)
     // for the moment we only support 1:1 color to stencil
-    , GrRenderTarget(gpu, desc)
+    , GrRenderTarget(gpu, desc, ComputeFlags(gpu->vkCaps()))
     , fColorAttachmentView(colorAttachmentView)
     , fMSAAImage(new GrVkImage(msaaInfo, GrBackendObjectOwnership::kOwned))
     , fResolveAttachmentView(resolveAttachmentView)
@@ -55,7 +56,7 @@
     : GrSurface(gpu, desc)
     , GrVkImage(info, ownership)
     // for the moment we only support 1:1 color to stencil
-    , GrRenderTarget(gpu, desc)
+    , GrRenderTarget(gpu, desc, ComputeFlags(gpu->vkCaps()))
     , fColorAttachmentView(colorAttachmentView)
     , fMSAAImage(new GrVkImage(msaaInfo, GrBackendObjectOwnership::kOwned))
     , fResolveAttachmentView(resolveAttachmentView)
@@ -75,7 +76,7 @@
                                    GrBackendObjectOwnership ownership)
     : GrSurface(gpu, desc)
     , GrVkImage(info, ownership)
-    , GrRenderTarget(gpu, desc)
+    , GrRenderTarget(gpu, desc, ComputeFlags(gpu->vkCaps()))
     , fColorAttachmentView(colorAttachmentView)
     , fMSAAImage(nullptr)
     , fResolveAttachmentView(nullptr)
@@ -95,7 +96,7 @@
                                    GrBackendObjectOwnership ownership)
     : GrSurface(gpu, desc)
     , GrVkImage(info, ownership)
-    , GrRenderTarget(gpu, desc)
+    , GrRenderTarget(gpu, desc, ComputeFlags(gpu->vkCaps()))
     , fColorAttachmentView(colorAttachmentView)
     , fMSAAImage(nullptr)
     , fResolveAttachmentView(nullptr)
@@ -105,6 +106,14 @@
     this->createFramebuffer(gpu);
 }
 
+inline GrRenderTargetFlags GrVkRenderTarget::ComputeFlags(const GrVkCaps& vkCaps) {
+    GrRenderTargetFlags flags = GrRenderTargetFlags::kNone;
+    if (GrCaps::WindowRectsSupport::kNone != vkCaps.windowRectsSupport()) {
+        flags |= GrRenderTargetFlags::kWindowRectsSupport;
+    }
+    return flags;
+}
+
 GrVkRenderTarget*
 GrVkRenderTarget::Create(GrVkGpu* gpu,
                          SkBudgeted budgeted,
diff --git a/src/gpu/vk/GrVkRenderTarget.h b/src/gpu/vk/GrVkRenderTarget.h
index 18a0bd3..2ea065b 100644
--- a/src/gpu/vk/GrVkRenderTarget.h
+++ b/src/gpu/vk/GrVkRenderTarget.h
@@ -15,6 +15,7 @@
 #include "GrVkRenderPass.h"
 #include "GrVkResourceProvider.h"
 
+class GrVkCaps;
 class GrVkCommandBuffer;
 class GrVkFramebuffer;
 class GrVkGpu;
@@ -91,6 +92,8 @@
                      const GrVkImageView* colorAttachmentView,
                      GrBackendObjectOwnership);
 
+    static GrRenderTargetFlags ComputeFlags(const GrVkCaps&);
+
     GrVkGpu* getVkGpu() const;
 
     void onAbandon() override;