diff --git a/src/igl/opengl/IContext.h b/src/igl/opengl/IContext.h index 86eaf3365b..fd79c5bb80 100644 --- a/src/igl/opengl/IContext.h +++ b/src/igl/opengl/IContext.h @@ -47,6 +47,8 @@ class IContext { virtual bool isCurrentSharegroup() const = 0; virtual void present(std::shared_ptr surface) const = 0; + virtual std::unique_ptr createShareContext(Result* outResult) = 0; + IContext(); virtual ~IContext(); diff --git a/src/igl/opengl/egl/Context.cpp b/src/igl/opengl/egl/Context.cpp index 87a9b11566..ecbd3cc202 100644 --- a/src/igl/opengl/egl/Context.cpp +++ b/src/igl/opengl/egl/Context.cpp @@ -223,6 +223,12 @@ Context::Context(RenderingAPI /*api*/, initialize(); } +std::unique_ptr 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, diff --git a/src/igl/opengl/egl/Context.h b/src/igl/opengl/egl/Context.h index aba20d9fe8..a4dc441f35 100644 --- a/src/igl/opengl/egl/Context.h +++ b/src/igl/opengl/egl/Context.h @@ -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 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); diff --git a/src/igl/opengl/empty/Context.cpp b/src/igl/opengl/empty/Context.cpp index 15df4bf0bf..437a01cb8c 100644 --- a/src/igl/opengl/empty/Context.cpp +++ b/src/igl/opengl/empty/Context.cpp @@ -38,6 +38,12 @@ void Context::present(std::shared_ptr surface) const { // Intentionally does nothing. } +std::unique_ptr Context::createShareContext(Result* outResult) { + IGL_ASSERT_NOT_IMPLEMENTED(); + Result::setResult(outResult, Result::Code::Unimplemented, "Implement as needed"); + return nullptr; +} + ///-------------------------------------- /// MARK: - GL APIs Overrides diff --git a/src/igl/opengl/empty/Context.h b/src/igl/opengl/empty/Context.h index 05ba6b9eba..8c21317f5e 100644 --- a/src/igl/opengl/empty/Context.h +++ b/src/igl/opengl/empty/Context.h @@ -23,6 +23,9 @@ class Context final : public IContext { bool isCurrentSharegroup() const override; void present(std::shared_ptr surface) const override; + /// Creates a shared context, matching format based on the current context. + std::unique_ptr createShareContext(Result* outResult) override; + ///-------------------------------------- /// MARK: - GL APIs Overrides diff --git a/src/igl/opengl/glx/Context.cpp b/src/igl/opengl/glx/Context.cpp index df272c993c..68ea4fad60 100644 --- a/src/igl/opengl/glx/Context.cpp +++ b/src/igl/opengl/glx/Context.cpp @@ -267,6 +267,12 @@ void Context::present(std::shared_ptr surface) const { module_->glXMakeCurrent(display_, windowHandle_, contextHandle_); } +std::unique_ptr Context::createShareContext(Result* outResult) { + IGL_ASSERT_NOT_IMPLEMENTED(); + Result::setResult(outResult, Result::Code::Unimplemented, "Implement as needed"); + return nullptr; +} + std::shared_ptr Context::getSharedModule() const { return module_; } diff --git a/src/igl/opengl/glx/Context.h b/src/igl/opengl/glx/Context.h index d057718690..bbd4dd644a 100644 --- a/src/igl/opengl/glx/Context.h +++ b/src/igl/opengl/glx/Context.h @@ -43,6 +43,9 @@ class Context : public IContext { bool isCurrentSharegroup() const override; void present(std::shared_ptr surface) const override; + /// Creates a shared context, matching format based on the current context. + std::unique_ptr createShareContext(Result* outResult) override; + std::shared_ptr getSharedModule() const; private: diff --git a/src/igl/opengl/ios/Context.h b/src/igl/opengl/ios/Context.h index aff59a4098..a3784d1a4b 100644 --- a/src/igl/opengl/ios/Context.h +++ b/src/igl/opengl/ios/Context.h @@ -33,6 +33,10 @@ class Context final : public IContext { bool isCurrentContext() const override; bool isCurrentSharegroup() const override; void present(std::shared_ptr surface) const override; + + /// Creates a shared context, matching format based on the current context. + std::unique_ptr createShareContext(Result* outResult) override; + CVOpenGLESTextureCacheRef getTextureCache(); private: diff --git a/src/igl/opengl/ios/Context.mm b/src/igl/opengl/ios/Context.mm index f854df0c7e..eb6e1915f4 100644 --- a/src/igl/opengl/ios/Context.mm +++ b/src/igl/opengl/ios/Context.mm @@ -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]; } } @@ -49,7 +50,7 @@ } } // namespace -Context::Context(RenderingAPI api) : context_(createEAGLContext(api)) { +Context::Context(RenderingAPI api) : context_(createEAGLContext(api, nil)) { if (context_ != nil) { IContext::registerContext(getOrGenerateContextUniqueID(context_), this); } @@ -57,7 +58,7 @@ 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 { @@ -121,6 +122,17 @@ return [EAGLContext currentContext].sharegroup == context_.sharegroup; } +std::unique_ptr 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(sharedContext); +} + CVOpenGLESTextureCacheRef Context::getTextureCache() { if (textureCache_ == nullptr) { CVOpenGLESTextureCacheCreate(kCFAllocatorDefault, nullptr, context_, nullptr, &textureCache_); diff --git a/src/igl/opengl/macos/Context.h b/src/igl/opengl/macos/Context.h index c2f89cc2b2..c1430488d3 100644 --- a/src/igl/opengl/macos/Context.h +++ b/src/igl/opengl/macos/Context.h @@ -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 createShareContext(Result* outResult) override; + /// Create a new context with new NSOpenGLContext. static std::unique_ptr createContext(igl::opengl::RenderingAPI api, Result* outResult); /// Create a new context with existing NSOpenGLContext. diff --git a/src/igl/opengl/macos/Context.mm b/src/igl/opengl/macos/Context.mm index b04acc325a..62e2c55390 100644 --- a/src/igl/opengl/macos/Context.mm +++ b/src/igl/opengl/macos/Context.mm @@ -48,6 +48,10 @@ ///-------------------------------------- /// MARK: - Context +std::unique_ptr Context::createShareContext(Result* outResult) { + return createShareContext(*this, outResult); +} + std::unique_ptr Context::createContext(igl::opengl::RenderingAPI api, Result* outResult) { return createContext(createOpenGLContext(api), {}, outResult); } diff --git a/src/igl/opengl/webgl/Context.cpp b/src/igl/opengl/webgl/Context.cpp index 55dc40db50..a53a5f5bd9 100644 --- a/src/igl/opengl/webgl/Context.cpp +++ b/src/igl/opengl/webgl/Context.cpp @@ -68,6 +68,12 @@ bool Context::isCurrentSharegroup() const { return true; } +std::unique_ptr 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) { diff --git a/src/igl/opengl/webgl/Context.h b/src/igl/opengl/webgl/Context.h index d3fac5b19c..f1ed40bff4 100644 --- a/src/igl/opengl/webgl/Context.h +++ b/src/igl/opengl/webgl/Context.h @@ -33,6 +33,9 @@ class Context final : public ::igl::opengl::IContext { bool isCurrentSharegroup() const override; void present(std::shared_ptr surface) const override; + /// Creates a shared context, matching format based on the current context. + std::unique_ptr createShareContext(Result* outResult) override; + void setCanvasBufferSize(int width, int height); EMSCRIPTEN_WEBGL_CONTEXT_HANDLE getWebGLContext() const { diff --git a/src/igl/opengl/wgl/Context.cpp b/src/igl/opengl/wgl/Context.cpp index 40c79ac7ae..4021ea47cb 100644 --- a/src/igl/opengl/wgl/Context.cpp +++ b/src/igl/opengl/wgl/Context.cpp @@ -194,6 +194,12 @@ void Context::present(std::shared_ptr surface) const { wglMakeCurrent(deviceContext_, renderContext_); } +std::unique_ptr 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 diff --git a/src/igl/opengl/wgl/Context.h b/src/igl/opengl/wgl/Context.h index 279a53a957..3e37c5bbc5 100644 --- a/src/igl/opengl/wgl/Context.h +++ b/src/igl/opengl/wgl/Context.h @@ -37,6 +37,10 @@ class Context : public IContext { bool isCurrentContext() const override; bool isCurrentSharegroup() const override; void present(std::shared_ptr surface) const override; + + /// Creates a shared context, matching format based on the current context. + std::unique_ptr createShareContext(Result* outResult) override; + HDC getDeviceContext() const { return deviceContext_; } diff --git a/src/igl/tests/ogl/Context.cpp b/src/igl/tests/ogl/Context.cpp index 881157cf6d..7418dd3fb2 100644 --- a/src/igl/tests/ogl/Context.cpp +++ b/src/igl/tests/ogl/Context.cpp @@ -11,6 +11,7 @@ #include #include #include +#include #define DUMMY_FILE_NAME "dummy_file_name" #define DUMMY_LINE_NUM 0 @@ -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(*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(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