Commit c43be720 by Jamie Madill Committed by Commit Bot

Implement ANGLE_program_cache_control extensions.

This will give the browsers the ability to control the cache size, query and populate the contents, and trim cache contents on memory pressure. BUG=angleproject:1897 Change-Id: I6edaa7d307b890223db98792d5b074e4a7fdfaa4 Reviewed-on: https://chromium-review.googlesource.com/563606 Commit-Queue: Jamie Madill <jmadill@chromium.org> Reviewed-by: 's avatarCorentin Wallez <cwallez@chromium.org>
parent bc58515e
...@@ -14,15 +14,7 @@ ...@@ -14,15 +14,7 @@
namespace gl namespace gl
{ {
// The size to set for the program cache for default and low-end device cases. // The binary cache is currently left disable by default, and the application can enable it.
// Temporarily disabled to prevent double memory use in Chrome.
// TODO(jmadill): Re-enable once we have memory cache control.
//#if !defined(ANGLE_PLATFORM_ANDROID)
// const size_t kDefaultMaxProgramCacheMemoryBytes = 6 * 1024 * 1024;
//#else
// const size_t kDefaultMaxProgramCacheMemoryBytes = 2 * 1024 * 1024;
// const size_t kLowEndMaxProgramCacheMemoryBytes = 512 * 1024;
//#endif
const size_t kDefaultMaxProgramCacheMemoryBytes = 0; const size_t kDefaultMaxProgramCacheMemoryBytes = 0;
enum enum
......
...@@ -283,7 +283,8 @@ Context::Context(rx::EGLImplFactory *implFactory, ...@@ -283,7 +283,8 @@ Context::Context(rx::EGLImplFactory *implFactory,
initWorkarounds(); initWorkarounds();
mGLState.initialize(this, GetDebug(attribs), GetBindGeneratesResource(attribs), mGLState.initialize(this, GetDebug(attribs), GetBindGeneratesResource(attribs),
GetClientArraysEnabled(attribs), robustResourceInit); GetClientArraysEnabled(attribs), robustResourceInit,
mMemoryProgramCache != nullptr);
mFenceNVHandleAllocator.setBaseHandle(0); mFenceNVHandleAllocator.setBaseHandle(0);
...@@ -2673,6 +2674,9 @@ void Context::initCaps(const egl::DisplayExtensions &displayExtensions) ...@@ -2673,6 +2674,9 @@ void Context::initCaps(const egl::DisplayExtensions &displayExtensions)
mExtensions.robustResourceInitialization = mExtensions.robustResourceInitialization =
egl::Display::GetClientExtensions().displayRobustResourceInitialization; egl::Display::GetClientExtensions().displayRobustResourceInitialization;
// Enable the cache control query unconditionally.
mExtensions.programCacheControl = true;
// Apply implementation limits // Apply implementation limits
mCaps.maxVertexAttributes = std::min<GLuint>(mCaps.maxVertexAttributes, MAX_VERTEX_ATTRIBS); mCaps.maxVertexAttributes = std::min<GLuint>(mCaps.maxVertexAttributes, MAX_VERTEX_ATTRIBS);
mCaps.maxVertexAttribBindings = mCaps.maxVertexAttribBindings =
......
...@@ -434,6 +434,13 @@ bool ValidationContext::getQueryParameterInfo(GLenum pname, GLenum *type, unsign ...@@ -434,6 +434,13 @@ bool ValidationContext::getQueryParameterInfo(GLenum pname, GLenum *type, unsign
return true; return true;
} }
if (getExtensions().programCacheControl && pname == GL_PROGRAM_CACHE_ENABLED_ANGLE)
{
*type = GL_BOOL;
*numParams = 1;
return true;
}
// Check for ES3.0+ parameter names which are also exposed as ES2 extensions // Check for ES3.0+ parameter names which are also exposed as ES2 extensions
switch (pname) switch (pname)
{ {
......
...@@ -741,10 +741,22 @@ Error Display::createContext(const Config *configuration, ...@@ -741,10 +741,22 @@ Error Display::createContext(const Config *configuration,
shareTextures = mTextureManager; shareTextures = mTextureManager;
} }
// Memory cache temporarily disabled to prevent double memory use in Chrome. gl::MemoryProgramCache *cachePointer = &mMemoryProgramCache;
// TODO(jmadill): Re-enable once we have memory cache control.
// Check context creation attributes to see if we should enable the cache.
if (mAttributeMap.get(EGL_CONTEXT_PROGRAM_BINARY_CACHE_ENABLED_ANGLE, EGL_TRUE) == EGL_FALSE)
{
cachePointer = nullptr;
}
// A program cache size of zero indicates it should be disabled.
if (mMemoryProgramCache.maxSize() == 0)
{
cachePointer = nullptr;
}
gl::Context *context = gl::Context *context =
new gl::Context(mImplementation, configuration, shareContext, shareTextures, nullptr, new gl::Context(mImplementation, configuration, shareContext, shareTextures, cachePointer,
attribs, mDisplayExtensions, isRobustResourceInitEnabled()); attribs, mDisplayExtensions, isRobustResourceInitEnabled());
ASSERT(context != nullptr); ASSERT(context != nullptr);
...@@ -1014,6 +1026,9 @@ void Display::initDisplayExtensions() ...@@ -1014,6 +1026,9 @@ void Display::initDisplayExtensions()
// Force EGL_KHR_get_all_proc_addresses on. // Force EGL_KHR_get_all_proc_addresses on.
mDisplayExtensions.getAllProcAddresses = true; mDisplayExtensions.getAllProcAddresses = true;
// Enable program cache control since it is not back-end dependent.
mDisplayExtensions.programCacheControl = true;
mDisplayExtensionString = GenerateExtensionsString(mDisplayExtensions); mDisplayExtensionString = GenerateExtensionsString(mDisplayExtensions);
} }
...@@ -1110,4 +1125,100 @@ bool Display::isRobustResourceInitEnabled() const ...@@ -1110,4 +1125,100 @@ bool Display::isRobustResourceInitEnabled() const
EGL_TRUE); EGL_TRUE);
} }
EGLint Display::programCacheGetAttrib(EGLenum attrib) const
{
switch (attrib)
{
case EGL_PROGRAM_CACHE_KEY_LENGTH_ANGLE:
return static_cast<EGLint>(gl::kProgramHashLength);
case EGL_PROGRAM_CACHE_SIZE_ANGLE:
return static_cast<EGLint>(mMemoryProgramCache.entryCount());
default:
UNREACHABLE();
return 0;
}
}
Error Display::programCacheQuery(EGLint index,
void *key,
EGLint *keysize,
void *binary,
EGLint *binarysize)
{
ASSERT(index >= 0 && index < static_cast<EGLint>(mMemoryProgramCache.entryCount()));
const angle::MemoryBuffer *programBinary = nullptr;
gl::ProgramHash programHash;
// TODO(jmadill): Make this thread-safe.
bool result =
mMemoryProgramCache.getAt(static_cast<size_t>(index), &programHash, &programBinary);
if (!result)
{
return EglBadAccess() << "Program binary not accessible.";
}
ASSERT(keysize && binarysize);
if (key)
{
ASSERT(*keysize == static_cast<EGLint>(gl::kProgramHashLength));
memcpy(key, programHash.data(), gl::kProgramHashLength);
}
if (binary)
{
// Note: we check the size here instead of in the validation code, since we need to
// access the cache as atomically as possible. It's possible that the cache contents
// could change between the validation size check and the retrieval.
if (programBinary->size() > static_cast<size_t>(*binarysize))
{
return EglBadAccess() << "Program binary too large or changed during access.";
}
memcpy(binary, programBinary->data(), programBinary->size());
}
*binarysize = static_cast<EGLint>(programBinary->size());
*keysize = static_cast<EGLint>(gl::kProgramHashLength);
return NoError();
}
Error Display::programCachePopulate(const void *key,
EGLint keysize,
const void *binary,
EGLint binarysize)
{
ASSERT(keysize == static_cast<EGLint>(gl::kProgramHashLength));
gl::ProgramHash programHash;
memcpy(programHash.data(), key, gl::kProgramHashLength);
mMemoryProgramCache.putBinary(programHash, reinterpret_cast<const uint8_t *>(binary),
static_cast<size_t>(binarysize));
return NoError();
}
EGLint Display::programCacheResize(EGLint limit, EGLenum mode)
{
switch (mode)
{
case EGL_PROGRAM_CACHE_RESIZE_ANGLE:
{
size_t initialSize = mMemoryProgramCache.size();
mMemoryProgramCache.resize(static_cast<size_t>(limit));
return static_cast<EGLint>(initialSize);
}
case EGL_PROGRAM_CACHE_TRIM_ANGLE:
return static_cast<EGLint>(mMemoryProgramCache.trim(static_cast<size_t>(limit)));
default:
UNREACHABLE();
return 0;
}
}
} // namespace egl } // namespace egl
...@@ -48,6 +48,9 @@ struct DisplayState final : private angle::NonCopyable ...@@ -48,6 +48,9 @@ struct DisplayState final : private angle::NonCopyable
SurfaceSet surfaceSet; SurfaceSet surfaceSet;
}; };
// Constant coded here as a sanity limit.
constexpr EGLAttrib kProgramCacheSizeAbsoluteMax = 0x4000000;
class Display final : angle::NonCopyable class Display final : angle::NonCopyable
{ {
public: public:
...@@ -132,6 +135,18 @@ class Display final : angle::NonCopyable ...@@ -132,6 +135,18 @@ class Display final : angle::NonCopyable
const std::string &getExtensionString() const; const std::string &getExtensionString() const;
const std::string &getVendorString() const; const std::string &getVendorString() const;
EGLint programCacheGetAttrib(EGLenum attrib) const;
Error programCacheQuery(EGLint index,
void *key,
EGLint *keysize,
void *binary,
EGLint *binarysize);
Error programCachePopulate(const void *key,
EGLint keysize,
const void *binary,
EGLint binarysize);
EGLint programCacheResize(EGLint limit, EGLenum mode);
const AttributeMap &getAttributeMap() const { return mAttributeMap; } const AttributeMap &getAttributeMap() const { return mAttributeMap; }
EGLNativeDisplayType getNativeDisplayId() const { return mDisplayId; } EGLNativeDisplayType getNativeDisplayId() const { return mDisplayId; }
......
...@@ -547,6 +547,13 @@ bool MemoryProgramCache::get(const ProgramHash &programHash, const angle::Memory ...@@ -547,6 +547,13 @@ bool MemoryProgramCache::get(const ProgramHash &programHash, const angle::Memory
return mProgramBinaryCache.get(programHash, programOut); return mProgramBinaryCache.get(programHash, programOut);
} }
bool MemoryProgramCache::getAt(size_t index,
ProgramHash *hashOut,
const angle::MemoryBuffer **programOut)
{
return mProgramBinaryCache.getAt(index, hashOut, programOut);
}
void MemoryProgramCache::remove(const ProgramHash &programHash) void MemoryProgramCache::remove(const ProgramHash &programHash)
{ {
bool result = mProgramBinaryCache.eraseByKey(programHash); bool result = mProgramBinaryCache.eraseByKey(programHash);
...@@ -554,7 +561,6 @@ void MemoryProgramCache::remove(const ProgramHash &programHash) ...@@ -554,7 +561,6 @@ void MemoryProgramCache::remove(const ProgramHash &programHash)
} }
void MemoryProgramCache::put(const ProgramHash &program, void MemoryProgramCache::put(const ProgramHash &program,
const Context *context,
angle::MemoryBuffer &&binaryProgram) angle::MemoryBuffer &&binaryProgram)
{ {
const angle::MemoryBuffer *result = const angle::MemoryBuffer *result =
...@@ -576,11 +582,10 @@ void MemoryProgramCache::putProgram(const ProgramHash &programHash, ...@@ -576,11 +582,10 @@ void MemoryProgramCache::putProgram(const ProgramHash &programHash,
{ {
angle::MemoryBuffer binaryProgram; angle::MemoryBuffer binaryProgram;
Serialize(context, program, &binaryProgram); Serialize(context, program, &binaryProgram);
put(programHash, context, std::move(binaryProgram)); put(programHash, std::move(binaryProgram));
} }
void MemoryProgramCache::putBinary(const Context *context, void MemoryProgramCache::putBinary(const ProgramHash &programHash,
const Program *program,
const uint8_t *binary, const uint8_t *binary,
size_t length) size_t length)
{ {
...@@ -589,12 +594,13 @@ void MemoryProgramCache::putBinary(const Context *context, ...@@ -589,12 +594,13 @@ void MemoryProgramCache::putBinary(const Context *context,
binaryProgram.resize(length); binaryProgram.resize(length);
memcpy(binaryProgram.data(), binary, length); memcpy(binaryProgram.data(), binary, length);
// Compute the hash.
ProgramHash programHash;
ComputeHash(context, program, &programHash);
// Store the binary. // Store the binary.
put(programHash, context, std::move(binaryProgram)); const angle::MemoryBuffer *result =
mProgramBinaryCache.put(programHash, std::move(binaryProgram), binaryProgram.size());
if (!result)
{
ERR() << "Failed to store binary program in memory cache, program is too large.";
}
} }
void MemoryProgramCache::clear() void MemoryProgramCache::clear()
...@@ -603,4 +609,29 @@ void MemoryProgramCache::clear() ...@@ -603,4 +609,29 @@ void MemoryProgramCache::clear()
mIssuedWarnings = 0; mIssuedWarnings = 0;
} }
void MemoryProgramCache::resize(size_t maxCacheSizeBytes)
{
mProgramBinaryCache.resize(maxCacheSizeBytes);
}
size_t MemoryProgramCache::entryCount() const
{
return mProgramBinaryCache.entryCount();
}
size_t MemoryProgramCache::trim(size_t limit)
{
return mProgramBinaryCache.shrinkToSize(limit);
}
size_t MemoryProgramCache::size() const
{
return mProgramBinaryCache.size();
}
size_t MemoryProgramCache::maxSize() const
{
return mProgramBinaryCache.maxSize();
}
} // namespace gl } // namespace gl
...@@ -19,7 +19,8 @@ ...@@ -19,7 +19,8 @@
namespace gl namespace gl
{ {
// 160-bit SHA-1 hash key. // 160-bit SHA-1 hash key.
using ProgramHash = std::array<uint8_t, 20>; constexpr size_t kProgramHashLength = 20;
using ProgramHash = std::array<uint8_t, kProgramHashLength>;
} // namespace gl } // namespace gl
namespace std namespace std
...@@ -72,17 +73,17 @@ class MemoryProgramCache final : angle::NonCopyable ...@@ -72,17 +73,17 @@ class MemoryProgramCache final : angle::NonCopyable
// Check if the cache contains a binary matching the specified program. // Check if the cache contains a binary matching the specified program.
bool get(const ProgramHash &programHash, const angle::MemoryBuffer **programOut); bool get(const ProgramHash &programHash, const angle::MemoryBuffer **programOut);
// For querying the contents of the cache.
bool getAt(size_t index, ProgramHash *hashOut, const angle::MemoryBuffer **programOut);
// Evict a program from the binary cache. // Evict a program from the binary cache.
void remove(const ProgramHash &programHash); void remove(const ProgramHash &programHash);
// Helper method that serializes a program. // Helper method that serializes a program.
void putProgram(const ProgramHash &programHash, const Context *context, const Program *program); void putProgram(const ProgramHash &programHash, const Context *context, const Program *program);
// Helper method that copies a user binary. // Store a binary directly.
void putBinary(const Context *context, void putBinary(const ProgramHash &programHash, const uint8_t *binary, size_t length);
const Program *program,
const uint8_t *binary,
size_t length);
// Check the cache, and deserialize and load the program if found. Evict existing hash if load // Check the cache, and deserialize and load the program if found. Evict existing hash if load
// fails. // fails.
...@@ -94,10 +95,24 @@ class MemoryProgramCache final : angle::NonCopyable ...@@ -94,10 +95,24 @@ class MemoryProgramCache final : angle::NonCopyable
// Empty the cache. // Empty the cache.
void clear(); void clear();
// Resize the cache. Discards current contents.
void resize(size_t maxCacheSizeBytes);
// Returns the number of entries in the cache.
size_t entryCount() const;
// Reduces the current cache size and returns the number of bytes freed.
size_t trim(size_t limit);
// Returns the current cache size in bytes.
size_t size() const;
// Returns the maximum cache size in bytes.
size_t maxSize() const;
private: private:
// Insert or update a binary program. Program contents are transferred. // Insert or update a binary program. Program contents are transferred.
void put(const ProgramHash &programHash, void put(const ProgramHash &programHash,
const Context *context,
angle::MemoryBuffer &&binaryProgram); angle::MemoryBuffer &&binaryProgram);
angle::SizedMRUCache<ProgramHash, angle::MemoryBuffer> mProgramBinaryCache; angle::SizedMRUCache<ProgramHash, angle::MemoryBuffer> mProgramBinaryCache;
......
...@@ -39,13 +39,7 @@ class SizedMRUCache final : angle::NonCopyable ...@@ -39,13 +39,7 @@ class SizedMRUCache final : angle::NonCopyable
auto retVal = mStore.Put(key, ValueAndSize(std::move(value), size)); auto retVal = mStore.Put(key, ValueAndSize(std::move(value), size));
mCurrentSize += size; mCurrentSize += size;
while (mCurrentSize > mMaximumTotalSize) shrinkToSize(mMaximumTotalSize);
{
ASSERT(!mStore.empty());
auto iter = mStore.rbegin();
mCurrentSize -= iter->second.size;
mStore.Erase(iter);
}
return &retVal->second.value; return &retVal->second.value;
} }
...@@ -61,6 +55,20 @@ class SizedMRUCache final : angle::NonCopyable ...@@ -61,6 +55,20 @@ class SizedMRUCache final : angle::NonCopyable
return true; return true;
} }
bool getAt(size_t index, Key *keyOut, const Value **valueOut)
{
if (index < mStore.size())
{
auto it = mStore.begin();
std::advance(it, index);
*keyOut = it->first;
*valueOut = &it->second.value;
return true;
}
*valueOut = nullptr;
return false;
}
bool empty() const { return mStore.empty(); } bool empty() const { return mStore.empty(); }
void clear() void clear()
...@@ -83,8 +91,35 @@ class SizedMRUCache final : angle::NonCopyable ...@@ -83,8 +91,35 @@ class SizedMRUCache final : angle::NonCopyable
return false; return false;
} }
size_t entryCount() const { return mStore.size(); }
size_t size() const { return mCurrentSize; } size_t size() const { return mCurrentSize; }
// Also discards the cache contents.
void resize(size_t maximumTotalSize)
{
clear();
mMaximumTotalSize = maximumTotalSize;
}
// Reduce current memory usage.
size_t shrinkToSize(size_t limit)
{
size_t initialSize = mCurrentSize;
while (mCurrentSize > limit)
{
ASSERT(!mStore.empty());
auto iter = mStore.rbegin();
mCurrentSize -= iter->second.size;
mStore.Erase(iter);
}
return (initialSize - mCurrentSize);
}
size_t maxSize() const { return mMaximumTotalSize; }
private: private:
struct ValueAndSize struct ValueAndSize
{ {
......
...@@ -63,7 +63,8 @@ State::State() ...@@ -63,7 +63,8 @@ State::State()
mMultiSampling(false), mMultiSampling(false),
mSampleAlphaToOne(false), mSampleAlphaToOne(false),
mFramebufferSRGB(true), mFramebufferSRGB(true),
mRobustResourceInit(false) mRobustResourceInit(false),
mProgramBinaryCacheEnabled(false)
{ {
} }
...@@ -75,7 +76,8 @@ void State::initialize(const Context *context, ...@@ -75,7 +76,8 @@ void State::initialize(const Context *context,
bool debug, bool debug,
bool bindGeneratesResource, bool bindGeneratesResource,
bool clientArraysEnabled, bool clientArraysEnabled,
bool robustResourceInit) bool robustResourceInit,
bool programBinaryCacheEnabled)
{ {
const Caps &caps = context->getCaps(); const Caps &caps = context->getCaps();
const Extensions &extensions = context->getExtensions(); const Extensions &extensions = context->getExtensions();
...@@ -186,6 +188,7 @@ void State::initialize(const Context *context, ...@@ -186,6 +188,7 @@ void State::initialize(const Context *context,
mPathStencilMask = std::numeric_limits<GLuint>::max(); mPathStencilMask = std::numeric_limits<GLuint>::max();
mRobustResourceInit = robustResourceInit; mRobustResourceInit = robustResourceInit;
mProgramBinaryCacheEnabled = programBinaryCacheEnabled;
} }
void State::reset(const Context *context) void State::reset(const Context *context)
...@@ -687,7 +690,12 @@ bool State::getEnableFeature(GLenum feature) const ...@@ -687,7 +690,12 @@ bool State::getEnableFeature(GLenum feature) const
return getFramebufferSRGB(); return getFramebufferSRGB();
case GL_CONTEXT_ROBUST_RESOURCE_INITIALIZATION_ANGLE: case GL_CONTEXT_ROBUST_RESOURCE_INITIALIZATION_ANGLE:
return mRobustResourceInit; return mRobustResourceInit;
default: UNREACHABLE(); return false; case GL_PROGRAM_CACHE_ENABLED_ANGLE:
return mProgramBinaryCacheEnabled;
default:
UNREACHABLE();
return false;
} }
} }
...@@ -1666,6 +1674,10 @@ void State::getBooleanv(GLenum pname, GLboolean *params) ...@@ -1666,6 +1674,10 @@ void State::getBooleanv(GLenum pname, GLboolean *params)
case GL_CONTEXT_ROBUST_RESOURCE_INITIALIZATION_ANGLE: case GL_CONTEXT_ROBUST_RESOURCE_INITIALIZATION_ANGLE:
*params = mRobustResourceInit ? GL_TRUE : GL_FALSE; *params = mRobustResourceInit ? GL_TRUE : GL_FALSE;
break; break;
case GL_PROGRAM_CACHE_ENABLED_ANGLE:
*params = mProgramBinaryCacheEnabled ? GL_TRUE : GL_FALSE;
break;
default: default:
UNREACHABLE(); UNREACHABLE();
break; break;
......
...@@ -45,7 +45,8 @@ class State : angle::NonCopyable ...@@ -45,7 +45,8 @@ class State : angle::NonCopyable
bool debug, bool debug,
bool bindGeneratesResource, bool bindGeneratesResource,
bool clientArraysEnabled, bool clientArraysEnabled,
bool robustResourceInit); bool robustResourceInit,
bool programBinaryCacheEnabled);
void reset(const Context *context); void reset(const Context *context);
// State chunk getters // State chunk getters
...@@ -569,6 +570,9 @@ class State : angle::NonCopyable ...@@ -569,6 +570,9 @@ class State : angle::NonCopyable
// GL_ANGLE_robust_resource_intialization // GL_ANGLE_robust_resource_intialization
bool mRobustResourceInit; bool mRobustResourceInit;
// GL_ANGLE_program_cache_control
bool mProgramBinaryCacheEnabled;
DirtyBits mDirtyBits; DirtyBits mDirtyBits;
DirtyObjects mDirtyObjects; DirtyObjects mDirtyObjects;
}; };
......
...@@ -714,6 +714,20 @@ Error ValidateCreateContext(Display *display, Config *configuration, gl::Context ...@@ -714,6 +714,20 @@ Error ValidateCreateContext(Display *display, Config *configuration, gl::Context
} }
break; break;
case EGL_CONTEXT_PROGRAM_BINARY_CACHE_ENABLED_ANGLE:
if (!display->getExtensions().programCacheControl)
{
return EglBadAttribute()
<< "Attribute EGL_CONTEXT_PROGRAM_BINARY_CACHE_ENABLED_ANGLE "
"requires EGL_ANGLE_program_cache_control.";
}
if (value != EGL_TRUE && value != EGL_FALSE)
{
return EglBadAttribute() << "EGL_CONTEXT_PROGRAM_BINARY_CACHE_ENABLED_ANGLE must "
"be EGL_TRUE or EGL_FALSE.";
}
break;
default: default:
return EglBadAttribute() << "Unknown attribute."; return EglBadAttribute() << "Unknown attribute.";
} }
...@@ -2158,4 +2172,122 @@ Error ValidateGetPlatformDisplayEXT(EGLenum platform, ...@@ -2158,4 +2172,122 @@ Error ValidateGetPlatformDisplayEXT(EGLenum platform,
return ValidateGetPlatformDisplayCommon(platform, native_display, attribMap); return ValidateGetPlatformDisplayCommon(platform, native_display, attribMap);
} }
Error ValidateProgramCacheGetAttribANGLE(const Display *display, EGLenum attrib)
{
ANGLE_TRY(ValidateDisplay(display));
if (!display->getExtensions().programCacheControl)
{
return EglBadAccess() << "Extension not supported";
}
switch (attrib)
{
case EGL_PROGRAM_CACHE_KEY_LENGTH_ANGLE:
case EGL_PROGRAM_CACHE_SIZE_ANGLE:
break;
default:
return EglBadParameter() << "Invalid program cache attribute.";
}
return NoError();
}
Error ValidateProgramCacheQueryANGLE(const Display *display,
EGLint index,
void *key,
EGLint *keysize,
void *binary,
EGLint *binarysize)
{
ANGLE_TRY(ValidateDisplay(display));
if (!display->getExtensions().programCacheControl)
{
return EglBadAccess() << "Extension not supported";
}
if (index < 0 || index >= display->programCacheGetAttrib(EGL_PROGRAM_CACHE_SIZE_ANGLE))
{
return EglBadParameter() << "Program index out of range.";
}
if (keysize == nullptr || binarysize == nullptr)
{
return EglBadParameter() << "keysize and binarysize must always be valid pointers.";
}
if (binary && *keysize != static_cast<EGLint>(gl::kProgramHashLength))
{
return EglBadParameter() << "Invalid program key size.";
}
if ((key == nullptr) != (binary == nullptr))
{
return EglBadParameter() << "key and binary must both be null or both non-null.";
}
return NoError();
}
Error ValidateProgramCachePopulateANGLE(const Display *display,
const void *key,
EGLint keysize,
const void *binary,
EGLint binarysize)
{
ANGLE_TRY(ValidateDisplay(display));
if (!display->getExtensions().programCacheControl)
{
return EglBadAccess() << "Extension not supported";
}
if (keysize != static_cast<EGLint>(gl::kProgramHashLength))
{
return EglBadParameter() << "Invalid program key size.";
}
if (key == nullptr || binary == nullptr)
{
return EglBadParameter() << "null pointer in arguments.";
}
// Upper bound for binarysize is arbitrary.
if (binarysize <= 0 || binarysize > egl::kProgramCacheSizeAbsoluteMax)
{
return EglBadParameter() << "binarysize out of valid range.";
}
return NoError();
}
Error ValidateProgramCacheResizeANGLE(const Display *display, EGLint limit, EGLenum mode)
{
ANGLE_TRY(ValidateDisplay(display));
if (!display->getExtensions().programCacheControl)
{
return EglBadAccess() << "Extension not supported";
}
if (limit < 0)
{
return EglBadParameter() << "limit must be non-negative.";
}
switch (mode)
{
case EGL_PROGRAM_CACHE_RESIZE_ANGLE:
case EGL_PROGRAM_CACHE_TRIM_ANGLE:
break;
default:
return EglBadParameter() << "Invalid cache resize mode.";
}
return NoError();
}
} // namespace egl } // namespace egl
...@@ -129,6 +129,41 @@ Error ValidateGetPlatformDisplay(EGLenum platform, ...@@ -129,6 +129,41 @@ Error ValidateGetPlatformDisplay(EGLenum platform,
Error ValidateGetPlatformDisplayEXT(EGLenum platform, Error ValidateGetPlatformDisplayEXT(EGLenum platform,
void *native_display, void *native_display,
const EGLint *attrib_list); const EGLint *attrib_list);
Error ValidateProgramCacheGetAttribANGLE(const Display *display, EGLenum attrib);
Error ValidateProgramCacheQueryANGLE(const Display *display,
EGLint index,
void *key,
EGLint *keysize,
void *binary,
EGLint *binarysize);
Error ValidateProgramCachePopulateANGLE(const Display *display,
const void *key,
EGLint keysize,
const void *binary,
EGLint binarysize);
Error ValidateProgramCacheResizeANGLE(const Display *display, EGLint limit, EGLenum mode);
} // namespace egl } // namespace egl
#define ANGLE_EGL_TRY(THREAD, EXPR) \
{ \
auto ANGLE_LOCAL_VAR = (EXPR); \
if (ANGLE_LOCAL_VAR.isError()) \
return THREAD->setError(ANGLE_LOCAL_VAR); \
}
#define ANGLE_EGL_TRY_RETURN(THREAD, EXPR, RETVAL) \
{ \
auto ANGLE_LOCAL_VAR = (EXPR); \
if (ANGLE_LOCAL_VAR.isError()) \
{ \
THREAD->setError(ANGLE_LOCAL_VAR); \
return RETVAL; \
} \
}
#endif // LIBANGLE_VALIDATIONEGL_H_ #endif // LIBANGLE_VALIDATIONEGL_H_
...@@ -81,7 +81,7 @@ TEST(ValidationESTest, DISABLED_DrawElementsWithMaxIndexGivesError) ...@@ -81,7 +81,7 @@ TEST(ValidationESTest, DISABLED_DrawElementsWithMaxIndexGivesError)
caps.maxElementIndex = 100; caps.maxElementIndex = 100;
caps.maxDrawBuffers = 1; caps.maxDrawBuffers = 1;
caps.maxColorAttachments = 1; caps.maxColorAttachments = 1;
state.initialize(nullptr, false, true, true, false); state.initialize(nullptr, false, true, true, false, false);
NiceMock<MockTextureImpl> *textureImpl = new NiceMock<MockTextureImpl>(); NiceMock<MockTextureImpl> *textureImpl = new NiceMock<MockTextureImpl>();
EXPECT_CALL(mockFactory, createTexture(_)).WillOnce(Return(textureImpl)); EXPECT_CALL(mockFactory, createTexture(_)).WillOnce(Return(textureImpl));
......
...@@ -790,8 +790,12 @@ EGLint EGLAPIENTRY ProgramCacheGetAttribANGLE(EGLDisplay dpy, EGLenum attrib) ...@@ -790,8 +790,12 @@ EGLint EGLAPIENTRY ProgramCacheGetAttribANGLE(EGLDisplay dpy, EGLenum attrib)
{ {
EVENT("(EGLDisplay dpy = 0x%0.8p, EGLenum attrib = 0x%X)", dpy, attrib); EVENT("(EGLDisplay dpy = 0x%0.8p, EGLenum attrib = 0x%X)", dpy, attrib);
UNIMPLEMENTED(); Display *display = static_cast<Display *>(dpy);
return 0; Thread *thread = GetCurrentThread();
ANGLE_EGL_TRY_RETURN(thread, ValidateProgramCacheGetAttribANGLE(display, attrib), 0);
return display->programCacheGetAttrib(attrib);
} }
void EGLAPIENTRY ProgramCacheQueryANGLE(EGLDisplay dpy, void EGLAPIENTRY ProgramCacheQueryANGLE(EGLDisplay dpy,
...@@ -806,7 +810,13 @@ void EGLAPIENTRY ProgramCacheQueryANGLE(EGLDisplay dpy, ...@@ -806,7 +810,13 @@ void EGLAPIENTRY ProgramCacheQueryANGLE(EGLDisplay dpy,
"0x%0.8p, void *binary = 0x%0.8p, EGLint *size = 0x%0.8p)", "0x%0.8p, void *binary = 0x%0.8p, EGLint *size = 0x%0.8p)",
dpy, index, key, keysize, binary, binarysize); dpy, index, key, keysize, binary, binarysize);
UNIMPLEMENTED(); Display *display = static_cast<Display *>(dpy);
Thread *thread = GetCurrentThread();
ANGLE_EGL_TRY(thread,
ValidateProgramCacheQueryANGLE(display, index, key, keysize, binary, binarysize));
ANGLE_EGL_TRY(thread, display->programCacheQuery(index, key, keysize, binary, binarysize));
} }
void EGLAPIENTRY ProgramCachePopulateANGLE(EGLDisplay dpy, void EGLAPIENTRY ProgramCachePopulateANGLE(EGLDisplay dpy,
...@@ -820,14 +830,25 @@ void EGLAPIENTRY ProgramCachePopulateANGLE(EGLDisplay dpy, ...@@ -820,14 +830,25 @@ void EGLAPIENTRY ProgramCachePopulateANGLE(EGLDisplay dpy,
"0x%0.8p, EGLint *size = 0x%0.8p)", "0x%0.8p, EGLint *size = 0x%0.8p)",
dpy, key, keysize, binary, binarysize); dpy, key, keysize, binary, binarysize);
UNIMPLEMENTED(); Display *display = static_cast<Display *>(dpy);
Thread *thread = GetCurrentThread();
ANGLE_EGL_TRY(thread,
ValidateProgramCachePopulateANGLE(display, key, keysize, binary, binarysize));
ANGLE_EGL_TRY(thread, display->programCachePopulate(key, keysize, binary, binarysize));
} }
EGLint EGLAPIENTRY ProgramCacheResizeANGLE(EGLDisplay dpy, EGLint limit, EGLenum mode) EGLint EGLAPIENTRY ProgramCacheResizeANGLE(EGLDisplay dpy, EGLint limit, EGLenum mode)
{ {
EVENT("(EGLDisplay dpy = 0x%0.8p, EGLint limit = %d, EGLenum mode = 0x%X)", dpy, limit, mode); EVENT("(EGLDisplay dpy = 0x%0.8p, EGLint limit = %d, EGLenum mode = 0x%X)", dpy, limit, mode);
UNIMPLEMENTED();
return 0; Display *display = static_cast<Display *>(dpy);
Thread *thread = GetCurrentThread();
ANGLE_EGL_TRY_RETURN(thread, ValidateProgramCacheResizeANGLE(display, limit, mode), 0);
return display->programCacheResize(limit, mode);
} }
} // namespace egl } // namespace egl
...@@ -56,6 +56,11 @@ class EGLProgramCacheControlTest : public ANGLETest ...@@ -56,6 +56,11 @@ class EGLProgramCacheControlTest : public ANGLETest
return eglDisplayExtensionEnabled(display, kEGLExtName); return eglDisplayExtensionEnabled(display, kEGLExtName);
} }
bool programBinaryAvailable()
{
return (getClientMajorVersion() >= 3 || extensionEnabled("GL_OES_get_program_binary"));
}
ProgramKeyType mCachedKey; ProgramKeyType mCachedKey;
std::vector<uint8_t> mCachedBinary; std::vector<uint8_t> mCachedBinary;
}; };
...@@ -169,7 +174,7 @@ TEST_P(EGLProgramCacheControlTest, NegativeAPI) ...@@ -169,7 +174,7 @@ TEST_P(EGLProgramCacheControlTest, NegativeAPI)
// Tests a basic use case. // Tests a basic use case.
TEST_P(EGLProgramCacheControlTest, SaveAndReload) TEST_P(EGLProgramCacheControlTest, SaveAndReload)
{ {
ANGLE_SKIP_TEST_IF(!extensionAvailable()); ANGLE_SKIP_TEST_IF(!extensionAvailable() || !programBinaryAvailable());
const std::string vertexShader = const std::string vertexShader =
"attribute vec4 position; void main() { gl_Position = position; }"; "attribute vec4 position; void main() { gl_Position = position; }";
......
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