Commit 8f276e25 by Martin Radev Committed by Commit Bot

Handle multiview Draw* calls

Because the ANGLE_multiview extension uses instancing to multiply geometry for each view, Draw* calls with an active multiview program have to be handled in the follwing way: 1) Convert non-instanced Draw calls to their instanced versions. 2) Multiply the number of instances in an instanced Draw call by the number of views. BUG=angleproject:2062 TEST=angle_end2end_tests Change-Id: I19119e8178f70336e5dbb1e5eed0658b5b9f43d7 Reviewed-on: https://chromium-review.googlesource.com/593657Reviewed-by: 's avatarGeoff Lang <geofflang@chromium.org> Reviewed-by: 's avatarCorentin Wallez <cwallez@chromium.org> Commit-Queue: Martin Radev <mradev@nvidia.com>
parent df7d7c9e
...@@ -536,6 +536,7 @@ class Program final : angle::NonCopyable, public LabeledObject ...@@ -536,6 +536,7 @@ class Program final : angle::NonCopyable, public LabeledObject
const Bindings &getFragmentInputBindings() const { return mFragmentInputBindings; } const Bindings &getFragmentInputBindings() const { return mFragmentInputBindings; }
int getNumViews() const { return mState.mNumViews; } int getNumViews() const { return mState.mNumViews; }
bool usesMultiview() const { return mState.mNumViews != -1; }
private: private:
~Program(); ~Program();
......
...@@ -12,6 +12,7 @@ ...@@ -12,6 +12,7 @@
#include "common/debug.h" #include "common/debug.h"
#include "libANGLE/AttributeMap.h" #include "libANGLE/AttributeMap.h"
#include "libANGLE/Context.h"
#include "libANGLE/ContextState.h" #include "libANGLE/ContextState.h"
#include "libANGLE/Path.h" #include "libANGLE/Path.h"
#include "libANGLE/Surface.h" #include "libANGLE/Surface.h"
...@@ -252,13 +253,22 @@ gl::Error RendererGL::drawArrays(const gl::Context *context, ...@@ -252,13 +253,22 @@ gl::Error RendererGL::drawArrays(const gl::Context *context,
GLint first, GLint first,
GLsizei count) GLsizei count)
{ {
ANGLE_TRY(mStateManager->setDrawArraysState(context, first, count, 0)); const gl::Program *program = context->getGLState().getProgram();
const bool usesMultiview = program->usesMultiview();
const GLsizei instanceCount = usesMultiview ? program->getNumViews() : 0;
ANGLE_TRY(mStateManager->setDrawArraysState(context, first, count, instanceCount));
if (!mSkipDrawCalls) if (!mSkipDrawCalls)
{ {
mFunctions->drawArrays(mode, first, count); if (!usesMultiview)
{
mFunctions->drawArrays(mode, first, count);
}
else
{
mFunctions->drawArraysInstanced(mode, first, count, instanceCount);
}
} }
return gl::NoError(); return gl::NoError();
} }
...@@ -268,13 +278,18 @@ gl::Error RendererGL::drawArraysInstanced(const gl::Context *context, ...@@ -268,13 +278,18 @@ gl::Error RendererGL::drawArraysInstanced(const gl::Context *context,
GLsizei count, GLsizei count,
GLsizei instanceCount) GLsizei instanceCount)
{ {
ANGLE_TRY(mStateManager->setDrawArraysState(context, first, count, instanceCount)); GLsizei adjustedInstanceCount = instanceCount;
const gl::Program *program = context->getGLState().getProgram();
if (program->usesMultiview())
{
adjustedInstanceCount *= program->getNumViews();
}
ANGLE_TRY(mStateManager->setDrawArraysState(context, first, count, adjustedInstanceCount));
if (!mSkipDrawCalls) if (!mSkipDrawCalls)
{ {
mFunctions->drawArraysInstanced(mode, first, count, instanceCount); mFunctions->drawArraysInstanced(mode, first, count, adjustedInstanceCount);
} }
return gl::NoError(); return gl::NoError();
} }
...@@ -284,14 +299,24 @@ gl::Error RendererGL::drawElements(const gl::Context *context, ...@@ -284,14 +299,24 @@ gl::Error RendererGL::drawElements(const gl::Context *context,
GLenum type, GLenum type,
const void *indices) const void *indices)
{ {
const gl::Program *program = context->getGLState().getProgram();
const bool usesMultiview = program->usesMultiview();
const GLsizei instanceCount = usesMultiview ? program->getNumViews() : 0;
const void *drawIndexPtr = nullptr; const void *drawIndexPtr = nullptr;
ANGLE_TRY(mStateManager->setDrawElementsState(context, count, type, indices, 0, &drawIndexPtr));
ANGLE_TRY(mStateManager->setDrawElementsState(context, count, type, indices, instanceCount,
&drawIndexPtr));
if (!mSkipDrawCalls) if (!mSkipDrawCalls)
{ {
mFunctions->drawElements(mode, count, type, drawIndexPtr); if (!usesMultiview)
{
mFunctions->drawElements(mode, count, type, drawIndexPtr);
}
else
{
mFunctions->drawElementsInstanced(mode, count, type, drawIndexPtr, instanceCount);
}
} }
return gl::NoError(); return gl::NoError();
} }
...@@ -302,15 +327,21 @@ gl::Error RendererGL::drawElementsInstanced(const gl::Context *context, ...@@ -302,15 +327,21 @@ gl::Error RendererGL::drawElementsInstanced(const gl::Context *context,
const void *indices, const void *indices,
GLsizei instances) GLsizei instances)
{ {
GLsizei adjustedInstanceCount = instances;
const gl::Program *program = context->getGLState().getProgram();
if (program->usesMultiview())
{
adjustedInstanceCount *= program->getNumViews();
}
const void *drawIndexPointer = nullptr; const void *drawIndexPointer = nullptr;
ANGLE_TRY(mStateManager->setDrawElementsState(context, count, type, indices, instances,
&drawIndexPointer));
ANGLE_TRY(mStateManager->setDrawElementsState(context, count, type, indices,
adjustedInstanceCount, &drawIndexPointer));
if (!mSkipDrawCalls) if (!mSkipDrawCalls)
{ {
mFunctions->drawElementsInstanced(mode, count, type, drawIndexPointer, instances); mFunctions->drawElementsInstanced(mode, count, type, drawIndexPointer,
adjustedInstanceCount);
} }
return gl::NoError(); return gl::NoError();
} }
...@@ -322,15 +353,24 @@ gl::Error RendererGL::drawRangeElements(const gl::Context *context, ...@@ -322,15 +353,24 @@ gl::Error RendererGL::drawRangeElements(const gl::Context *context,
GLenum type, GLenum type,
const void *indices) const void *indices)
{ {
const gl::Program *program = context->getGLState().getProgram();
const bool usesMultiview = program->usesMultiview();
const GLsizei instanceCount = usesMultiview ? program->getNumViews() : 0;
const void *drawIndexPointer = nullptr; const void *drawIndexPointer = nullptr;
ANGLE_TRY(
mStateManager->setDrawElementsState(context, count, type, indices, 0, &drawIndexPointer));
ANGLE_TRY(mStateManager->setDrawElementsState(context, count, type, indices, instanceCount,
&drawIndexPointer));
if (!mSkipDrawCalls) if (!mSkipDrawCalls)
{ {
mFunctions->drawRangeElements(mode, start, end, count, type, drawIndexPointer); if (!usesMultiview)
{
mFunctions->drawRangeElements(mode, start, end, count, type, drawIndexPointer);
}
else
{
mFunctions->drawElementsInstanced(mode, count, type, drawIndexPointer, instanceCount);
}
} }
return gl::NoError(); return gl::NoError();
} }
......
...@@ -84,6 +84,140 @@ class MultiviewDrawValidationTest : public MultiviewDrawTest ...@@ -84,6 +84,140 @@ class MultiviewDrawValidationTest : public MultiviewDrawTest
GLFramebuffer mFramebuffer; GLFramebuffer mFramebuffer;
}; };
class MultiviewSideBySideRenderTest : public MultiviewDrawTest
{
protected:
void createFBO(int width, int height, int numViews)
{
// Assert that width is a multiple of numViews.
ASSERT_TRUE(width % numViews == 0);
const int widthPerView = width / numViews;
// Create color and depth textures.
glBindTexture(GL_TEXTURE_2D, mColorTexture);
glTexImage2D(GL_TEXTURE_2D, 0, GL_RGBA8, width, height, 0, GL_RGBA, GL_UNSIGNED_BYTE, NULL);
glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_NEAREST);
glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_NEAREST);
ASSERT_GL_NO_ERROR();
glBindTexture(GL_TEXTURE_2D, mDepthTexture);
glTexImage2D(GL_TEXTURE_2D, 0, GL_DEPTH_COMPONENT32F, width, height, 0, GL_DEPTH_COMPONENT,
GL_FLOAT, NULL);
glBindTexture(GL_TEXTURE_2D, 0);
ASSERT_GL_NO_ERROR();
// Create draw framebuffer to be used for side-by-side rendering.
glBindFramebuffer(GL_DRAW_FRAMEBUFFER, mDrawFramebuffer);
std::vector<GLint> viewportOffsets(numViews * 2u);
for (int i = 0u; i < numViews; ++i)
{
viewportOffsets[i * 2] = i * widthPerView;
viewportOffsets[i * 2 + 1] = 0;
}
glFramebufferTextureMultiviewSideBySideANGLE(GL_DRAW_FRAMEBUFFER, GL_COLOR_ATTACHMENT0,
mColorTexture, 0, numViews,
&viewportOffsets[0]);
glFramebufferTextureMultiviewSideBySideANGLE(GL_DRAW_FRAMEBUFFER, GL_DEPTH_ATTACHMENT,
mDepthTexture, 0, numViews,
&viewportOffsets[0]);
GLenum DrawBuffers[1] = {GL_COLOR_ATTACHMENT0};
glDrawBuffers(1, DrawBuffers);
ASSERT_GL_NO_ERROR();
ASSERT_GLENUM_EQ(GL_FRAMEBUFFER_COMPLETE, glCheckFramebufferStatus(GL_DRAW_FRAMEBUFFER));
// Create read framebuffer to be used to retrieve the pixel information for testing
// purposes.
glBindFramebuffer(GL_READ_FRAMEBUFFER, mReadFramebuffer);
glFramebufferTexture2D(GL_READ_FRAMEBUFFER, GL_COLOR_ATTACHMENT0, GL_TEXTURE_2D,
mColorTexture, 0);
ASSERT_GLENUM_EQ(GL_FRAMEBUFFER_COMPLETE, glCheckFramebufferStatus(GL_READ_FRAMEBUFFER));
// Clear the buffers.
glViewport(0, 0, width, height);
glScissor(0, 0, width, height);
glClearColor(0, 0, 0, 0);
glClear(GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT);
// Set viewport and scissor of each view.
glViewport(0, 0, widthPerView, height);
glScissor(0, 0, widthPerView, height);
}
GLTexture mColorTexture;
GLTexture mDepthTexture;
GLFramebuffer mDrawFramebuffer;
GLFramebuffer mReadFramebuffer;
};
class MultiviewSideBySideRenderDualViewTest : public MultiviewSideBySideRenderTest
{
protected:
MultiviewSideBySideRenderDualViewTest() : mProgram(0u) {}
~MultiviewSideBySideRenderDualViewTest()
{
if (mProgram != 0u)
{
glDeleteProgram(mProgram);
}
}
void SetUp() override
{
MultiviewSideBySideRenderTest::SetUp();
if (!requestMultiviewExtension())
{
return;
}
const std::string vsSource =
"#version 300 es\n"
"#extension GL_OVR_multiview : require\n"
"layout(num_views = 2) in;\n"
"in vec4 vPosition;\n"
"void main()\n"
"{\n"
" gl_Position.x = (gl_ViewID_OVR == 0u ? vPosition.x*0.5 + 0.5 : vPosition.x*0.5);\n"
" gl_Position.yzw = vPosition.yzw;\n"
"}\n";
const std::string fsSource =
"#version 300 es\n"
"#extension GL_OVR_multiview : require\n"
"precision mediump float;\n"
"out vec4 col;\n"
"void main()\n"
"{\n"
" col = vec4(1,0,0,0);\n"
"}\n";
createFBO(4, 1, 2);
createProgram(vsSource, fsSource);
}
void createProgram(const std::string &vs, const std::string &fs)
{
mProgram = CompileProgram(vs, fs);
if (mProgram == 0)
{
FAIL() << "shader compilation failed.";
}
glUseProgram(mProgram);
ASSERT_GL_NO_ERROR();
}
void checkOutput()
{
EXPECT_PIXEL_EQ(0, 0, 0, 0, 0, 0);
EXPECT_PIXEL_EQ(1, 0, 255, 0, 0, 0);
EXPECT_PIXEL_EQ(2, 0, 255, 0, 0, 0);
EXPECT_PIXEL_EQ(3, 0, 0, 0, 0, 0);
}
GLuint mProgram;
};
// The test verifies that glDraw*Indirect: // The test verifies that glDraw*Indirect:
// 1) generates an INVALID_OPERATION error if the number of views in the draw framebuffer is greater // 1) generates an INVALID_OPERATION error if the number of views in the draw framebuffer is greater
// than 1. // than 1.
...@@ -344,4 +478,161 @@ TEST_P(MultiviewDrawValidationTest, ActiveTimeElapsedQuery) ...@@ -344,4 +478,161 @@ TEST_P(MultiviewDrawValidationTest, ActiveTimeElapsedQuery)
glDeleteQueries(1, &query); glDeleteQueries(1, &query);
} }
ANGLE_INSTANTIATE_TEST(MultiviewDrawValidationTest, ES31_OPENGL()); // The test checks that glDrawArrays can be used to render into two views.
\ No newline at end of file TEST_P(MultiviewSideBySideRenderDualViewTest, DrawArrays)
{
if (!requestMultiviewExtension())
{
return;
}
drawQuad(mProgram, "vPosition", 0.0f, 1.0f, true);
ASSERT_GL_NO_ERROR();
checkOutput();
}
// The test checks that glDrawElements can be used to render into two views.
TEST_P(MultiviewSideBySideRenderDualViewTest, DrawElements)
{
if (!requestMultiviewExtension())
{
return;
}
drawIndexedQuad(mProgram, "vPosition", 0.0f, 1.0f, true);
ASSERT_GL_NO_ERROR();
checkOutput();
}
// The test checks that glDrawRangeElements can be used to render into two views.
TEST_P(MultiviewSideBySideRenderDualViewTest, DrawRangeElements)
{
if (!requestMultiviewExtension())
{
return;
}
drawIndexedQuad(mProgram, "vPosition", 0.0f, 1.0f, true, true);
ASSERT_GL_NO_ERROR();
checkOutput();
}
// The test checks that glDrawArrays can be used to render into four views.
TEST_P(MultiviewSideBySideRenderTest, DrawArraysFourViews)
{
if (!requestMultiviewExtension())
{
return;
}
const std::string vsSource =
"#version 300 es\n"
"#extension GL_OVR_multiview2 : require\n"
"layout(num_views = 4) in;\n"
"in vec4 vPosition;\n"
"void main()\n"
"{\n"
" if (gl_ViewID_OVR == 0u) {\n"
" gl_Position.x = vPosition.x*0.25 - 0.75;\n"
" } else if (gl_ViewID_OVR == 1u) {\n"
" gl_Position.x = vPosition.x*0.25 - 0.25;\n"
" } else if (gl_ViewID_OVR == 2u) {\n"
" gl_Position.x = vPosition.x*0.25 + 0.25;\n"
" } else {\n"
" gl_Position.x = vPosition.x*0.25 + 0.75;\n"
" }"
" gl_Position.yzw = vPosition.yzw;\n"
"}\n";
const std::string fsSource =
"#version 300 es\n"
"#extension GL_OVR_multiview2 : require\n"
"precision mediump float;\n"
"out vec4 col;\n"
"void main()\n"
"{\n"
" col = vec4(1,0,0,0);\n"
"}\n";
createFBO(16, 1, 4);
ANGLE_GL_PROGRAM(program, vsSource, fsSource);
glUseProgram(program);
drawQuad(program, "vPosition", 0.0f, 1.0f, true);
ASSERT_GL_NO_ERROR();
for (int i = 0; i < 4; ++i)
{
for (int j = 0; j < 4; ++j)
{
const int arrayIndex = i * 4 + j;
if (i == j)
{
EXPECT_PIXEL_EQ(arrayIndex, 0, 255, 0, 0, 0);
}
else
{
EXPECT_PIXEL_EQ(arrayIndex, 0, 0, 0, 0, 0);
}
}
}
EXPECT_GL_NO_ERROR();
}
// The test checks that glDrawArraysInstanced can be used to render into two views.
TEST_P(MultiviewSideBySideRenderTest, DrawArraysInstanced)
{
if (!requestMultiviewExtension())
{
return;
}
const std::string vsSource =
"#version 300 es\n"
"#extension GL_OVR_multiview : require\n"
"layout(num_views = 2) in;\n"
"in vec4 vPosition;\n"
"void main()\n"
"{\n"
" vec4 p = vPosition;\n"
" if (gl_InstanceID == 1){\n"
" p.y = .5*p.y + .5;\n"
" } else {\n"
" p.y = p.y*.5;\n"
" }\n"
" gl_Position.x = (gl_ViewID_OVR == 0u ? p.x*0.5 + 0.5 : p.x*0.5);\n"
" gl_Position.yzw = p.yzw;\n"
"}\n";
const std::string fsSource =
"#version 300 es\n"
"#extension GL_OVR_multiview : require\n"
"precision mediump float;\n"
"out vec4 col;\n"
"void main()\n"
"{\n"
" col = vec4(1,0,0,0);\n"
"}\n";
createFBO(4, 2, 2);
ANGLE_GL_PROGRAM(program, vsSource, fsSource);
glUseProgram(program);
drawQuad(program, "vPosition", 0.0f, 1.0f, true, true, 2);
ASSERT_GL_NO_ERROR();
const GLubyte expectedResult[4][8] = {
{0, 255, 255, 0}, {0, 255, 255, 0},
};
for (int row = 0; row < 2; ++row)
{
for (int col = 0; col < 4; ++col)
{
EXPECT_PIXEL_EQ(col, row, expectedResult[row][col], 0, 0, 0);
}
}
}
ANGLE_INSTANTIATE_TEST(MultiviewDrawValidationTest, ES31_OPENGL());
ANGLE_INSTANTIATE_TEST(MultiviewSideBySideRenderDualViewTest, ES3_OPENGL());
ANGLE_INSTANTIATE_TEST(MultiviewSideBySideRenderTest, ES3_OPENGL());
\ No newline at end of file
...@@ -412,6 +412,18 @@ void ANGLETestBase::drawQuad(GLuint program, ...@@ -412,6 +412,18 @@ void ANGLETestBase::drawQuad(GLuint program,
GLfloat positionAttribXYScale, GLfloat positionAttribXYScale,
bool useVertexBuffer) bool useVertexBuffer)
{ {
drawQuad(program, positionAttribName, positionAttribZ, positionAttribXYScale, useVertexBuffer,
false, 0u);
}
void ANGLETestBase::drawQuad(GLuint program,
const std::string &positionAttribName,
GLfloat positionAttribZ,
GLfloat positionAttribXYScale,
bool useVertexBuffer,
bool useInstancedDrawCalls,
GLuint numInstances)
{
GLint previousProgram = 0; GLint previousProgram = 0;
glGetIntegerv(GL_CURRENT_PROGRAM, &previousProgram); glGetIntegerv(GL_CURRENT_PROGRAM, &previousProgram);
if (previousProgram != static_cast<GLint>(program)) if (previousProgram != static_cast<GLint>(program))
...@@ -441,7 +453,14 @@ void ANGLETestBase::drawQuad(GLuint program, ...@@ -441,7 +453,14 @@ void ANGLETestBase::drawQuad(GLuint program,
} }
glEnableVertexAttribArray(positionLocation); glEnableVertexAttribArray(positionLocation);
glDrawArrays(GL_TRIANGLES, 0, 6); if (useInstancedDrawCalls)
{
glDrawArraysInstanced(GL_TRIANGLES, 0, 6, numInstances);
}
else
{
glDrawArrays(GL_TRIANGLES, 0, 6);
}
glDisableVertexAttribArray(positionLocation); glDisableVertexAttribArray(positionLocation);
glVertexAttribPointer(positionLocation, 4, GL_FLOAT, GL_FALSE, 0, nullptr); glVertexAttribPointer(positionLocation, 4, GL_FLOAT, GL_FALSE, 0, nullptr);
...@@ -473,6 +492,17 @@ void ANGLETestBase::drawIndexedQuad(GLuint program, ...@@ -473,6 +492,17 @@ void ANGLETestBase::drawIndexedQuad(GLuint program,
GLfloat positionAttribXYScale, GLfloat positionAttribXYScale,
bool useIndexBuffer) bool useIndexBuffer)
{ {
drawIndexedQuad(program, positionAttribName, positionAttribZ, positionAttribXYScale,
useIndexBuffer, false);
}
void ANGLETestBase::drawIndexedQuad(GLuint program,
const std::string &positionAttribName,
GLfloat positionAttribZ,
GLfloat positionAttribXYScale,
bool useIndexBuffer,
bool restrictedRange)
{
GLint positionLocation = glGetAttribLocation(program, positionAttribName.c_str()); GLint positionLocation = glGetAttribLocation(program, positionAttribName.c_str());
GLint activeProgram = 0; GLint activeProgram = 0;
...@@ -506,7 +536,14 @@ void ANGLETestBase::drawIndexedQuad(GLuint program, ...@@ -506,7 +536,14 @@ void ANGLETestBase::drawIndexedQuad(GLuint program,
indices = angle::IndexedQuadIndices; indices = angle::IndexedQuadIndices;
} }
glDrawElements(GL_TRIANGLES, 6, GL_UNSIGNED_SHORT, indices); if (!restrictedRange)
{
glDrawElements(GL_TRIANGLES, 6, GL_UNSIGNED_SHORT, indices);
}
else
{
glDrawRangeElements(GL_TRIANGLES, 0, 3, 6, GL_UNSIGNED_SHORT, indices);
}
if (useIndexBuffer) if (useIndexBuffer)
{ {
......
...@@ -246,6 +246,13 @@ class ANGLETestBase ...@@ -246,6 +246,13 @@ class ANGLETestBase
GLfloat positionAttribZ, GLfloat positionAttribZ,
GLfloat positionAttribXYScale, GLfloat positionAttribXYScale,
bool useVertexBuffer); bool useVertexBuffer);
void drawQuad(GLuint program,
const std::string &positionAttribName,
GLfloat positionAttribZ,
GLfloat positionAttribXYScale,
bool useVertexBuffer,
bool useInstancedDrawCalls,
GLuint numInstances);
static std::array<angle::Vector3, 6> GetQuadVertices(); static std::array<angle::Vector3, 6> GetQuadVertices();
void drawIndexedQuad(GLuint program, void drawIndexedQuad(GLuint program,
const std::string &positionAttribName, const std::string &positionAttribName,
...@@ -260,6 +267,13 @@ class ANGLETestBase ...@@ -260,6 +267,13 @@ class ANGLETestBase
GLfloat positionAttribXYScale, GLfloat positionAttribXYScale,
bool useBufferObject); bool useBufferObject);
void drawIndexedQuad(GLuint program,
const std::string &positionAttribName,
GLfloat positionAttribZ,
GLfloat positionAttribXYScale,
bool useBufferObject,
bool restrictedRange);
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);
static bool extensionRequestable(const std::string &extName); static bool extensionRequestable(const std::string &extName);
......
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