Accumulated uncommitted work: GHOST Web backend, WebGL/GPU web layer, web workflow engine and protocol, tooling, docs and task records. Large generated evidence, user-testing archives, build snapshots and imported test assets remain untracked. Co-Authored-By: Claude Code <noreply@anthropic.com>
100 lines
3.0 KiB
C++
100 lines
3.0 KiB
C++
/* SPDX-FileCopyrightText: 2026 Blender Authors
|
|
* SPDX-License-Identifier: GPL-2.0-or-later */
|
|
#include "GHOST_ContextWeb.hh"
|
|
#include <GLES2/gl2.h>
|
|
#include <emscripten.h>
|
|
|
|
GHOST_ContextWeb::GHOST_ContextWeb(const GHOST_ContextParams ¶ms, const char *selector)
|
|
: GHOST_Context(params), selector_(selector)
|
|
{
|
|
}
|
|
|
|
GHOST_ContextWeb::~GHOST_ContextWeb()
|
|
{
|
|
if (handle_ > 0) {
|
|
if (active_context_ == this) releaseDrawingContext();
|
|
emscripten_webgl_destroy_context(handle_);
|
|
}
|
|
EM_ASM({ delete GL.offscreenCanvases[UTF8ToString($0).slice(1)]; }, selector_.c_str());
|
|
}
|
|
|
|
GHOST_TSuccess GHOST_ContextWeb::initializeDrawingContext()
|
|
{
|
|
if (handle_ > 0) return activateDrawingContext();
|
|
EM_ASM({ GL.offscreenCanvases[UTF8ToString($0).slice(1)] = Module['canvas']; }, selector_.c_str());
|
|
EmscriptenWebGLContextAttributes attributes;
|
|
emscripten_webgl_init_context_attributes(&attributes);
|
|
attributes.alpha = EM_FALSE;
|
|
attributes.depth = EM_TRUE;
|
|
attributes.stencil = EM_TRUE;
|
|
attributes.antialias = EM_FALSE;
|
|
/* File-save thumbnails read the last native window contents without drawing
|
|
* again (drawing can mutate Main during save). Keep those pixels available
|
|
* after OffscreenCanvas presentation and the worker event-loop yield. */
|
|
attributes.preserveDrawingBuffer = EM_TRUE;
|
|
attributes.majorVersion = 2;
|
|
attributes.enableExtensionsByDefault = EM_TRUE;
|
|
/* OffscreenCanvas presents when the owning worker yields to its event loop. */
|
|
attributes.explicitSwapControl = EM_FALSE;
|
|
handle_ = emscripten_webgl_create_context(selector_.c_str(), &attributes);
|
|
return activateDrawingContext();
|
|
}
|
|
|
|
GHOST_TSuccess GHOST_ContextWeb::activateDrawingContext()
|
|
{
|
|
if (handle_ <= 0 || emscripten_is_webgl_context_lost(handle_) ||
|
|
emscripten_webgl_make_context_current(handle_) != EMSCRIPTEN_RESULT_SUCCESS)
|
|
{
|
|
return GHOST_kFailure;
|
|
}
|
|
active_context_ = this;
|
|
return GHOST_kSuccess;
|
|
}
|
|
|
|
bool GHOST_ContextWeb::isContextLost() const
|
|
{
|
|
return handle_ > 0 && emscripten_is_webgl_context_lost(handle_);
|
|
}
|
|
|
|
GHOST_TSuccess GHOST_ContextWeb::releaseDrawingContext()
|
|
{
|
|
if (active_context_ != this) return GHOST_kSuccess;
|
|
if (emscripten_webgl_make_context_current(0) != EMSCRIPTEN_RESULT_SUCCESS) return GHOST_kFailure;
|
|
active_context_ = nullptr;
|
|
return GHOST_kSuccess;
|
|
}
|
|
|
|
GHOST_TSuccess GHOST_ContextWeb::updateDrawingContext()
|
|
{
|
|
return activateDrawingContext();
|
|
}
|
|
|
|
GHOST_TSuccess GHOST_ContextWeb::releaseNativeHandles()
|
|
{
|
|
/* The canvas must remain alive until the context is destroyed. */
|
|
return GHOST_kFailure;
|
|
}
|
|
|
|
GHOST_TSuccess GHOST_ContextWeb::swapBufferAcquire()
|
|
{
|
|
return activateDrawingContext();
|
|
}
|
|
|
|
GHOST_TSuccess GHOST_ContextWeb::swapBufferRelease()
|
|
{
|
|
if (!activateDrawingContext()) return GHOST_kFailure;
|
|
glFlush();
|
|
return GHOST_kSuccess;
|
|
}
|
|
|
|
GHOST_TSuccess GHOST_ContextWeb::setSwapInterval(int interval)
|
|
{
|
|
return interval == 1 ? GHOST_kSuccess : GHOST_kFailure;
|
|
}
|
|
|
|
GHOST_TSuccess GHOST_ContextWeb::getSwapInterval(int &interval)
|
|
{
|
|
interval = 1;
|
|
return GHOST_kSuccess;
|
|
}
|