blob: 281df311eb95cff708fc7934a98db31f691c53fb [file] [log] [blame]
//
// Copyright 2017 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.
//
// EGLProgramCacheControlTest:
// Unit tests for the EGL_ANGLE_program_cache_control extension.
#include "common/angleutils.h"
#include "test_utils/ANGLETest.h"
#include "test_utils/gl_raii.h"
#include "util/EGLWindow.h"
using namespace angle;
constexpr EGLint kEnabledCacheSize = 0x10000;
constexpr char kEGLExtName[] = "EGL_ANGLE_program_cache_control";
void TestCacheProgram(PlatformMethods *platform,
const ProgramKeyType &key,
size_t programSize,
const uint8_t *programBytes);
class EGLProgramCacheControlTest : public ANGLETest
{
public:
void onCache(const ProgramKeyType &key, size_t programSize, const uint8_t *programBytes)
{
mCachedKey = key;
mCachedBinary.assign(&programBytes[0], &programBytes[programSize]);
}
protected:
EGLProgramCacheControlTest()
{
// Test flakiness was noticed when reusing displays.
forceNewDisplay();
setDeferContextInit(true);
setContextProgramCacheEnabled(true);
gDefaultPlatformMethods.cacheProgram = TestCacheProgram;
}
void testSetUp() override
{
if (extensionAvailable())
{
EGLDisplay display = getEGLWindow()->getDisplay();
eglProgramCacheResizeANGLE(display, kEnabledCacheSize, EGL_PROGRAM_CACHE_RESIZE_ANGLE);
ASSERT_EGL_SUCCESS();
}
ASSERT_TRUE(getEGLWindow()->initializeContext());
}
void testTearDown() override { gDefaultPlatformMethods.cacheProgram = DefaultCacheProgram; }
bool extensionAvailable()
{
EGLDisplay display = getEGLWindow()->getDisplay();
return IsEGLDisplayExtensionEnabled(display, kEGLExtName);
}
bool programBinaryAvailable()
{
return (getClientMajorVersion() >= 3 || IsGLExtensionEnabled("GL_OES_get_program_binary"));
}
ProgramKeyType mCachedKey;
std::vector<uint8_t> mCachedBinary;
};
void TestCacheProgram(PlatformMethods *platform,
const ProgramKeyType &key,
size_t programSize,
const uint8_t *programBytes)
{
auto *testPlatformContext = static_cast<TestPlatformContext *>(platform->context);
auto *testCase =
reinterpret_cast<EGLProgramCacheControlTest *>(testPlatformContext->currentTest);
testCase->onCache(key, programSize, programBytes);
}
// Tests error conditions of the APIs.
TEST_P(EGLProgramCacheControlTest, NegativeAPI)
{
ANGLE_SKIP_TEST_IF(!extensionAvailable());
constexpr char kDefaultKey[] = "defaultMakeItLongEnough";
constexpr char kDefaultBinary[] = "defaultMakeItLongEnough";
constexpr EGLint kDefaultKeySize = static_cast<EGLint>(ArraySize(kDefaultKey));
constexpr EGLint kDefaultBinarySize = static_cast<EGLint>(ArraySize(kDefaultBinary));
// Test that passing an invalid display to the entry point methods fails.
eglProgramCacheGetAttribANGLE(EGL_NO_DISPLAY, EGL_PROGRAM_CACHE_KEY_LENGTH_ANGLE);
EXPECT_EGL_ERROR(EGL_BAD_DISPLAY);
eglProgramCachePopulateANGLE(EGL_NO_DISPLAY, kDefaultKey, kDefaultKeySize, kDefaultBinary,
kDefaultBinarySize);
EXPECT_EGL_ERROR(EGL_BAD_DISPLAY);
EGLint tempKeySize = 0;
EGLint tempBinarySize = 0;
eglProgramCacheQueryANGLE(EGL_NO_DISPLAY, 0, nullptr, &tempKeySize, nullptr, &tempBinarySize);
EXPECT_EGL_ERROR(EGL_BAD_DISPLAY);
eglProgramCacheResizeANGLE(EGL_NO_DISPLAY, 0, EGL_PROGRAM_CACHE_TRIM_ANGLE);
EXPECT_EGL_ERROR(EGL_BAD_DISPLAY);
// Test querying properties with bad parameters.
EGLDisplay display = getEGLWindow()->getDisplay();
eglProgramCacheGetAttribANGLE(display, EGL_PROGRAM_CACHE_RESIZE_ANGLE);
EXPECT_EGL_ERROR(EGL_BAD_PARAMETER);
// Test populating with invalid parameters.
EGLint keySize = eglProgramCacheGetAttribANGLE(display, EGL_PROGRAM_CACHE_KEY_LENGTH_ANGLE);
EXPECT_GT(kDefaultKeySize, keySize);
eglProgramCachePopulateANGLE(display, kDefaultKey, keySize + 1, kDefaultBinary,
kDefaultBinarySize);
EXPECT_EGL_ERROR(EGL_BAD_PARAMETER);
eglProgramCachePopulateANGLE(display, kDefaultKey, keySize, kDefaultBinary, -1);
EXPECT_EGL_ERROR(EGL_BAD_PARAMETER);
eglProgramCachePopulateANGLE(display, nullptr, keySize, kDefaultBinary, kDefaultBinarySize);
EXPECT_EGL_ERROR(EGL_BAD_PARAMETER);
eglProgramCachePopulateANGLE(display, kDefaultKey, keySize, nullptr, kDefaultBinarySize);
EXPECT_EGL_ERROR(EGL_BAD_PARAMETER);
// Test querying cache entries with invalid parameters.
eglProgramCachePopulateANGLE(display, kDefaultKey, keySize, kDefaultBinary, kDefaultBinarySize);
ASSERT_EGL_SUCCESS();
EGLint cacheSize = eglProgramCacheGetAttribANGLE(display, EGL_PROGRAM_CACHE_SIZE_ANGLE);
ASSERT_EQ(1, cacheSize);
eglProgramCacheQueryANGLE(display, -1, nullptr, &tempKeySize, nullptr, &tempBinarySize);
EXPECT_EGL_ERROR(EGL_BAD_PARAMETER);
eglProgramCacheQueryANGLE(display, 1, nullptr, &tempKeySize, nullptr, &tempBinarySize);
EXPECT_EGL_ERROR(EGL_BAD_PARAMETER);
eglProgramCacheQueryANGLE(display, 0, nullptr, nullptr, nullptr, &tempBinarySize);
EXPECT_EGL_ERROR(EGL_BAD_PARAMETER);
eglProgramCacheQueryANGLE(display, 0, nullptr, &tempKeySize, nullptr, nullptr);
EXPECT_EGL_ERROR(EGL_BAD_PARAMETER);
eglProgramCacheQueryANGLE(display, 0, nullptr, &tempKeySize, nullptr, &tempBinarySize);
ASSERT_EGL_SUCCESS();
ASSERT_EQ(keySize, tempKeySize);
ASSERT_EQ(kDefaultBinarySize, tempBinarySize);
std::vector<uint8_t> tempKey(tempKeySize + 5);
std::vector<uint8_t> tempBinary(tempBinarySize + 5);
tempKeySize--;
eglProgramCacheQueryANGLE(display, 0, tempKey.data(), &tempKeySize, tempBinary.data(),
&tempBinarySize);
EXPECT_EGL_ERROR(EGL_BAD_PARAMETER);
tempKeySize++;
tempBinarySize--;
eglProgramCacheQueryANGLE(display, 0, tempKey.data(), &tempKeySize, tempBinary.data(),
&tempBinarySize);
EXPECT_EGL_ERROR(EGL_BAD_ACCESS);
// Test resizing with invalid parameters.
eglProgramCacheResizeANGLE(display, -1, EGL_PROGRAM_CACHE_TRIM_ANGLE);
EXPECT_EGL_ERROR(EGL_BAD_PARAMETER);
eglProgramCacheResizeANGLE(display, 0, EGL_PROGRAM_CACHE_KEY_LENGTH_ANGLE);
EXPECT_EGL_ERROR(EGL_BAD_PARAMETER);
}
// Tests a basic use case.
TEST_P(EGLProgramCacheControlTest, SaveAndReload)
{
ANGLE_SKIP_TEST_IF(!extensionAvailable() || !programBinaryAvailable());
constexpr char kVS[] = "attribute vec4 position; void main() { gl_Position = position; }";
constexpr char kFS[] = "void main() { gl_FragColor = vec4(1, 0, 0, 1); }";
// Link a program, which will miss the cache.
{
glClearColor(0.0f, 1.0f, 0.0f, 1.0f);
glClear(GL_COLOR_BUFFER_BIT);
EXPECT_PIXEL_COLOR_EQ(0, 0, GLColor::green);
ANGLE_GL_PROGRAM(program, kVS, kFS);
drawQuad(program, "position", 0.5f);
EXPECT_GL_NO_ERROR();
EXPECT_PIXEL_COLOR_EQ(0, 0, GLColor::red);
}
EGLDisplay display = getEGLWindow()->getDisplay();
EGLint cacheSize = eglProgramCacheGetAttribANGLE(display, EGL_PROGRAM_CACHE_SIZE_ANGLE);
EXPECT_EQ(1, cacheSize);
EGLint keySize = 0;
EGLint binarySize = 0;
eglProgramCacheQueryANGLE(display, 0, nullptr, &keySize, nullptr, &binarySize);
EXPECT_EQ(static_cast<EGLint>(mCachedKey.size()), keySize);
ASSERT_EGL_SUCCESS();
ProgramKeyType keyBuffer;
std::vector<uint8_t> binaryBuffer(binarySize);
eglProgramCacheQueryANGLE(display, 0, keyBuffer.data(), &keySize, binaryBuffer.data(),
&binarySize);
ASSERT_EGL_SUCCESS();
EXPECT_EQ(mCachedKey, keyBuffer);
EXPECT_EQ(mCachedBinary, binaryBuffer);
// Restart EGL and GL.
recreateTestFixture();
// Warm up the cache.
EGLint newCacheSize = eglProgramCacheGetAttribANGLE(display, EGL_PROGRAM_CACHE_SIZE_ANGLE);
EXPECT_EQ(0, newCacheSize);
eglProgramCachePopulateANGLE(display, keyBuffer.data(), keySize, binaryBuffer.data(),
binarySize);
mCachedBinary.clear();
// Link a program, which will hit the cache.
{
glClearColor(0.0f, 1.0f, 0.0f, 1.0f);
glClear(GL_COLOR_BUFFER_BIT);
EXPECT_PIXEL_COLOR_EQ(0, 0, GLColor::green);
ANGLE_GL_PROGRAM(program, kVS, kFS);
drawQuad(program, "position", 0.5f);
EXPECT_GL_NO_ERROR();
EXPECT_PIXEL_COLOR_EQ(0, 0, GLColor::red);
}
// Verify no new shader was compiled.
EXPECT_TRUE(mCachedBinary.empty());
}
// Tests that trying to link a program without correct shaders doesn't buggily call the cache.
TEST_P(EGLProgramCacheControlTest, LinkProgramWithBadShaders)
{
ANGLE_SKIP_TEST_IF(!extensionAvailable());
GLuint shader = glCreateShader(GL_FRAGMENT_SHADER);
GLuint program = glCreateProgram();
glAttachShader(program, shader);
glLinkProgram(program);
GLint linkStatus = 0;
glGetProgramiv(program, GL_LINK_STATUS, &linkStatus);
EXPECT_GL_FALSE(linkStatus);
EXPECT_GL_NO_ERROR();
glDeleteShader(shader);
glDeleteProgram(program);
}
ANGLE_INSTANTIATE_TEST(EGLProgramCacheControlTest,
ES2_D3D9(),
ES2_D3D11(),
ES2_OPENGL(),
ES2_VULKAN());