blob: 445e5e55a0bda60f66b360b202f83e1062b1ee1f [file] [log] [blame]
//
// Copyright 2021 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.
//
// Demonstrates GLES usage with two contexts, one uploading texture data.
#include "SampleApplication.h"
#include "util/random_utils.h"
#include "util/shader_utils.h"
#include "util/test_utils.h"
#include <array>
#include <mutex>
#include <queue>
#include <thread>
struct TextureAndFence
{
GLuint textureID;
GLsync fenceSync;
};
constexpr GLuint64 kTimeout = 10000000;
static constexpr GLint kTextureSize = 2;
constexpr size_t kWindowWidth = 400;
constexpr size_t kWindowHeight = 300;
void UpdateThreadLoop(EGLDisplay display,
EGLConfig config,
EGLContext shareContext,
std::mutex *updateThreadMutex,
std::queue<TextureAndFence> *updateThreadQueue,
std::mutex *mainThreadMutex,
std::queue<TextureAndFence> *mainThreadQueue)
{
angle::RNG rng;
EGLint contextAttribs[] = {EGL_CONTEXT_MAJOR_VERSION, 3, EGL_NONE};
EGLContext context = eglCreateContext(display, config, shareContext, contextAttribs);
EGLint surfaceAttribs[] = {EGL_NONE};
EGLSurface surface = eglCreatePbufferSurface(display, config, surfaceAttribs);
eglMakeCurrent(display, surface, surface, context);
for (;;)
{
bool hasUpdate = false;
TextureAndFence textureAndFence;
{
std::lock_guard<std::mutex> lock(*updateThreadMutex);
if (!updateThreadQueue->empty())
{
textureAndFence = updateThreadQueue->back();
hasUpdate = true;
updateThreadQueue->pop();
}
}
if (hasUpdate)
{
if (textureAndFence.textureID == 0)
{
// Signal from the main thread to stop execution.
break;
}
glClientWaitSync(textureAndFence.fenceSync, 0, kTimeout);
glDeleteSync(textureAndFence.fenceSync);
glBindTexture(GL_TEXTURE_2D, textureAndFence.textureID);
std::vector<uint8_t> bytes(3, 0);
FillVectorWithRandomUBytes(&rng, &bytes);
bytes.push_back(255);
std::vector<uint8_t> textureData;
for (GLint x = 0; x < kTextureSize; ++x)
{
for (GLint y = 0; y < kTextureSize; ++y)
{
textureData.insert(textureData.end(), bytes.begin(), bytes.end());
}
}
glTexSubImage2D(GL_TEXTURE_2D, 0, 0, 0, kTextureSize, kTextureSize, GL_RGBA,
GL_UNSIGNED_BYTE, textureData.data());
GLsync mainThreadSync = glFenceSync(GL_SYNC_GPU_COMMANDS_COMPLETE, 0);
{
std::lock_guard<std::mutex> lock(*mainThreadMutex);
mainThreadQueue->push({textureAndFence.textureID, mainThreadSync});
}
}
angle::Sleep(200);
}
eglMakeCurrent(display, EGL_NO_SURFACE, EGL_NO_SURFACE, EGL_NO_CONTEXT);
eglDestroySurface(display, surface);
eglDestroyContext(display, context);
}
class MultipleContextsSample : public SampleApplication
{
public:
MultipleContextsSample(int argc, char **argv)
: SampleApplication("MultipleContexts",
argc,
argv,
ClientType::ES3_0,
kWindowWidth,
kWindowHeight)
{}
bool initialize() override
{
// Initialize some textures and send them to the update thread.
glGenTextures(kNumTextures, mTextures.data());
for (GLuint texture : mTextures)
{
glBindTexture(GL_TEXTURE_2D, texture);
glTexImage2D(GL_TEXTURE_2D, 0, GL_RGBA, kTextureSize, kTextureSize, 0, GL_RGBA,
GL_UNSIGNED_BYTE, nullptr);
glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_NEAREST);
glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_NEAREST);
glBindTexture(GL_TEXTURE_2D, 0);
GLsync fenceSync = glFenceSync(GL_SYNC_GPU_COMMANDS_COMPLETE, 0);
mUpdateThreadQueue.push({texture, fenceSync});
}
mUpdateThread.reset(new std::thread(UpdateThreadLoop, getDisplay(), getConfig(),
getContext(), &mUpdateThreadMutex, &mUpdateThreadQueue,
&mMainThreadMutex, &mMainThreadQueue));
mProgram = CompileProgram(angle::essl1_shaders::vs::Texture2D(),
angle::essl1_shaders::fs::Texture2D());
glUseProgram(mProgram);
glEnableVertexAttribArray(0);
glGenBuffers(1, &mVertexBuffer);
glBindBuffer(GL_ARRAY_BUFFER, mVertexBuffer);
glBufferData(GL_ARRAY_BUFFER, sizeof(GLfloat) * 2 * 6, nullptr, GL_DYNAMIC_DRAW);
glVertexAttribPointer(0, 2, GL_FLOAT, GL_FALSE, 0, nullptr);
return true;
}
void destroy() override
{
{
// Signal the worker thread to stop execution.
std::lock_guard<std::mutex> lock(mUpdateThreadMutex);
mUpdateThreadQueue.push({0, 0});
}
for (;;)
{
{
std::lock_guard<std::mutex> mainLock(mMainThreadMutex);
if (mMainThreadQueue.empty())
{
std::lock_guard<std::mutex> updateLock(mUpdateThreadMutex);
if (mUpdateThreadQueue.empty())
{
break;
}
}
else
{
TextureAndFence textureAndFence = mMainThreadQueue.back();
mMainThreadQueue.pop();
glClientWaitSync(textureAndFence.fenceSync, 0, kTimeout);
glDeleteSync(textureAndFence.fenceSync);
}
}
}
glDeleteTextures(kNumTextures, mTextures.data());
glDeleteProgram(mProgram);
glDeleteBuffers(1, &mVertexBuffer);
}
void draw() override
{
bool hasUpdate = false;
TextureAndFence textureAndFence;
while (!hasUpdate)
{
{
std::lock_guard<std::mutex> lock(mMainThreadMutex);
if (!mMainThreadQueue.empty())
{
hasUpdate = true;
textureAndFence = mMainThreadQueue.back();
mMainThreadQueue.pop();
}
}
}
glClientWaitSync(textureAndFence.fenceSync, 0, kTimeout);
glDeleteSync(textureAndFence.fenceSync);
glBindTexture(GL_TEXTURE_2D, textureAndFence.textureID);
constexpr size_t kNumRows = 3;
constexpr size_t kNumCols = 4;
constexpr size_t kTileHeight = kWindowHeight / kNumRows;
constexpr size_t kTileWidth = kWindowWidth / kNumCols;
size_t tileX = mDrawCount % kNumCols;
size_t tileY = (mDrawCount / kNumCols) % kNumRows;
mDrawCount++;
GLfloat tileX0 = static_cast<float>(tileX) / static_cast<float>(kNumCols);
GLfloat tileY0 = static_cast<float>(tileY) / static_cast<float>(kNumRows);
GLfloat tileX1 = tileX0 + static_cast<float>(kTileWidth) / static_cast<float>(kWindowWidth);
GLfloat tileY1 =
tileY0 + static_cast<float>(kTileHeight) / static_cast<float>(kWindowHeight);
tileX0 = tileX0 * 2.0f - 1.0f;
tileX1 = tileX1 * 2.0f - 1.0f;
tileY0 = tileY0 * 2.0f - 1.0f;
tileY1 = tileY1 * 2.0f - 1.0f;
std::vector<GLfloat> vertices = {tileX0, tileY0, tileX0, tileY1, tileX1, tileY0,
tileX1, tileY0, tileX1, tileY1, tileX0, tileY1};
glBufferSubData(GL_ARRAY_BUFFER, 0, sizeof(vertices[0]) * vertices.size(), vertices.data());
glClear(GL_COLOR_BUFFER_BIT);
glDrawArrays(GL_TRIANGLES, 0, 6);
GLsync drawSync = glFenceSync(GL_SYNC_GPU_COMMANDS_COMPLETE, 0);
{
std::lock_guard<std::mutex> lock(mUpdateThreadMutex);
mUpdateThreadQueue.push({textureAndFence.textureID, drawSync});
}
}
private:
std::unique_ptr<std::thread> mUpdateThread;
static constexpr GLuint kNumTextures = 4;
std::array<GLuint, kNumTextures> mTextures;
GLuint mProgram = 0;
GLuint mVertexBuffer = 0;
std::mutex mUpdateThreadMutex;
std::queue<TextureAndFence> mUpdateThreadQueue;
std::mutex mMainThreadMutex;
std::queue<TextureAndFence> mMainThreadQueue;
size_t mDrawCount = 0;
};
int main(int argc, char **argv)
{
MultipleContextsSample app(argc, argv);
return app.run();
}