Commit 4fd18b1b by Austin Kinross Committed by Geoff Lang

Emulate large and negative viewports on D3D11 Feature Level 9_3

Like D3D9, D3D11 Feature Level 9_3 doesn't support large or negative viewports. We have to emulate these in the vertex shader. BUG=angle:858 Change-Id: I2bd53e3921dc3590cc7193164d73596deafca9ea Reviewed-on: https://chromium-review.googlesource.com/236040Tested-by: 's avatarAustin Kinross <aukinros@microsoft.com> Reviewed-by: 's avatarGeoff Lang <geofflang@chromium.org>
parent 324bcc46
...@@ -511,14 +511,21 @@ void OutputHLSL::header() ...@@ -511,14 +511,21 @@ void OutputHLSL::header()
if (mOutputType == SH_HLSL11_OUTPUT) if (mOutputType == SH_HLSL11_OUTPUT)
{ {
out << "cbuffer DriverConstants : register(b1)\n"
"{\n";
if (mUsesDepthRange) if (mUsesDepthRange)
{ {
out << "cbuffer DriverConstants : register(b1)\n" out << " float3 dx_DepthRange : packoffset(c0);\n";
"{\n"
" float3 dx_DepthRange : packoffset(c0);\n"
"};\n"
"\n";
} }
// dx_ViewAdjust will only be used in Feature Level 9 shaders.
// However, we declare it for all shaders (including Feature Level 10+).
// The bytecode is the same whether we declare it or not, since D3DCompiler removes it if it's unused.
out << " float4 dx_ViewAdjust : packoffset(c1);\n";
out << "};\n"
"\n";
} }
else else
{ {
......
...@@ -728,7 +728,8 @@ bool DynamicHLSL::generateShaderLinkHLSL(const gl::Data &data, InfoLog &infoLog, ...@@ -728,7 +728,8 @@ bool DynamicHLSL::generateShaderLinkHLSL(const gl::Data &data, InfoLog &infoLog,
"{\n" "{\n"
" initAttributes(input);\n"; " initAttributes(input);\n";
if (shaderModel >= 4) // On D3D9 or D3D11 Feature Level 9, we need to emulate large viewports using dx_ViewAdjust.
if (shaderModel >= 4 && mRenderer->getShaderModelSuffix() == "")
{ {
vertexHLSL += "\n" vertexHLSL += "\n"
" gl_main();\n" " gl_main();\n"
......
...@@ -875,28 +875,55 @@ void Renderer11::setViewport(const gl::Rectangle &viewport, float zNear, float z ...@@ -875,28 +875,55 @@ void Renderer11::setViewport(const gl::Rectangle &viewport, float zNear, float z
actualZFar = 1.0f; actualZFar = 1.0f;
} }
const gl::Caps& caps = getRendererCaps();
// 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, -static_cast<int>(caps.maxViewportWidth), static_cast<int>(caps.maxViewportWidth));
dxViewport.TopLeftY = gl::clamp(actualViewport.y, -static_cast<int>(caps.maxViewportHeight), static_cast<int>(caps.maxViewportHeight));
dxViewport.Width = gl::clamp(actualViewport.width, 0, static_cast<int>(caps.maxViewportWidth - dxViewport.TopLeftX));
dxViewport.Height = gl::clamp(actualViewport.height, 0, static_cast<int>(caps.maxViewportHeight - dxViewport.TopLeftY));
dxViewport.MinDepth = actualZNear;
dxViewport.MaxDepth = actualZFar;
bool viewportChanged = mForceSetViewport || memcmp(&actualViewport, &mCurViewport, sizeof(gl::Rectangle)) != 0 || bool viewportChanged = mForceSetViewport || memcmp(&actualViewport, &mCurViewport, sizeof(gl::Rectangle)) != 0 ||
actualZNear != mCurNear || actualZFar != mCurFar; actualZNear != mCurNear || actualZFar != mCurFar;
if (viewportChanged) if (viewportChanged)
{ {
const gl::Caps& caps = getRendererCaps();
int dxMaxViewportBoundsX = static_cast<int>(caps.maxViewportWidth);
int dxMaxViewportBoundsY = static_cast<int>(caps.maxViewportHeight);
int dxMinViewportBoundsX = -dxMaxViewportBoundsX;
int dxMinViewportBoundsY = -dxMaxViewportBoundsY;
if (mFeatureLevel <= D3D_FEATURE_LEVEL_9_3)
{
// Feature Level 9 viewports shouldn't exceed the dimensions of the rendertarget.
dxMaxViewportBoundsX = mRenderTargetDesc.width;
dxMaxViewportBoundsY = mRenderTargetDesc.height;
dxMinViewportBoundsX = 0;
dxMinViewportBoundsY = 0;
}
int dxViewportTopLeftX = gl::clamp(actualViewport.x, dxMinViewportBoundsX, dxMaxViewportBoundsX);
int dxViewportTopLeftY = gl::clamp(actualViewport.y, dxMinViewportBoundsY, dxMaxViewportBoundsY);
int dxViewportWidth = gl::clamp(actualViewport.width, 0, dxMaxViewportBoundsX - dxViewportTopLeftX);
int dxViewportHeight = gl::clamp(actualViewport.height, 0, dxMaxViewportBoundsY - dxViewportTopLeftY);
D3D11_VIEWPORT dxViewport;
dxViewport.TopLeftX = static_cast<float>(dxViewportTopLeftX);
dxViewport.TopLeftY = static_cast<float>(dxViewportTopLeftY);
dxViewport.Width = static_cast<float>(dxViewportWidth);
dxViewport.Height = static_cast<float>(dxViewportHeight);
dxViewport.MinDepth = actualZNear;
dxViewport.MaxDepth = actualZFar;
mDeviceContext->RSSetViewports(1, &dxViewport); mDeviceContext->RSSetViewports(1, &dxViewport);
mCurViewport = actualViewport; mCurViewport = actualViewport;
mCurNear = actualZNear; mCurNear = actualZNear;
mCurFar = actualZFar; mCurFar = actualZFar;
// On Feature Level 9_*, we must emulate large and/or negative viewports in the shaders using viewAdjust (like the D3D9 renderer).
if (mFeatureLevel <= D3D_FEATURE_LEVEL_9_3)
{
mVertexConstants.viewAdjust[0] = static_cast<float>((actualViewport.width - dxViewportWidth) + 2 * (actualViewport.x - dxViewportTopLeftX)) / dxViewport.Width;
mVertexConstants.viewAdjust[1] = static_cast<float>((actualViewport.height - dxViewportHeight) + 2 * (actualViewport.y - dxViewportTopLeftY)) / dxViewport.Height;
mVertexConstants.viewAdjust[2] = static_cast<float>(actualViewport.width) / dxViewport.Width;
mVertexConstants.viewAdjust[3] = static_cast<float>(actualViewport.height) / dxViewport.Height;
}
mPixelConstants.viewCoords[0] = actualViewport.width * 0.5f; mPixelConstants.viewCoords[0] = actualViewport.width * 0.5f;
mPixelConstants.viewCoords[1] = actualViewport.height * 0.5f; mPixelConstants.viewCoords[1] = actualViewport.height * 0.5f;
mPixelConstants.viewCoords[2] = actualViewport.x + (actualViewport.width * 0.5f); mPixelConstants.viewCoords[2] = actualViewport.x + (actualViewport.width * 0.5f);
......
...@@ -53,7 +53,7 @@ void ANGLETest::swapBuffers() ...@@ -53,7 +53,7 @@ void ANGLETest::swapBuffers()
mEGLWindow->swap(); mEGLWindow->swap();
} }
void ANGLETest::drawQuad(GLuint program, const std::string& positionAttribName, GLfloat quadDepth) void ANGLETest::drawQuad(GLuint program, const std::string& positionAttribName, GLfloat quadDepth, GLfloat quadScale)
{ {
GLint positionLocation = glGetAttribLocation(program, positionAttribName.c_str()); GLint positionLocation = glGetAttribLocation(program, positionAttribName.c_str());
...@@ -61,13 +61,13 @@ void ANGLETest::drawQuad(GLuint program, const std::string& positionAttribName, ...@@ -61,13 +61,13 @@ void ANGLETest::drawQuad(GLuint program, const std::string& positionAttribName,
const GLfloat vertices[] = const GLfloat vertices[] =
{ {
-1.0f, 1.0f, quadDepth, -1.0f * quadScale, 1.0f * quadScale, quadDepth,
-1.0f, -1.0f, quadDepth, -1.0f * quadScale, -1.0f * quadScale, quadDepth,
1.0f, -1.0f, quadDepth, 1.0f * quadScale, -1.0f * quadScale, quadDepth,
-1.0f, 1.0f, quadDepth, -1.0f * quadScale, 1.0f * quadScale, quadDepth,
1.0f, -1.0f, quadDepth, 1.0f * quadScale, -1.0f * quadScale, quadDepth,
1.0f, 1.0f, quadDepth, 1.0f * quadScale, 1.0f * quadScale, quadDepth,
}; };
glVertexAttribPointer(positionLocation, 3, GL_FLOAT, GL_FALSE, 0, vertices); glVertexAttribPointer(positionLocation, 3, GL_FLOAT, GL_FALSE, 0, vertices);
......
...@@ -68,7 +68,7 @@ class ANGLETest : public testing::Test ...@@ -68,7 +68,7 @@ class ANGLETest : public testing::Test
virtual void swapBuffers(); virtual void swapBuffers();
static void drawQuad(GLuint program, const std::string& positionAttribName, GLfloat quadDepth); static void drawQuad(GLuint program, const std::string& positionAttribName, GLfloat quadDepth, GLfloat quadScale = 1.0f);
static GLuint compileShader(GLenum type, const std::string &source); static GLuint compileShader(GLenum type, const std::string &source);
static bool extensionEnabled(const std::string &extName); static bool extensionEnabled(const std::string &extName);
......
#include "ANGLETest.h"
// Use this to select which configurations (e.g. which renderer, which GLES major version) these tests should be run against.
// D3D11 Feature Level 9 and D3D9 emulate large and negative viewports in the vertex shader. We should test both of these as well as D3D11 Feature Level 10_0+.
ANGLE_TYPED_TEST_CASE(ViewportTest, ES2_D3D9, ES2_D3D11, ES2_D3D11_FL9_3);
template<typename T>
class ViewportTest : public ANGLETest
{
protected:
ViewportTest() : ANGLETest(T::GetGlesMajorVersion(), T::GetPlatform())
{
setWindowWidth(512);
setWindowHeight(512);
setConfigRedBits(8);
setConfigGreenBits(8);
setConfigBlueBits(8);
setConfigAlphaBits(8);
setConfigDepthBits(24);
mProgram = 0;
}
void runNonScissoredTest()
{
glClearColor(0, 0, 0, 1);
glClear(GL_COLOR_BUFFER_BIT);
runTest();
}
void runScissoredTest()
{
glClearColor(0, 0, 0, 1);
glClear(GL_COLOR_BUFFER_BIT);
glEnable(GL_SCISSOR_TEST);
glScissor(0, getWindowHeight() / 2, getWindowWidth(), getWindowHeight() / 2);
runTest();
}
void runTest()
{
// Firstly ensure that no errors have been hit.
EXPECT_GL_NO_ERROR();
GLint viewportSize[4];
glGetIntegerv(GL_VIEWPORT, viewportSize);
// Clear to green. Might be a scissored clear, if scissorSize != window size
glClearColor(0, 1, 0, 1);
glClear(GL_COLOR_BUFFER_BIT);
// Draw a red quad centered in the middle of the viewport, with dimensions 25% of the size of the viewport.
drawQuad(mProgram, "position", 0.5f, 0.25f);
GLint centerViewportX = viewportSize[0] + (viewportSize[2] / 2);
GLint centerViewportY = viewportSize[1] + (viewportSize[3] / 2);
GLint redQuadLeftSideX = viewportSize[0] + viewportSize[2] * 3 / 8;
GLint redQuadRightSideX = viewportSize[0] + viewportSize[2] * 5 / 8;
GLint redQuadTopSideY = viewportSize[1] + viewportSize[3] * 3 / 8;
GLint redQuadBottomSideY = viewportSize[1] + viewportSize[3] * 5 / 8;
// The midpoint of the viewport should be red.
checkPixel(centerViewportX, centerViewportY, true);
// Pixels just inside the red quad should be red.
checkPixel(redQuadLeftSideX, redQuadTopSideY, true);
checkPixel(redQuadLeftSideX, redQuadBottomSideY - 1, true);
checkPixel(redQuadRightSideX - 1, redQuadTopSideY, true);
checkPixel(redQuadRightSideX - 1, redQuadBottomSideY - 1, true);
// Pixels just outside the red quad shouldn't be red.
checkPixel(redQuadLeftSideX - 1, redQuadTopSideY - 1, false);
checkPixel(redQuadLeftSideX - 1, redQuadBottomSideY, false);
checkPixel(redQuadRightSideX, redQuadTopSideY - 1, false);
checkPixel(redQuadRightSideX, redQuadBottomSideY, false);
// Pixels just within the viewport shouldn't be red.
checkPixel(viewportSize[0], viewportSize[1], false);
checkPixel(viewportSize[0], viewportSize[1] + viewportSize[3] - 1, false);
checkPixel(viewportSize[0] + viewportSize[2] - 1, viewportSize[1], false);
checkPixel(viewportSize[0] + viewportSize[2] - 1, viewportSize[1] + viewportSize[3] - 1, false);
}
void checkPixel(GLint x, GLint y, GLboolean renderedRed)
{
// By default, expect the pixel to be black.
GLint expectedRedChannel = 0;
GLint expectedGreenChannel = 0;
GLint scissorSize[4];
glGetIntegerv(GL_SCISSOR_BOX, scissorSize);
EXPECT_GL_NO_ERROR();
if (scissorSize[0] <= x && x < scissorSize[0] + scissorSize[2]
&& scissorSize[1] <= y && y < scissorSize[1] + scissorSize[3])
{
// If the pixel lies within the scissor rect, then it should have been cleared to green.
// If we rendered a red square on top of it, then the pixel should be red (the green channel will have been reset to 0).
expectedRedChannel = renderedRed ? 255 : 0;
expectedGreenChannel = renderedRed ? 0 : 255;
}
// If the pixel is within the bounds of the window, then we check it. Otherwise we skip it.
if (0 <= x && x < getWindowWidth() && 0 <= y && y < getWindowHeight())
{
EXPECT_PIXEL_EQ(x, y, expectedRedChannel, expectedGreenChannel, 0, 255);
}
}
virtual void SetUp()
{
ANGLETest::SetUp();
const std::string testVertexShaderSource = SHADER_SOURCE
(
attribute highp vec4 position;
void main(void)
{
gl_Position = position;
}
);
const std::string testFragmentShaderSource = SHADER_SOURCE
(
void main(void)
{
gl_FragColor = vec4(1.0, 0.0, 0.0, 1.0);
}
);
mProgram = CompileProgram(testVertexShaderSource, testFragmentShaderSource);
if (mProgram == 0)
{
FAIL() << "shader compilation failed.";
}
glUseProgram(mProgram);
glClearColor(0, 0, 0, 1);
glClearDepthf(0.0);
glClear(GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT);
// Call glViewport with default parameters.
glViewport(0, 0, getWindowWidth(), getWindowHeight());
glDisable(GL_DEPTH_TEST);
}
virtual void TearDown()
{
glDeleteProgram(mProgram);
ANGLETest::TearDown();
}
GLuint mProgram;
};
TYPED_TEST(ViewportTest, QuarterWindow)
{
glViewport(0, 0, getWindowWidth() / 4, getWindowHeight() / 4);
runNonScissoredTest();
runScissoredTest();
}
TYPED_TEST(ViewportTest, QuarterWindowCentered)
{
glViewport(getWindowWidth() * 3 / 8, getWindowHeight() * 3 / 8, getWindowWidth() / 4, getWindowHeight() / 4);
runNonScissoredTest();
runScissoredTest();
}
TYPED_TEST(ViewportTest, FullWindow)
{
glViewport(0, 0, getWindowWidth(), getWindowHeight());
runNonScissoredTest();
runScissoredTest();
}
TYPED_TEST(ViewportTest, FullWindowOffCenter)
{
glViewport(-getWindowWidth() / 2, getWindowHeight() / 2, getWindowWidth(), getWindowHeight());
runNonScissoredTest();
runScissoredTest();
}
TYPED_TEST(ViewportTest, DoubleWindow)
{
glViewport(0, 0, getWindowWidth() * 2, getWindowHeight() * 2);
runNonScissoredTest();
runScissoredTest();
}
TYPED_TEST(ViewportTest, DoubleWindowCentered)
{
glViewport(-getWindowWidth() / 2, -getWindowHeight() / 2, getWindowWidth() * 2, getWindowHeight() * 2);
runNonScissoredTest();
runScissoredTest();
}
TYPED_TEST(ViewportTest, DoubleWindowOffCenter)
{
glViewport(-getWindowWidth() * 3 / 4, getWindowHeight() * 3 / 4, getWindowWidth(), getWindowHeight());
runNonScissoredTest();
runScissoredTest();
}
TYPED_TEST(ViewportTest, TripleWindow)
{
glViewport(0, 0, getWindowWidth() * 3, getWindowHeight() * 3);
runNonScissoredTest();
runScissoredTest();
}
TYPED_TEST(ViewportTest, TripleWindowCentered)
{
glViewport(-getWindowWidth(), -getWindowHeight(), getWindowWidth() * 3, getWindowHeight() * 3);
runNonScissoredTest();
runScissoredTest();
}
TYPED_TEST(ViewportTest, TripleWindowOffCenter)
{
glViewport(-getWindowWidth() * 3 / 2, -getWindowHeight() * 3 / 2, getWindowWidth() * 3, getWindowHeight() * 3);
runNonScissoredTest();
runScissoredTest();
}
\ No newline at end of file
Markdown is supported
0% or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment