Implement larger bounds for viewports and scissor rectangles in D3D11.

This fixes a bug where we would not allow the application to draw outside the viewport, which is valid.

TRAC #22497

Signed-off-by: Nicolas Capens
Signed-off-by: Shannon Woods
Author: Jamie Madill

git-svn-id: https://angleproject.googlecode.com/svn/branches/dx11proto@1875 736b8ea6-26fd-11df-bfd4-992fa37f6226
diff --git a/src/libGLESv2/renderer/Renderer.h b/src/libGLESv2/renderer/Renderer.h
index 20b5c80..b228704 100644
--- a/src/libGLESv2/renderer/Renderer.h
+++ b/src/libGLESv2/renderer/Renderer.h
@@ -169,6 +169,7 @@
 
     virtual int getMajorShaderModel() const = 0;
     virtual float getMaxPointSize() const = 0;
+    virtual int getMaxViewportDimension() const = 0;
     virtual int getMaxTextureWidth() const = 0;
     virtual int getMaxTextureHeight() const = 0;
     virtual bool get32BitIndexSupport() const = 0;
diff --git a/src/libGLESv2/renderer/Renderer11.cpp b/src/libGLESv2/renderer/Renderer11.cpp
index 099d931..56fa133 100644
--- a/src/libGLESv2/renderer/Renderer11.cpp
+++ b/src/libGLESv2/renderer/Renderer11.cpp
@@ -562,10 +562,10 @@
         if (enabled)
         {
             D3D11_RECT rect;
-            rect.left = gl::clamp(scissor.x, 0, static_cast<int>(mRenderTargetDesc.width));
-            rect.top = gl::clamp(scissor.y, 0, static_cast<int>(mRenderTargetDesc.height));
-            rect.right = gl::clamp(scissor.x + scissor.width, 0, static_cast<int>(mRenderTargetDesc.width));
-            rect.bottom = gl::clamp(scissor.y + scissor.height, 0, static_cast<int>(mRenderTargetDesc.height));
+            rect.left = std::max(0, scissor.x);
+            rect.top = std::max(0, scissor.y);
+            rect.right = scissor.x + std::max(0, scissor.width);
+            rect.bottom = scissor.y + std::max(0, scissor.height);
 
             mDeviceContext->RSSetScissorRects(1, &rect);
         }
@@ -598,11 +598,17 @@
         actualZFar = 1.0f;
     }
 
+    // Get D3D viewport bounds, which depends on the feature level
+    const Range& viewportBounds = getViewportBounds();
+
+    // Clamp width and height first to the gl maximum, then clamp further if we extend past the D3D maximum bounds
     D3D11_VIEWPORT dxViewport;
-    dxViewport.TopLeftX = gl::clamp(actualViewport.x, 0, static_cast<int>(mRenderTargetDesc.width));
-    dxViewport.TopLeftY = gl::clamp(actualViewport.y, 0, static_cast<int>(mRenderTargetDesc.height));
-    dxViewport.Width = gl::clamp(actualViewport.width, 0, static_cast<int>(mRenderTargetDesc.width) - static_cast<int>(dxViewport.TopLeftX));
-    dxViewport.Height = gl::clamp(actualViewport.height, 0, static_cast<int>(mRenderTargetDesc.height) - static_cast<int>(dxViewport.TopLeftY));
+    dxViewport.TopLeftX = gl::clamp(actualViewport.x, viewportBounds.start, viewportBounds.end);
+    dxViewport.TopLeftY = gl::clamp(actualViewport.y, viewportBounds.start, viewportBounds.end);
+    dxViewport.Width = gl::clamp(actualViewport.width, 0, getMaxViewportDimension());
+    dxViewport.Height = gl::clamp(actualViewport.height, 0, getMaxViewportDimension());
+    dxViewport.Width = std::min((int)dxViewport.Width, viewportBounds.end - static_cast<int>(dxViewport.TopLeftX));
+    dxViewport.Height = std::min((int)dxViewport.Height, viewportBounds.end - static_cast<int>(dxViewport.TopLeftY));
     dxViewport.MinDepth = actualZNear;
     dxViewport.MaxDepth = actualZFar;
 
@@ -793,8 +799,8 @@
         mRenderTargetDesc.width = renderTargetWidth;
         mRenderTargetDesc.height = renderTargetHeight;
         mRenderTargetDesc.format = renderTargetFormat;
-        mForceSetViewport = true; // TODO: It may not be required to clamp the viewport in D3D11
-        mForceSetScissor = true; // TODO: It may not be required to clamp the scissor in D3D11
+        mForceSetViewport = true;
+        mForceSetScissor = true;
 
         if (!mDepthStencilInitialized || depthSize != mCurDepthSize)
         {
@@ -2023,6 +2029,20 @@
     return true;
 }
 
+Range Renderer11::getViewportBounds() const
+{
+    switch (mFeatureLevel)
+    {
+      case D3D_FEATURE_LEVEL_11_0:
+        return Range(D3D11_VIEWPORT_BOUNDS_MIN, D3D11_VIEWPORT_BOUNDS_MAX);
+      case D3D_FEATURE_LEVEL_10_1:
+      case D3D_FEATURE_LEVEL_10_0:
+        return Range(D3D10_VIEWPORT_BOUNDS_MIN, D3D10_VIEWPORT_BOUNDS_MAX);
+      default: UNREACHABLE();
+        return Range(0, 0);
+    }
+}
+
 unsigned int Renderer11::getMaxVertexTextureImageUnits() const
 {
     META_ASSERT(MAX_TEXTURE_IMAGE_UNITS_VTF_SM4 <= gl::IMPLEMENTATION_MAX_VERTEX_TEXTURE_IMAGE_UNITS);
@@ -2160,6 +2180,21 @@
     return 1024.0f;
 }
 
+int Renderer11::getMaxViewportDimension() const
+{
+    // Clamp viewport width/height to half of the maximum right/bottom edge
+    switch (mFeatureLevel)
+    {
+      case D3D_FEATURE_LEVEL_11_0: 
+        return D3D11_VIEWPORT_BOUNDS_MAX - D3D11_REQ_TEXTURE2D_U_OR_V_DIMENSION;   // 16384
+      case D3D_FEATURE_LEVEL_10_1:
+      case D3D_FEATURE_LEVEL_10_0: 
+        return D3D10_VIEWPORT_BOUNDS_MAX - D3D10_REQ_TEXTURE2D_U_OR_V_DIMENSION;   // 8192
+      default: UNREACHABLE();      
+        return 0;
+    }
+}
+
 int Renderer11::getMaxTextureWidth() const
 {
     switch (mFeatureLevel)
diff --git a/src/libGLESv2/renderer/Renderer11.h b/src/libGLESv2/renderer/Renderer11.h
index 4722c8e..ed68237 100644
--- a/src/libGLESv2/renderer/Renderer11.h
+++ b/src/libGLESv2/renderer/Renderer11.h
@@ -21,6 +21,7 @@
 
 #include "common/angleutils.h"
 #include "libGLESv2/angletypes.h"
+#include "libGLESv2/mathutil.h"
 
 #include "libGLESv2/renderer/Renderer.h"
 #include "libGLESv2/renderer/RenderStateCache.h"
@@ -120,6 +121,7 @@
 
     virtual int getMajorShaderModel() const;
     virtual float getMaxPointSize() const;
+    virtual int getMaxViewportDimension() const;
     virtual int getMaxTextureWidth() const;
     virtual int getMaxTextureHeight() const;
     virtual bool get32BitIndexSupport() const;
@@ -193,6 +195,7 @@
                          GLint packAlignment, void *pixels);
 
     void maskedClear(const gl::ClearParameters &clearParams);
+    rx::Range getViewportBounds() const;
 
     bool blitRect(gl::Framebuffer *readTarget, const gl::Rectangle &readRect, gl::Framebuffer *drawTarget,
                   const gl::Rectangle &drawRect, BlitTarget target);
diff --git a/src/libGLESv2/renderer/Renderer9.cpp b/src/libGLESv2/renderer/Renderer9.cpp
index 7a4d31c..3846abe 100644
--- a/src/libGLESv2/renderer/Renderer9.cpp
+++ b/src/libGLESv2/renderer/Renderer9.cpp
@@ -2307,6 +2307,13 @@
     return getMajorShaderModel() == 3 ? mDeviceCaps.MaxPointSize : 1.0f;
 }
 
+int Renderer9::getMaxViewportDimension() const
+{
+    int maxTextureDimension = std::min(std::min(getMaxTextureWidth(), getMaxTextureHeight()),
+                                       (int)gl::IMPLEMENTATION_MAX_TEXTURE_SIZE);
+    return maxTextureDimension;
+}
+
 int Renderer9::getMaxTextureWidth() const
 {
     return (int)mDeviceCaps.MaxTextureWidth;
diff --git a/src/libGLESv2/renderer/Renderer9.h b/src/libGLESv2/renderer/Renderer9.h
index 3841208..765625c 100644
--- a/src/libGLESv2/renderer/Renderer9.h
+++ b/src/libGLESv2/renderer/Renderer9.h
@@ -149,6 +149,7 @@
 
     virtual int getMajorShaderModel() const;
     virtual float getMaxPointSize() const;
+    virtual int getMaxViewportDimension() const;
     virtual int getMaxTextureWidth() const;
     virtual int getMaxTextureHeight() const;
     virtual bool get32BitIndexSupport() const;