Commit d4cfa57d by Jamie Madill

Move more draw call validation to the API.

The GL expects us to reject invalid draw calls before we start doing any work, so we can prevent internal unnecessary state changes. Also update the program binary's cached sampler data when we validate. The previous patch was breaking draw calls in Google Earth WebGL. BUG=angle:571 BUG=390412 Change-Id: I1c4e204ae2467afc36b76af975a3a49e26349639 Reviewed-on: https://chromium-review.googlesource.com/206482Tested-by: 's avatarJamie Madill <jmadill@chromium.org> Reviewed-by: 's avatarGeoff Lang <geofflang@chromium.org>
parent f6d38b05
......@@ -1336,6 +1336,11 @@ void Context::setProgramBinary(GLuint program, const void *binary, GLint length)
}
GLuint Context::getCurrentProgram() const
{
return mState.currentProgram;
}
void Context::bindTransformFeedback(GLuint transformFeedback)
{
TransformFeedback *transformFeedbackObject = getTransformFeedback(transformFeedback);
......@@ -1479,7 +1484,7 @@ Buffer *Context::getElementArrayBuffer() const
return getCurrentVertexArray()->getElementArrayBuffer();
}
ProgramBinary *Context::getCurrentProgramBinary()
ProgramBinary *Context::getCurrentProgramBinary() const
{
return mCurrentProgramBinary.get();
}
......@@ -2829,13 +2834,10 @@ void Context::readPixels(GLint x, GLint y, GLsizei width, GLsizei height,
void Context::drawArrays(GLenum mode, GLint first, GLsizei count, GLsizei instances)
{
if (!mState.currentProgram)
{
return gl::error(GL_INVALID_OPERATION);
}
ASSERT(mState.currentProgram);
ProgramBinary *programBinary = getCurrentProgramBinary();
programBinary->applyUniforms();
programBinary->updateSamplerMapping();
Texture *vsTextures[IMPLEMENTATION_MAX_VERTEX_TEXTURE_IMAGE_UNITS];
TextureType vsTextureTypes[IMPLEMENTATION_MAX_VERTEX_TEXTURE_IMAGE_UNITS];
......@@ -2883,11 +2885,6 @@ void Context::drawArrays(GLenum mode, GLint first, GLsizei count, GLsizei instan
return;
}
if (!programBinary->validateSamplers(NULL))
{
return gl::error(GL_INVALID_OPERATION);
}
if (!skipDraw(mode))
{
mRenderer->drawArrays(mode, count, instances, transformFeedbackActive);
......@@ -2901,19 +2898,10 @@ void Context::drawArrays(GLenum mode, GLint first, GLsizei count, GLsizei instan
void Context::drawElements(GLenum mode, GLsizei count, GLenum type, const GLvoid *indices, GLsizei instances)
{
if (!mState.currentProgram)
{
return gl::error(GL_INVALID_OPERATION);
}
VertexArray *vao = getCurrentVertexArray();
if (!indices && !vao->getElementArrayBuffer())
{
return gl::error(GL_INVALID_OPERATION);
}
ASSERT(mState.currentProgram);
ProgramBinary *programBinary = getCurrentProgramBinary();
programBinary->applyUniforms();
programBinary->updateSamplerMapping();
Texture *vsTextures[IMPLEMENTATION_MAX_VERTEX_TEXTURE_IMAGE_UNITS];
TextureType vsTextureTypes[IMPLEMENTATION_MAX_VERTEX_TEXTURE_IMAGE_UNITS];
......@@ -2940,6 +2928,7 @@ void Context::drawElements(GLenum mode, GLsizei count, GLenum type, const GLvoid
applyState(mode);
VertexArray *vao = getCurrentVertexArray();
rx::TranslatedIndexData indexInfo;
GLenum err = mRenderer->applyIndexBuffer(indices, vao->getElementArrayBuffer(), count, mode, type, &indexInfo);
if (err != GL_NO_ERROR)
......@@ -2972,11 +2961,6 @@ void Context::drawElements(GLenum mode, GLsizei count, GLenum type, const GLvoid
return;
}
if (!programBinary->validateSamplers(NULL))
{
return gl::error(GL_INVALID_OPERATION);
}
if (!skipDraw(mode))
{
mRenderer->drawElements(mode, count, type, indices, vao->getElementArrayBuffer(), indexInfo, instances);
......
......@@ -306,6 +306,7 @@ class Context
void useProgram(GLuint program);
void linkProgram(GLuint program);
void setProgramBinary(GLuint program, const void *binary, GLint length);
GLuint getCurrentProgram() const;
void bindTransformFeedback(GLuint transformFeedback);
void beginQuery(GLenum target, GLuint query);
......@@ -341,7 +342,7 @@ class Context
Buffer *getTargetBuffer(GLenum target) const;
Buffer *getArrayBuffer();
Buffer *getElementArrayBuffer() const;
ProgramBinary *getCurrentProgramBinary();
ProgramBinary *getCurrentProgramBinary() const;
Texture *getTargetTexture(GLenum target) const;
Texture2D *getTexture2D() const;
......
......@@ -150,6 +150,7 @@ ProgramBinary::ProgramBinary(rx::Renderer *renderer)
mUsedPixelSamplerRange(0),
mUsesPointSize(false),
mShaderVersion(100),
mDirtySamplerMapping(true),
mVertexUniformStorage(NULL),
mFragmentUniformStorage(NULL),
mValidated(false),
......@@ -765,6 +766,13 @@ void ProgramBinary::setUniform1iv(GLint location, GLsizei count, const GLint *v)
}
}
else UNREACHABLE();
// Set a special flag if we change a sampler uniform
if (IsSampler(targetUniform->type) &&
(memcmp(targetUniform->data, v, sizeof(GLint)) != 0))
{
mDirtySamplerMapping = true;
}
}
void ProgramBinary::setUniform2iv(GLint location, GLsizei count, const GLint *v)
......@@ -909,9 +917,15 @@ void ProgramBinary::dirtyAllUniforms()
}
}
// Applies all the uniforms set for this program object to the renderer
void ProgramBinary::applyUniforms()
void ProgramBinary::updateSamplerMapping()
{
if (!mDirtySamplerMapping)
{
return;
}
mDirtySamplerMapping = false;
// Retrieve sampler uniform values
for (size_t uniformIndex = 0; uniformIndex < mUniforms.size(); uniformIndex++)
{
......@@ -922,7 +936,7 @@ void ProgramBinary::applyUniforms()
if (IsSampler(targetUniform->type))
{
int count = targetUniform->elementCount();
GLint (*v)[4] = (GLint(*)[4])targetUniform->data;
GLint (*v)[4] = reinterpret_cast<GLint(*)[4]>(targetUniform->data);
if (targetUniform->isReferencedByFragmentShader())
{
......@@ -958,6 +972,12 @@ void ProgramBinary::applyUniforms()
}
}
}
}
// Applies all the uniforms set for this program object to the renderer
void ProgramBinary::applyUniforms()
{
updateSamplerMapping();
mRenderer->applyUniforms(*this);
......@@ -2615,6 +2635,7 @@ bool ProgramBinary::validateSamplers(InfoLog *infoLog)
// if any two active samplers in a program are of different types, but refer to the same
// texture image unit, and this is the current program, then ValidateProgram will fail, and
// DrawArrays and DrawElements will issue the INVALID_OPERATION error.
updateSamplerMapping();
const unsigned int maxCombinedTextureImageUnits = mRenderer->getMaxCombinedTextureImageUnits();
TextureType textureUnitType[IMPLEMENTATION_MAX_COMBINED_TEXTURE_IMAGE_UNITS];
......@@ -2800,6 +2821,7 @@ void ProgramBinary::reset()
mUsedPixelSamplerRange = 0;
mUsesPointSize = false;
mShaderVersion = 0;
mDirtySamplerMapping = true;
SafeDeleteContainer(mUniforms);
SafeDeleteContainer(mUniformBlocks);
......
......@@ -162,6 +162,7 @@ class ProgramBinary : public RefCountObject
void validate(InfoLog &infoLog);
bool validateSamplers(InfoLog *infoLog);
bool isValidated() const;
void updateSamplerMapping();
unsigned int getSerial() const;
int getShaderVersion() const;
......@@ -290,6 +291,7 @@ class ProgramBinary : public RefCountObject
GLuint mUsedPixelSamplerRange;
bool mUsesPointSize;
int mShaderVersion;
bool mDirtySamplerMapping;
std::vector<LinkedUniform*> mUniforms;
std::vector<UniformBlock*> mUniformBlocks;
......
......@@ -19,6 +19,7 @@
#include "libGLESv2/Query.h"
#include "libGLESv2/ProgramBinary.h"
#include "libGLESv2/TransformFeedback.h"
#include "libGLESv2/VertexArray.h"
#include "common/mathutil.h"
#include "common/utilities.h"
......@@ -1346,6 +1347,17 @@ static bool ValidateDrawBase(const gl::Context *context, GLenum mode, GLsizei co
return gl::error(GL_INVALID_FRAMEBUFFER_OPERATION, false);
}
if (!context->getCurrentProgram())
{
return gl::error(GL_INVALID_OPERATION, false);
}
gl::ProgramBinary *programBinary = context->getCurrentProgramBinary();
if (!programBinary->validateSamplers(NULL))
{
return gl::error(GL_INVALID_OPERATION, false);
}
// No-op if zero count
return (count > 0);
}
......@@ -1422,6 +1434,12 @@ bool ValidateDrawElements(const gl::Context *context, GLenum mode, GLsizei count
return gl::error(GL_INVALID_OPERATION, false);
}
gl::VertexArray *vao = context->getCurrentVertexArray();
if (!indices && !vao->getElementArrayBuffer())
{
return gl::error(GL_INVALID_OPERATION, false);
}
if (!ValidateDrawBase(context, mode, count))
{
return false;
......
......@@ -16,12 +16,17 @@ protected:
virtual void SetUp()
{
ANGLETest::SetUp();
glGenTextures(1, &mTexture);
glGenTextures(1, &mTexture2D);
glGenTextures(1, &mTextureCube);
glBindTexture(GL_TEXTURE_2D, mTexture);
glBindTexture(GL_TEXTURE_2D, mTexture2D);
glTexImage2D(GL_TEXTURE_2D, 0, GL_RGBA, 1, 1, 0, GL_RGBA, GL_UNSIGNED_BYTE, NULL);
EXPECT_GL_NO_ERROR();
glBindTexture(GL_TEXTURE_CUBE_MAP, mTextureCube);
glTexStorage2DEXT(GL_TEXTURE_CUBE_MAP, 1, GL_RGBA8, 1, 1);
EXPECT_GL_NO_ERROR();
ASSERT_GL_NO_ERROR();
const std::string vertexShaderSource = SHADER_SOURCE
......@@ -37,7 +42,7 @@ protected:
}
);
const std::string fragmentShaderSource = SHADER_SOURCE
const std::string fragmentShaderSource2D = SHADER_SOURCE
(
precision highp float;
uniform sampler2D tex;
......@@ -49,32 +54,51 @@ protected:
}
);
mProgram = compileProgram(vertexShaderSource, fragmentShaderSource);
if (mProgram == 0)
const std::string fragmentShaderSourceCube = SHADER_SOURCE
(
precision highp float;
uniform sampler2D tex2D;
uniform samplerCube texCube;
varying vec2 texcoord;
void main()
{
gl_FragColor = texture2D(tex2D, texcoord);
gl_FragColor += textureCube(texCube, vec3(texcoord, 0));
}
);
m2DProgram = compileProgram(vertexShaderSource, fragmentShaderSource2D);
mCubeProgram = compileProgram(vertexShaderSource, fragmentShaderSourceCube);
if (m2DProgram == 0 || mCubeProgram == 0)
{
FAIL() << "shader compilation failed.";
}
mTextureUniformLocation = glGetUniformLocation(mProgram, "tex");
mTexture2DUniformLocation = glGetUniformLocation(m2DProgram, "tex");
}
virtual void TearDown()
{
glDeleteTextures(1, &mTexture);
glDeleteProgram(mProgram);
glDeleteTextures(1, &mTexture2D);
glDeleteTextures(1, &mTextureCube);
glDeleteProgram(m2DProgram);
glDeleteProgram(mCubeProgram);
ANGLETest::TearDown();
}
GLuint mTexture;
GLuint mTexture2D;
GLuint mTextureCube;
GLuint mProgram;
GLint mTextureUniformLocation;
GLuint m2DProgram;
GLuint mCubeProgram;
GLint mTexture2DUniformLocation;
};
TEST_F(TextureTest, negative_api_subimage)
{
glBindTexture(GL_TEXTURE_2D, mTexture);
glBindTexture(GL_TEXTURE_2D, mTexture2D);
EXPECT_GL_ERROR(GL_NO_ERROR);
const GLubyte *pixels[20] = { 0 };
......@@ -84,13 +108,13 @@ TEST_F(TextureTest, negative_api_subimage)
TEST_F(TextureTest, zero_sized_uploads)
{
glBindTexture(GL_TEXTURE_2D, mTexture);
glBindTexture(GL_TEXTURE_2D, mTexture2D);
EXPECT_GL_ERROR(GL_NO_ERROR);
// Use the texture first to make sure it's in video memory
glUseProgram(mProgram);
glUniform1i(mTextureUniformLocation, 0);
drawQuad(mProgram, "position", 0.5f);
glUseProgram(m2DProgram);
glUniform1i(mTexture2DUniformLocation, 0);
drawQuad(m2DProgram, "position", 0.5f);
const GLubyte *pixel[4] = { 0 };
......@@ -103,3 +127,23 @@ TEST_F(TextureTest, zero_sized_uploads)
glTexSubImage2D(GL_TEXTURE_2D, 0, 0, 0, 1, 0, GL_RGBA, GL_UNSIGNED_BYTE, pixel);
EXPECT_GL_NO_ERROR();
}
// Test drawing with two texture types, to trigger an ANGLE bug in validation
TEST_F(TextureTest, cube_map_bug)
{
glActiveTexture(GL_TEXTURE0);
glBindTexture(GL_TEXTURE_2D, mTexture2D);
glActiveTexture(GL_TEXTURE1);
glBindTexture(GL_TEXTURE_CUBE_MAP, mTextureCube);
EXPECT_GL_ERROR(GL_NO_ERROR);
glUseProgram(mCubeProgram);
GLint tex2DUniformLocation = glGetUniformLocation(mCubeProgram, "tex2D");
GLint texCubeUniformLocation = glGetUniformLocation(mCubeProgram, "texCube");
EXPECT_NE(-1, tex2DUniformLocation);
EXPECT_NE(-1, texCubeUniformLocation);
glUniform1i(tex2DUniformLocation, 0);
glUniform1i(texCubeUniformLocation, 1);
drawQuad(mCubeProgram, "position", 0.5f);
EXPECT_GL_NO_ERROR();
}
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