Skip to content

Commit

Permalink
Add IContext::createShareContext
Browse files Browse the repository at this point in the history
Summary:
State of "shared contexts" before this diff:
* The only way to create a shared context is via the platform specific headers in igl/opengl/<platform>/. If that's not enough friction, the macos header is ObjC++ and can't be used from C++ code.
* Of all platforms, only `egl` and `macos` provide API for creating shared contexts.

This new API allows us to create a shared context from an IContext instance. This new generic method might not allow full customization across all implementations since that sometimes requires platform specific objects/information, so the assumptions here are (1) the new context is part of the same sharegroup, and (2) it's as similar to the existing context as possible.

Note #1: this diff does not implement the new API for all platforms because I don't have the means to test them all. IMO, it's fine for people to have to implement it as needed, since they'll clearly be able to test it.

Note #2: don't be fooled by `IDevice::getSharedContext`, it has nothing to do with multiple contexts in the same sharegroup.

Differential Revision: D49297661

fbshipit-source-id: cfc39b10d9310df9a8e854ae31e852c1e895720d
  • Loading branch information
Thiago Goulart authored and facebook-github-bot committed Sep 19, 2023
1 parent f0a2d23 commit e87c2a5
Show file tree
Hide file tree
Showing 16 changed files with 110 additions and 6 deletions.
2 changes: 2 additions & 0 deletions src/igl/opengl/IContext.h
Original file line number Diff line number Diff line change
Expand Up @@ -47,6 +47,8 @@ class IContext {
virtual bool isCurrentSharegroup() const = 0;
virtual void present(std::shared_ptr<ITexture> surface) const = 0;

virtual std::unique_ptr<IContext> createShareContext(Result* outResult) = 0;

IContext();
virtual ~IContext();

Expand Down
6 changes: 6 additions & 0 deletions src/igl/opengl/egl/Context.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -223,6 +223,12 @@ Context::Context(RenderingAPI /*api*/,
initialize();
}

std::unique_ptr<IContext> Context::createShareContext(Result* outResult) {
IGL_ASSERT_NOT_IMPLEMENTED();
Result::setResult(outResult, Result::Code::Unimplemented, "Implement as needed");
return nullptr;
}

Context::Context(EGLDisplay display,
EGLContext context,
EGLSurface readSurface,
Expand Down
4 changes: 4 additions & 0 deletions src/igl/opengl/egl/Context.h
Original file line number Diff line number Diff line change
Expand Up @@ -30,6 +30,10 @@ class Context final : public IContext {
EGLSurface readSurface,
EGLSurface drawSurface,
Result* outResult);

/// Creates a shared context, matching format based on the current context.
std::unique_ptr<IContext> createShareContext(Result* outResult) override;

/// Create a new context for default display. This constructor makes the assumption that the EGL
/// surfaces to be associated with this context are already present and set to current.
Context(RenderingAPI api, EGLNativeWindowType window);
Expand Down
6 changes: 6 additions & 0 deletions src/igl/opengl/empty/Context.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -38,6 +38,12 @@ void Context::present(std::shared_ptr<ITexture> surface) const {
// Intentionally does nothing.
}

std::unique_ptr<IContext> Context::createShareContext(Result* outResult) {
IGL_ASSERT_NOT_IMPLEMENTED();
Result::setResult(outResult, Result::Code::Unimplemented, "Implement as needed");
return nullptr;
}

///--------------------------------------
/// MARK: - GL APIs Overrides

Expand Down
3 changes: 3 additions & 0 deletions src/igl/opengl/empty/Context.h
Original file line number Diff line number Diff line change
Expand Up @@ -23,6 +23,9 @@ class Context final : public IContext {
bool isCurrentSharegroup() const override;
void present(std::shared_ptr<ITexture> surface) const override;

/// Creates a shared context, matching format based on the current context.
std::unique_ptr<IContext> createShareContext(Result* outResult) override;

///--------------------------------------
/// MARK: - GL APIs Overrides

Expand Down
6 changes: 6 additions & 0 deletions src/igl/opengl/glx/Context.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -267,6 +267,12 @@ void Context::present(std::shared_ptr<ITexture> surface) const {
module_->glXMakeCurrent(display_, windowHandle_, contextHandle_);
}

std::unique_ptr<IContext> Context::createShareContext(Result* outResult) {
IGL_ASSERT_NOT_IMPLEMENTED();
Result::setResult(outResult, Result::Code::Unimplemented, "Implement as needed");
return nullptr;
}

std::shared_ptr<GLXSharedModule> Context::getSharedModule() const {
return module_;
}
Expand Down
3 changes: 3 additions & 0 deletions src/igl/opengl/glx/Context.h
Original file line number Diff line number Diff line change
Expand Up @@ -43,6 +43,9 @@ class Context : public IContext {
bool isCurrentSharegroup() const override;
void present(std::shared_ptr<ITexture> surface) const override;

/// Creates a shared context, matching format based on the current context.
std::unique_ptr<IContext> createShareContext(Result* outResult) override;

std::shared_ptr<GLXSharedModule> getSharedModule() const;

private:
Expand Down
4 changes: 4 additions & 0 deletions src/igl/opengl/ios/Context.h
Original file line number Diff line number Diff line change
Expand Up @@ -33,6 +33,10 @@ class Context final : public IContext {
bool isCurrentContext() const override;
bool isCurrentSharegroup() const override;
void present(std::shared_ptr<ITexture> surface) const override;

/// Creates a shared context, matching format based on the current context.
std::unique_ptr<IContext> createShareContext(Result* outResult) override;

CVOpenGLESTextureCacheRef getTextureCache();

private:
Expand Down
24 changes: 18 additions & 6 deletions src/igl/opengl/ios/Context.mm
Original file line number Diff line number Diff line change
Expand Up @@ -19,17 +19,18 @@
namespace opengl {
namespace ios {
namespace {
EAGLContext* createEAGLContext(RenderingAPI api) {
EAGLContext* createEAGLContext(RenderingAPI api, EAGLSharegroup* sharegroup) {
if (api == RenderingAPI::GLES3) {
EAGLContext* context = [[EAGLContext alloc] initWithAPI:kEAGLRenderingAPIOpenGLES3];
EAGLContext* context = [[EAGLContext alloc] initWithAPI:kEAGLRenderingAPIOpenGLES3
sharegroup:sharegroup];
if (context == nullptr) {
return [[EAGLContext alloc] initWithAPI:kEAGLRenderingAPIOpenGLES2];
return [[EAGLContext alloc] initWithAPI:kEAGLRenderingAPIOpenGLES2 sharegroup:sharegroup];
}
return context;
} else {
IGL_ASSERT_MSG(api == RenderingAPI::GLES2,
"IGL: unacceptable enum for rendering API for iOS\n");
return [[EAGLContext alloc] initWithAPI:kEAGLRenderingAPIOpenGLES2];
return [[EAGLContext alloc] initWithAPI:kEAGLRenderingAPIOpenGLES2 sharegroup:sharegroup];
}
}

Expand All @@ -49,15 +50,15 @@
}
} // namespace

Context::Context(RenderingAPI api) : context_(createEAGLContext(api)) {
Context::Context(RenderingAPI api) : context_(createEAGLContext(api, nil)) {
if (context_ != nil) {
IContext::registerContext(getOrGenerateContextUniqueID(context_), this);
}

initialize();
}

Context::Context(RenderingAPI api, Result* result) : context_(createEAGLContext(api)) {
Context::Context(RenderingAPI api, Result* result) : context_(createEAGLContext(api, nil)) {
if (context_ != nil) {
IContext::registerContext(getOrGenerateContextUniqueID(context_), this);
} else {
Expand Down Expand Up @@ -121,6 +122,17 @@
return [EAGLContext currentContext].sharegroup == context_.sharegroup;
}

std::unique_ptr<IContext> Context::createShareContext(Result* outResult) {
EAGLContext* sharedContext = [[EAGLContext alloc] initWithAPI:context_.API
sharegroup:context_.sharegroup];
if (!sharedContext) {
Result::setResult(outResult, Result::Code::RuntimeError, "Failed to create shared context");
return nullptr;
}
Result::setOk(outResult);
return std::make_unique<Context>(sharedContext);
}

CVOpenGLESTextureCacheRef Context::getTextureCache() {
if (textureCache_ == nullptr) {
CVOpenGLESTextureCacheCreate(kCFAllocatorDefault, nullptr, context_, nullptr, &textureCache_);
Expand Down
3 changes: 3 additions & 0 deletions src/igl/opengl/macos/Context.h
Original file line number Diff line number Diff line change
Expand Up @@ -22,6 +22,9 @@ namespace opengl::macos {

class Context final : public IContext {
public:
/// Creates a shared context, matching format based on the current context.
std::unique_ptr<IContext> createShareContext(Result* outResult) override;

/// Create a new context with new NSOpenGLContext.
static std::unique_ptr<Context> createContext(igl::opengl::RenderingAPI api, Result* outResult);
/// Create a new context with existing NSOpenGLContext.
Expand Down
4 changes: 4 additions & 0 deletions src/igl/opengl/macos/Context.mm
Original file line number Diff line number Diff line change
Expand Up @@ -48,6 +48,10 @@
///--------------------------------------
/// MARK: - Context

std::unique_ptr<IContext> Context::createShareContext(Result* outResult) {
return createShareContext(*this, outResult);
}

std::unique_ptr<Context> Context::createContext(igl::opengl::RenderingAPI api, Result* outResult) {
return createContext(createOpenGLContext(api), {}, outResult);
}
Expand Down
6 changes: 6 additions & 0 deletions src/igl/opengl/webgl/Context.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -68,6 +68,12 @@ bool Context::isCurrentSharegroup() const {
return true;
}

std::unique_ptr<IContext> Context::createShareContext(Result* outResult) {
IGL_ASSERT_NOT_IMPLEMENTED();
Result::setResult(outResult, Result::Code::Unimplemented, "Implement as needed");
return nullptr;
}

void Context::setCanvasBufferSize(int width, int height) {
auto result = emscripten_set_canvas_element_size(canvasName_.c_str(), width, height);
if (result != EMSCRIPTEN_RESULT_SUCCESS) {
Expand Down
3 changes: 3 additions & 0 deletions src/igl/opengl/webgl/Context.h
Original file line number Diff line number Diff line change
Expand Up @@ -33,6 +33,9 @@ class Context final : public ::igl::opengl::IContext {
bool isCurrentSharegroup() const override;
void present(std::shared_ptr<ITexture> surface) const override;

/// Creates a shared context, matching format based on the current context.
std::unique_ptr<IContext> createShareContext(Result* outResult) override;

void setCanvasBufferSize(int width, int height);

EMSCRIPTEN_WEBGL_CONTEXT_HANDLE getWebGLContext() const {
Expand Down
6 changes: 6 additions & 0 deletions src/igl/opengl/wgl/Context.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -194,6 +194,12 @@ void Context::present(std::shared_ptr<ITexture> surface) const {
wglMakeCurrent(deviceContext_, renderContext_);
}

std::unique_ptr<IContext> Context::createShareContext(Result* outResult) {
IGL_ASSERT_NOT_IMPLEMENTED();
Result::setResult(outResult, Result::Code::Unimplemented, "Implement as needed");
return nullptr;
}

} // namespace wgl
} // namespace opengl
} // namespace igl
4 changes: 4 additions & 0 deletions src/igl/opengl/wgl/Context.h
Original file line number Diff line number Diff line change
Expand Up @@ -37,6 +37,10 @@ class Context : public IContext {
bool isCurrentContext() const override;
bool isCurrentSharegroup() const override;
void present(std::shared_ptr<ITexture> surface) const override;

/// Creates a shared context, matching format based on the current context.
std::unique_ptr<IContext> createShareContext(Result* outResult) override;

HDC getDeviceContext() const {
return deviceContext_;
}
Expand Down
32 changes: 32 additions & 0 deletions src/igl/tests/ogl/Context.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -11,6 +11,7 @@
#include <igl/opengl/Device.h>
#include <igl/opengl/GLIncludes.h>
#include <igl/opengl/IContext.h>
#include <igl/opengl/Texture.h>

#define DUMMY_FILE_NAME "dummy_file_name"
#define DUMMY_LINE_NUM 0
Expand Down Expand Up @@ -310,5 +311,36 @@ TEST_F(ContextOGLTest, CheckForErrorsInvalidFrameBufferOperation) {
context_->deleteFramebuffers(1, &frameBuffer);
}

/// Verify that an object is visible across contexts in the same sharegroup
TEST_F(ContextOGLTest, BasicSharedContexts) {
// Setup is three contexts, (1) and (2) part of the same sharegroup and (3) not.
igl::Result result;
auto sharedContext = context_->createShareContext(&result);
ASSERT_TRUE(result.isOk());

auto unsharedDevice = util::createTestDevice();
ASSERT_TRUE(unsharedDevice != nullptr);
auto* unsharedContext = &static_cast<opengl::Device&>(*unsharedDevice).getContext();
ASSERT_TRUE(unsharedDevice != nullptr);

// Create texture from context (1)
context_->setCurrent();
const igl::TextureDesc textureDesc = igl::TextureDesc::new2D(
igl::TextureFormat::RGBA_UNorm8, 16, 16, igl::TextureDesc::TextureUsageBits::Sampled);
auto texture = device_->createTexture(textureDesc, &result);
ASSERT_TRUE(result.isOk());

const uint64_t glTextureId = static_cast<opengl::Texture*>(texture.get())->getId();
context_->flush(); // Required for texture to be visible from other contexts

// Confirm that texture is visible from context (2)
sharedContext->setCurrent();
ASSERT_TRUE(sharedContext->isTexture(glTextureId));

// Confirm that texture is not visible from context (3)
unsharedContext->setCurrent();
ASSERT_FALSE(unsharedContext->isTexture(glTextureId));
}

} // namespace tests
} // namespace igl

0 comments on commit e87c2a5

Please sign in to comment.