Commit 57ff6f95 by Jamie Madill Committed by Commit Bot

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/1171510Reviewed-by: 's avatarShahbaz Youssefi <syoussefi@chromium.org> Reviewed-by: 's avatarJamie Madill <jmadill@chromium.org> Commit-Queue: Jamie Madill <jmadill@chromium.org>
parent bb2f2c43
......@@ -972,11 +972,6 @@ Buffer *Context::getBuffer(GLuint handle) const
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);
......
......@@ -474,7 +474,8 @@ class Context final : public egl::LabeledObject, angle::NonCopyable, public angl
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;
......
......@@ -242,12 +242,6 @@ GLuint TextureManager::createTexture()
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)
......
......@@ -166,7 +166,11 @@ class TextureManager : public TypedResourceManager<Texture, HandleAllocator, Tex
{
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;
......
......@@ -23,7 +23,16 @@ class ResourceMap final : angle::NonCopyable
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 @@ class ResourceMap final : angle::NonCopyable
// 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 @@ class ResourceMap final : angle::NonCopyable
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 @@ ResourceMap<ResourceType>::~ResourceMap()
}
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 @@ bool ResourceMap<ResourceType>::contains(GLuint handle) const
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 @@ void ResourceMap<ResourceType>::assign(GLuint handle, ResourceType *resource)
{
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 @@ typename ResourceMap<ResourceType>::Iterator ResourceMap<ResourceType>::begin()
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 @@ bool ResourceMap<ResourceType>::empty() const
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 @@ bool ResourceMap<ResourceType>::Iterator::operator!=(const Iterator &other) cons
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 @@ const typename ResourceMap<ResourceType>::IndexAndResource
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];
......
//
// 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
......@@ -1033,12 +1033,6 @@ Texture *State::getTargetTexture(TextureType type) const
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());
......
......@@ -162,10 +162,7 @@ class State : angle::NonCopyable
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 @@ class State : angle::NonCopyable
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);
......
......@@ -979,7 +979,7 @@ angle::Result Renderer9::setTexture(const gl::Context *context,
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;
......
......@@ -2981,20 +2981,6 @@ bool ValidateFlushMappedBufferRangeEXT(Context *context,
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 @@ bool ValidateBindTexture(Context *context, TextureType target, GLuint texture)
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;
}
......
......@@ -25,6 +25,7 @@ angle_unittests_sources = [
"../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",
......
......@@ -2774,7 +2774,18 @@ class TextureLimitsTest : public ANGLETest
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 @@ class TextureLimitsTest : public ANGLETest
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,
......
......@@ -3232,12 +3232,12 @@ TEST_P(WebGL2CompatibilityTest, RenderingFeedbackLoopWithDepthStencil)
// 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 @@ TEST_P(WebGL2CompatibilityTest, RenderingFeedbackLoopWithDepthStencil)
// 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 @@ TEST_P(WebGL2CompatibilityTest, RenderingFeedbackLoopWithDepthStencil)
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 @@ TEST_P(WebGL2CompatibilityTest, RenderingFeedbackLoopWithDepthStencil)
// 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);
......
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