Inline and micro-optimize more for perf tests.
Using a custom array instead of std::vector speeds up the resource
manager. One reason is because calls to size() are implemented in many
implementations as a difference between two pointers. This sub size
implementations are slower than storing a simple size variable in a
custom class.
Also includes more inlining of hot spots functions.
Also includes a small unit test class for ResourceMap. And an unrelated
but small test fix for TextureLimisTest. Also a small unrelated fix for
a Transform Feedback test.
Increase the scores of the draw call perf test with texture and buffer
bindings and the buffer binding perf test.
Bug: angleproject:2763
Change-Id: I41c327987db27ac45e6a62579f01e1cdc22e396c
Reviewed-on: https://chromium-review.googlesource.com/1171510
Reviewed-by: Shahbaz Youssefi <syoussefi@chromium.org>
Reviewed-by: Jamie Madill <jmadill@chromium.org>
Commit-Queue: Jamie Madill <jmadill@chromium.org>
diff --git a/src/libANGLE/Context.cpp b/src/libANGLE/Context.cpp
index dc6326a..f20d64e 100644
--- a/src/libANGLE/Context.cpp
+++ b/src/libANGLE/Context.cpp
@@ -972,11 +972,6 @@
return mState.mBuffers->getBuffer(handle);
}
-Texture *Context::getTexture(GLuint handle) const
-{
- return mState.mTextures->getTexture(handle);
-}
-
Renderbuffer *Context::getRenderbuffer(GLuint handle) const
{
return mState.mRenderbuffers->getRenderbuffer(handle);
diff --git a/src/libANGLE/Context.h b/src/libANGLE/Context.h
index bfaa81d..304317f 100644
--- a/src/libANGLE/Context.h
+++ b/src/libANGLE/Context.h
@@ -474,7 +474,8 @@
Buffer *getBuffer(GLuint handle) const;
FenceNV *getFenceNV(GLuint handle);
Sync *getSync(GLsync handle) const;
- Texture *getTexture(GLuint handle) const;
+ Texture *getTexture(GLuint handle) const { return mState.mTextures->getTexture(handle); }
+
Framebuffer *getFramebuffer(GLuint handle) const;
Renderbuffer *getRenderbuffer(GLuint handle) const;
VertexArray *getVertexArray(GLuint handle) const;
diff --git a/src/libANGLE/ResourceManager.cpp b/src/libANGLE/ResourceManager.cpp
index b81dc84..31c4f32 100644
--- a/src/libANGLE/ResourceManager.cpp
+++ b/src/libANGLE/ResourceManager.cpp
@@ -242,12 +242,6 @@
return AllocateEmptyObject(&mHandleAllocator, &mObjectMap);
}
-Texture *TextureManager::getTexture(GLuint handle) const
-{
- ASSERT(mObjectMap.query(0) == nullptr);
- return mObjectMap.query(handle);
-}
-
void TextureManager::signalAllTexturesDirty(const Context *context) const
{
for (const auto &texture : mObjectMap)
diff --git a/src/libANGLE/ResourceManager.h b/src/libANGLE/ResourceManager.h
index 828f3fb..d4deb57 100644
--- a/src/libANGLE/ResourceManager.h
+++ b/src/libANGLE/ResourceManager.h
@@ -166,7 +166,11 @@
{
public:
GLuint createTexture();
- Texture *getTexture(GLuint handle) const;
+ Texture *getTexture(GLuint handle) const
+ {
+ ASSERT(mObjectMap.query(0) == nullptr);
+ return mObjectMap.query(handle);
+ }
void signalAllTexturesDirty(const Context *context) const;
diff --git a/src/libANGLE/ResourceMap.h b/src/libANGLE/ResourceMap.h
index b00da68..7cfb9bd 100644
--- a/src/libANGLE/ResourceMap.h
+++ b/src/libANGLE/ResourceMap.h
@@ -23,7 +23,16 @@
ResourceMap();
~ResourceMap();
- ResourceType *query(GLuint handle) const;
+ ANGLE_INLINE ResourceType *query(GLuint handle) const
+ {
+ if (handle < mFlatResourcesSize)
+ {
+ ResourceType *value = mFlatResources[handle];
+ return (value == InvalidPointer() ? nullptr : value);
+ }
+ auto it = mHashedResources.find(handle);
+ return (it == mHashedResources.end() ? nullptr : it->second);
+ }
// Returns true if the handle was reserved. Not necessarily if the resource is created.
bool contains(GLuint handle) const;
@@ -84,7 +93,11 @@
// Experimental testing suggests that 16k is a reasonable upper limit.
static constexpr size_t kFlatResourcesLimit = 0x4000;
- std::vector<ResourceType *> mFlatResources;
+ // Size of one map element.
+ static constexpr size_t kElementSize = sizeof(ResourceType *);
+
+ size_t mFlatResourcesSize;
+ ResourceType **mFlatResources;
// A map of GL objects indexed by object ID.
HashMap mHashedResources;
@@ -92,8 +105,11 @@
template <typename ResourceType>
ResourceMap<ResourceType>::ResourceMap()
- : mFlatResources(kInitialFlatResourcesSize, InvalidPointer()), mHashedResources()
+ : mFlatResourcesSize(kInitialFlatResourcesSize),
+ mFlatResources(new ResourceType *[kInitialFlatResourcesSize]),
+ mHashedResources()
{
+ memset(mFlatResources, kInvalidPointer, mFlatResourcesSize * kElementSize);
}
template <typename ResourceType>
@@ -103,21 +119,9 @@
}
template <typename ResourceType>
-ResourceType *ResourceMap<ResourceType>::query(GLuint handle) const
-{
- if (handle < mFlatResources.size())
- {
- auto value = mFlatResources[handle];
- return (value == InvalidPointer() ? nullptr : value);
- }
- auto it = mHashedResources.find(handle);
- return (it == mHashedResources.end() ? nullptr : it->second);
-}
-
-template <typename ResourceType>
bool ResourceMap<ResourceType>::contains(GLuint handle) const
{
- if (handle < mFlatResources.size())
+ if (handle < mFlatResourcesSize)
{
return (mFlatResources[handle] != InvalidPointer());
}
@@ -127,7 +131,7 @@
template <typename ResourceType>
bool ResourceMap<ResourceType>::erase(GLuint handle, ResourceType **resourceOut)
{
- if (handle < mFlatResources.size())
+ if (handle < mFlatResourcesSize)
{
auto &value = mFlatResources[handle];
if (value == InvalidPointer())
@@ -155,17 +159,25 @@
{
if (handle < kFlatResourcesLimit)
{
- if (handle >= mFlatResources.size())
+ if (handle >= mFlatResourcesSize)
{
// Use power-of-two.
- size_t newSize = mFlatResources.size();
+ size_t newSize = mFlatResourcesSize;
while (newSize <= handle)
{
newSize *= 2;
}
- mFlatResources.resize(newSize, nullptr);
+
+ ResourceType **oldResources = mFlatResources;
+
+ mFlatResources = new ResourceType *[newSize];
+ memset(&mFlatResources[mFlatResourcesSize], kInvalidPointer,
+ (newSize - mFlatResourcesSize) * kElementSize);
+ memcpy(mFlatResources, oldResources, mFlatResourcesSize * kElementSize);
+ mFlatResourcesSize = newSize;
+ delete[] oldResources;
}
- ASSERT(mFlatResources.size() > handle);
+ ASSERT(mFlatResourcesSize > handle);
mFlatResources[handle] = resource;
}
else
@@ -183,13 +195,13 @@
template <typename ResourceType>
typename ResourceMap<ResourceType>::Iterator ResourceMap<ResourceType>::end() const
{
- return Iterator(*this, static_cast<GLuint>(mFlatResources.size()), mHashedResources.end());
+ return Iterator(*this, static_cast<GLuint>(mFlatResourcesSize), mHashedResources.end());
}
template <typename ResourceType>
typename ResourceMap<ResourceType>::Iterator ResourceMap<ResourceType>::find(GLuint handle) const
{
- if (handle < mFlatResources.size())
+ if (handle < mFlatResourcesSize)
{
return (mFlatResources[handle] != InvalidPointer()
? Iterator(handle, mHashedResources.begin())
@@ -210,21 +222,22 @@
template <typename ResourceType>
void ResourceMap<ResourceType>::clear()
{
- mFlatResources.assign(kInitialFlatResourcesSize, InvalidPointer());
+ memset(mFlatResources, kInvalidPointer, kInitialFlatResourcesSize * kElementSize);
+ mFlatResourcesSize = kInitialFlatResourcesSize;
mHashedResources.clear();
}
template <typename ResourceType>
GLuint ResourceMap<ResourceType>::nextNonNullResource(size_t flatIndex) const
{
- for (size_t index = flatIndex; index < mFlatResources.size(); index++)
+ for (size_t index = flatIndex; index < mFlatResourcesSize; index++)
{
if (mFlatResources[index] != nullptr && mFlatResources[index] != InvalidPointer())
{
return static_cast<GLuint>(index);
}
}
- return static_cast<GLuint>(mFlatResources.size());
+ return static_cast<GLuint>(mFlatResourcesSize);
}
template <typename ResourceType>
@@ -259,7 +272,7 @@
template <typename ResourceType>
typename ResourceMap<ResourceType>::Iterator &ResourceMap<ResourceType>::Iterator::operator++()
{
- if (mFlatIndex < static_cast<GLuint>(mOrigin.mFlatResources.size()))
+ if (mFlatIndex < static_cast<GLuint>(mOrigin.mFlatResourcesSize))
{
mFlatIndex = mOrigin.nextNonNullResource(mFlatIndex + 1);
}
@@ -288,7 +301,7 @@
template <typename ResourceType>
void ResourceMap<ResourceType>::Iterator::updateValue()
{
- if (mFlatIndex < static_cast<GLuint>(mOrigin.mFlatResources.size()))
+ if (mFlatIndex < static_cast<GLuint>(mOrigin.mFlatResourcesSize))
{
mValue.first = mFlatIndex;
mValue.second = mOrigin.mFlatResources[mFlatIndex];
diff --git a/src/libANGLE/ResourceMap_unittest.cpp b/src/libANGLE/ResourceMap_unittest.cpp
new file mode 100644
index 0000000..fd9d3e9
--- /dev/null
+++ b/src/libANGLE/ResourceMap_unittest.cpp
@@ -0,0 +1,136 @@
+//
+// Copyright 2018 The ANGLE Project Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+//
+// ResourceMap_unittest:
+// Unit tests for the ResourceMap template class.
+//
+
+#include <gtest/gtest.h>
+
+#include "libANGLE/ResourceMap.h"
+
+using namespace gl;
+
+namespace
+{
+// Tests assigning slots in the map and then deleting elements.
+TEST(ResourceMapTest, AssignAndErase)
+{
+ constexpr size_t kSize = 64;
+ ResourceMap<size_t> resourceMap;
+ std::vector<size_t> objects(kSize, 1);
+ for (size_t index = 0; index < kSize; ++index)
+ {
+ resourceMap.assign(index + 1, &objects[index]);
+ }
+
+ for (size_t index = 0; index < kSize; ++index)
+ {
+ size_t *found = nullptr;
+ ASSERT_TRUE(resourceMap.erase(index + 1, &found));
+ ASSERT_EQ(&objects[index], found);
+ }
+
+ ASSERT_TRUE(resourceMap.empty());
+}
+
+// Tests assigning slots in the map and then using clear() to free it.
+TEST(ResourceMapTest, AssignAndClear)
+{
+ constexpr size_t kSize = 64;
+ ResourceMap<size_t> resourceMap;
+ std::vector<size_t> objects(kSize, 1);
+ for (size_t index = 0; index < kSize; ++index)
+ {
+ resourceMap.assign(index + 1, &objects[index]);
+ }
+
+ resourceMap.clear();
+ ASSERT_TRUE(resourceMap.empty());
+}
+
+// Tests growing a map more than double the size.
+TEST(ResourceMapTest, BigGrowth)
+{
+ constexpr size_t kSize = 8;
+
+ ResourceMap<size_t> resourceMap;
+ std::vector<size_t> objects;
+
+ for (size_t index = 0; index < kSize; ++index)
+ {
+ objects.push_back(index);
+ }
+
+ // Assign a large value.
+ constexpr size_t kLargeIndex = 128;
+ objects.push_back(kLargeIndex);
+
+ for (size_t &object : objects)
+ {
+ resourceMap.assign(object, &object);
+ }
+
+ for (size_t object : objects)
+ {
+ size_t *found = nullptr;
+ ASSERT_TRUE(resourceMap.erase(object, &found));
+ ASSERT_EQ(object, *found);
+ }
+
+ ASSERT_TRUE(resourceMap.empty());
+}
+
+// Tests querying unassigned or erased values.
+TEST(ResourceMapTest, QueryUnassigned)
+{
+ constexpr size_t kSize = 8;
+
+ ResourceMap<size_t> resourceMap;
+ std::vector<size_t> objects;
+
+ for (size_t index = 0; index < kSize; ++index)
+ {
+ objects.push_back(index);
+ }
+
+ ASSERT_FALSE(resourceMap.contains(0));
+ ASSERT_EQ(nullptr, resourceMap.query(0));
+ ASSERT_FALSE(resourceMap.contains(100));
+ ASSERT_EQ(nullptr, resourceMap.query(100));
+
+ for (size_t &object : objects)
+ {
+ resourceMap.assign(object, &object);
+ }
+
+ ASSERT_FALSE(resourceMap.empty());
+
+ for (size_t &object : objects)
+ {
+ ASSERT_TRUE(resourceMap.contains(object));
+ ASSERT_EQ(&object, resourceMap.query(object));
+ }
+
+ ASSERT_FALSE(resourceMap.contains(10));
+ ASSERT_EQ(nullptr, resourceMap.query(10));
+ ASSERT_FALSE(resourceMap.contains(100));
+ ASSERT_EQ(nullptr, resourceMap.query(100));
+
+ for (size_t object : objects)
+ {
+ size_t *found = nullptr;
+ ASSERT_TRUE(resourceMap.erase(object, &found));
+ ASSERT_EQ(object, *found);
+ }
+
+ ASSERT_TRUE(resourceMap.empty());
+
+ ASSERT_FALSE(resourceMap.contains(0));
+ ASSERT_EQ(nullptr, resourceMap.query(0));
+ ASSERT_FALSE(resourceMap.contains(100));
+ ASSERT_EQ(nullptr, resourceMap.query(100));
+}
+} // anonymous namespace
diff --git a/src/libANGLE/State.cpp b/src/libANGLE/State.cpp
index 7a78ae4..2a5b8f0 100644
--- a/src/libANGLE/State.cpp
+++ b/src/libANGLE/State.cpp
@@ -1033,12 +1033,6 @@
return getSamplerTexture(static_cast<unsigned int>(mActiveSampler), type);
}
-Texture *State::getSamplerTexture(unsigned int sampler, TextureType type) const
-{
- ASSERT(sampler < mSamplerTextures[type].size());
- return mSamplerTextures[type][sampler].get();
-}
-
GLuint State::getSamplerTextureId(unsigned int sampler, TextureType type) const
{
ASSERT(sampler < mSamplerTextures[type].size());
diff --git a/src/libANGLE/State.h b/src/libANGLE/State.h
index 4d1f0e6..84446ec 100644
--- a/src/libANGLE/State.h
+++ b/src/libANGLE/State.h
@@ -162,10 +162,7 @@
void setFragmentShaderDerivativeHint(GLenum hint);
// GL_CHROMIUM_bind_generates_resource
- bool isBindGeneratesResourceEnabled() const
- {
- return mBindGeneratesResource;
- }
+ bool isBindGeneratesResourceEnabled() const { return mBindGeneratesResource; }
// GL_ANGLE_client_arrays
bool areClientArraysEnabled() const;
@@ -179,7 +176,13 @@
unsigned int getActiveSampler() const;
void setSamplerTexture(const Context *context, TextureType type, Texture *texture);
Texture *getTargetTexture(TextureType type) const;
- Texture *getSamplerTexture(unsigned int sampler, TextureType type) const;
+
+ Texture *getSamplerTexture(unsigned int sampler, TextureType type) const
+ {
+ ASSERT(sampler < mSamplerTextures[type].size());
+ return mSamplerTextures[type][sampler].get();
+ }
+
GLuint getSamplerTextureId(unsigned int sampler, TextureType type) const;
void detachTexture(const Context *context, const TextureMap &zeroTextures, GLuint texture);
void initializeZeroTextures(const Context *context, const TextureMap &zeroTextures);
diff --git a/src/libANGLE/renderer/d3d/d3d9/Renderer9.cpp b/src/libANGLE/renderer/d3d/d3d9/Renderer9.cpp
index 62be3fb..cfc22cf 100644
--- a/src/libANGLE/renderer/d3d/d3d9/Renderer9.cpp
+++ b/src/libANGLE/renderer/d3d/d3d9/Renderer9.cpp
@@ -979,7 +979,7 @@
gl::Texture *texture)
{
int d3dSamplerOffset = (type == gl::ShaderType::Fragment) ? 0 : D3DVERTEXTEXTURESAMPLER0;
- int d3dSampler = index + d3dSamplerOffset;
+ int d3dSampler = index + d3dSamplerOffset;
IDirect3DBaseTexture9 *d3dTexture = nullptr;
bool forceSetTexture = false;
diff --git a/src/libANGLE/validationES2.cpp b/src/libANGLE/validationES2.cpp
index 5a1521e..30b8251 100644
--- a/src/libANGLE/validationES2.cpp
+++ b/src/libANGLE/validationES2.cpp
@@ -2981,20 +2981,6 @@
bool ValidateBindTexture(Context *context, TextureType target, GLuint texture)
{
- Texture *textureObject = context->getTexture(texture);
- if (textureObject && textureObject->getType() != target && texture != 0)
- {
- ANGLE_VALIDATION_ERR(context, InvalidOperation(), TypeMismatch);
- return false;
- }
-
- if (!context->getGLState().isBindGeneratesResourceEnabled() &&
- !context->isTextureGenerated(texture))
- {
- context->handleError(InvalidOperation() << "Texture was not generated");
- return false;
- }
-
switch (target)
{
case TextureType::_2D:
@@ -3046,6 +3032,25 @@
return false;
}
+ if (texture == 0)
+ {
+ return true;
+ }
+
+ Texture *textureObject = context->getTexture(texture);
+ if (textureObject && textureObject->getType() != target)
+ {
+ ANGLE_VALIDATION_ERR(context, InvalidOperation(), TypeMismatch);
+ return false;
+ }
+
+ if (!context->getGLState().isBindGeneratesResourceEnabled() &&
+ !context->isTextureGenerated(texture))
+ {
+ context->handleError(InvalidOperation() << "Texture was not generated");
+ return false;
+ }
+
return true;
}
diff --git a/src/tests/angle_unittests.gni b/src/tests/angle_unittests.gni
index b39b5a5..f475ae1 100644
--- a/src/tests/angle_unittests.gni
+++ b/src/tests/angle_unittests.gni
@@ -25,6 +25,7 @@
"../libANGLE/Observer_unittest.cpp",
"../libANGLE/Program_unittest.cpp",
"../libANGLE/ResourceManager_unittest.cpp",
+ "../libANGLE/ResourceMap_unittest.cpp",
"../libANGLE/SizedMRUCache_unittest.cpp",
"../libANGLE/Surface_unittest.cpp",
"../libANGLE/TransformFeedback_unittest.cpp",
diff --git a/src/tests/gl_tests/TextureTest.cpp b/src/tests/gl_tests/TextureTest.cpp
index 4f1cf76..d05ad9e 100644
--- a/src/tests/gl_tests/TextureTest.cpp
+++ b/src/tests/gl_tests/TextureTest.cpp
@@ -2774,7 +2774,18 @@
setConfigAlphaBits(8);
}
- ~TextureLimitsTest()
+ void SetUp() override
+ {
+ ANGLETest::SetUp();
+
+ glGetIntegerv(GL_MAX_VERTEX_TEXTURE_IMAGE_UNITS, &mMaxVertexTextures);
+ glGetIntegerv(GL_MAX_TEXTURE_IMAGE_UNITS, &mMaxFragmentTextures);
+ glGetIntegerv(GL_MAX_COMBINED_TEXTURE_IMAGE_UNITS, &mMaxCombinedTextures);
+
+ ASSERT_GL_NO_ERROR();
+ }
+
+ void TearDown() override
{
if (mProgram != 0)
{
@@ -2786,17 +2797,8 @@
glDeleteTextures(static_cast<GLsizei>(mTextures.size()), &mTextures[0]);
}
}
- }
- void SetUp() override
- {
- ANGLETest::SetUp();
-
- glGetIntegerv(GL_MAX_VERTEX_TEXTURE_IMAGE_UNITS, &mMaxVertexTextures);
- glGetIntegerv(GL_MAX_TEXTURE_IMAGE_UNITS, &mMaxFragmentTextures);
- glGetIntegerv(GL_MAX_COMBINED_TEXTURE_IMAGE_UNITS, &mMaxCombinedTextures);
-
- ASSERT_GL_NO_ERROR();
+ ANGLETest::TearDown();
}
void compileProgramWithTextureCounts(const std::string &vertexPrefix,
diff --git a/src/tests/gl_tests/WebGLCompatibilityTest.cpp b/src/tests/gl_tests/WebGLCompatibilityTest.cpp
index 21c517b..ae790c7 100644
--- a/src/tests/gl_tests/WebGLCompatibilityTest.cpp
+++ b/src/tests/gl_tests/WebGLCompatibilityTest.cpp
@@ -3232,12 +3232,12 @@
// Create textures and allocate storage
GLTexture tex0;
GLTexture tex1;
- GLRenderbuffer rb;
+ GLTexture tex2;
FillTexture2D(tex0.get(), width, height, GLColor::black, 0, GL_RGBA, GL_RGBA, GL_UNSIGNED_BYTE);
FillTexture2D(tex1.get(), width, height, 0x80, 0, GL_DEPTH_COMPONENT16, GL_DEPTH_COMPONENT,
GL_UNSIGNED_INT);
- glBindRenderbuffer(GL_RENDERBUFFER, rb.get());
- glRenderbufferStorage(GL_RENDERBUFFER, GL_STENCIL_INDEX8, width, height);
+ FillTexture2D(tex2.get(), width, height, 0x40, 0, GL_DEPTH_STENCIL, GL_DEPTH_STENCIL,
+ GL_UNSIGNED_INT_24_8);
ASSERT_GL_NO_ERROR();
GLFramebuffer fbo;
@@ -3252,7 +3252,7 @@
// The same image is used as depth buffer during rendering.
glEnable(GL_DEPTH_TEST);
drawQuad(program.get(), "aPosition", 0.5f, 1.0f, true);
- EXPECT_GL_ERROR(GL_INVALID_OPERATION);
+ EXPECT_GL_ERROR(GL_INVALID_OPERATION) << "Same image as depth buffer should fail";
// The same image is used as depth buffer. But depth mask is false.
glDepthMask(GL_FALSE);
@@ -3266,9 +3266,9 @@
EXPECT_GL_NO_ERROR();
// Test rendering and sampling feedback loop for stencil buffer
- glBindTexture(GL_RENDERBUFFER, rb.get());
+ glBindTexture(GL_TEXTURE_2D, tex2.get());
glFramebufferTexture2D(GL_FRAMEBUFFER, GL_DEPTH_ATTACHMENT, GL_TEXTURE_2D, 0, 0);
- glFramebufferRenderbuffer(GL_FRAMEBUFFER, GL_STENCIL_ATTACHMENT, GL_RENDERBUFFER, rb.get());
+ glFramebufferTexture2D(GL_FRAMEBUFFER, GL_STENCIL_ATTACHMENT, GL_TEXTURE_2D, tex2.get(), 0);
ASSERT_GLENUM_EQ(GL_FRAMEBUFFER_COMPLETE, glCheckFramebufferStatus(GL_FRAMEBUFFER));
constexpr GLint stencilClearValue = 0x40;
glClearBufferiv(GL_STENCIL, 0, &stencilClearValue);
@@ -3276,7 +3276,7 @@
// The same image is used as stencil buffer during rendering.
glEnable(GL_STENCIL_TEST);
drawQuad(program.get(), "aPosition", 0.5f, 1.0f, true);
- EXPECT_GL_ERROR(GL_INVALID_OPERATION);
+ EXPECT_GL_ERROR(GL_INVALID_OPERATION) << "Same image as stencil buffer should fail";
// The same image is used as stencil buffer. But stencil mask is zero.
glStencilMask(0x0);