wg_engine: external device handles (web integration)

move instance, adapter and device creation from renderer to application
its necessary for web integration, because browser have its own mechanics to create hardware handles
this changes makes webgpu canvas more universal to use in case of system and web applications

issue: https://github.com/thorvg/thorvg/issues/2410
This commit is contained in:
Sergii Liebodkin 2024-09-06 07:08:58 +03:00 committed by GitHub
parent d807ac91ea
commit e2e4fc6964
No known key found for this signature in database
GPG key ID: B5690EEEBB952194
7 changed files with 69 additions and 54 deletions

View file

@ -1745,7 +1745,7 @@ public:
* @param[in] surface WGPUSurface, handle to a presentable surface. * @param[in] surface WGPUSurface, handle to a presentable surface.
* @param[in] w The width of the surface. * @param[in] w The width of the surface.
* @param[in] h The height of the surface. * @param[in] h The height of the surface.
* * @param[in] device WGPUDevice: desired handle for the WebGPU device. If it is nullptr, ThorVG will assign an appropriate device internally.
* @retval Result::InsufficientCondition if the canvas is performing rendering. Please ensure the canvas is synced. * @retval Result::InsufficientCondition if the canvas is performing rendering. Please ensure the canvas is synced.
* @retval Result::NonSupport In case the wg engine is not supported. * @retval Result::NonSupport In case the wg engine is not supported.
* *
@ -1754,7 +1754,7 @@ public:
* @see Canvas::viewport() * @see Canvas::viewport()
* @see Canvas::sync() * @see Canvas::sync()
*/ */
Result target(void* instance, void* surface, uint32_t w, uint32_t h) noexcept; Result target(void* instance, void* surface, uint32_t w, uint32_t h, void* device = nullptr) noexcept;
/** /**
* @brief Creates a new WgCanvas object. * @brief Creates a new WgCanvas object.

View file

@ -50,11 +50,14 @@ WgCanvas::WgCanvas() : Canvas(nullptr), pImpl(nullptr)
WgCanvas::~WgCanvas() WgCanvas::~WgCanvas()
{ {
delete pImpl; #ifdef THORVG_WG_RASTER_SUPPORT
auto renderer = static_cast<WgRenderer*>(Canvas::pImpl->renderer);
renderer->target(nullptr, 0, 0);
#endif
} }
Result WgCanvas::target(void* instance, void* surface, uint32_t w, uint32_t h) noexcept Result WgCanvas::target(void* instance, void* surface, uint32_t w, uint32_t h, void* device) noexcept
{ {
#ifdef THORVG_WG_RASTER_SUPPORT #ifdef THORVG_WG_RASTER_SUPPORT
if (Canvas::pImpl->status != Status::Damaged && Canvas::pImpl->status != Status::Synced) { if (Canvas::pImpl->status != Status::Damaged && Canvas::pImpl->status != Status::Synced) {
@ -67,7 +70,7 @@ Result WgCanvas::target(void* instance, void* surface, uint32_t w, uint32_t h) n
auto renderer = static_cast<WgRenderer*>(Canvas::pImpl->renderer); auto renderer = static_cast<WgRenderer*>(Canvas::pImpl->renderer);
if (!renderer) return Result::MemoryCorruption; if (!renderer) return Result::MemoryCorruption;
if (!renderer->target((WGPUInstance)instance, (WGPUSurface)surface, w, h)) return Result::Unknown; if (!renderer->target((WGPUInstance)instance, (WGPUSurface)surface, w, h, (WGPUDevice)device)) return Result::Unknown;
Canvas::pImpl->vport = {0, 0, (int32_t)w, (int32_t)h}; Canvas::pImpl->vport = {0, 0, (int32_t)w, (int32_t)h};
renderer->viewport(Canvas::pImpl->vport); renderer->viewport(Canvas::pImpl->vport);

View file

@ -25,43 +25,16 @@
#endif #endif
#include "tvgWgCommon.h" #include "tvgWgCommon.h"
#include "tvgArray.h" #include "tvgArray.h"
#include <iostream>
void WgContext::initialize(WGPUInstance instance, WGPUSurface surface) void WgContext::initialize(WGPUInstance instance, WGPUDevice device)
{ {
assert(instance); assert(instance);
assert(surface); assert(device);
// store global instance and surface // store global instance and surface
this->instance = instance; this->instance = instance;
this->surface = surface; this->device = device;
this->preferredFormat = WGPUTextureFormat_BGRA8Unorm;
// request adapter
const WGPURequestAdapterOptions requestAdapterOptions { .nextInChain = nullptr, .compatibleSurface = surface, .powerPreference = WGPUPowerPreference_HighPerformance, .forceFallbackAdapter = false };
auto onAdapterRequestEnded = [](WGPURequestAdapterStatus status, WGPUAdapter adapter, char const * message, void * pUserData) { *((WGPUAdapter*)pUserData) = adapter; };
wgpuInstanceRequestAdapter(instance, &requestAdapterOptions, onAdapterRequestEnded, &adapter);
#ifdef __EMSCRIPTEN__
while (!adapter) emscripten_sleep(10);
#endif
assert(adapter);
// get adapter and surface properties
WGPUFeatureName featureNames[32]{};
size_t featuresCount = wgpuAdapterEnumerateFeatures(adapter, featureNames);
preferredFormat = WGPUTextureFormat_BGRA8Unorm;
// request device
const WGPUDeviceDescriptor deviceDesc { .nextInChain = nullptr, .label = "The device", .requiredFeatureCount = featuresCount, .requiredFeatures = featureNames };
auto onDeviceRequestEnded = [](WGPURequestDeviceStatus status, WGPUDevice device, char const * message, void * pUserData) { *((WGPUDevice*)pUserData) = device; };
wgpuAdapterRequestDevice(adapter, &deviceDesc, onDeviceRequestEnded, &device);
#ifdef __EMSCRIPTEN__
while (!device) emscripten_sleep(10);
#endif
assert(device);
// device uncaptured error callback
auto onDeviceError = [](WGPUErrorType type, char const* message, void* pUserData) { std::cout << message << std::endl; };
wgpuDeviceSetUncapturedErrorCallback(device, onDeviceError, nullptr);
// get current queue // get current queue
queue = wgpuDeviceGetQueue(device); queue = wgpuDeviceGetQueue(device);
@ -87,8 +60,6 @@ void WgContext::release()
releaseSampler(samplerLinearRepeat); releaseSampler(samplerLinearRepeat);
releaseSampler(samplerNearestRepeat); releaseSampler(samplerNearestRepeat);
releaseBuffer(bufferIndexFan); releaseBuffer(bufferIndexFan);
wgpuDeviceRelease(device);
wgpuAdapterRelease(adapter);
} }

View file

@ -34,10 +34,9 @@ class WgPipelines;
struct WgContext { struct WgContext {
// external webgpu handles // external webgpu handles
WGPUInstance instance{}; WGPUInstance instance{};
WGPUSurface surface{};
// common webgpu handles
WGPUAdapter adapter{}; WGPUAdapter adapter{};
WGPUDevice device{}; WGPUDevice device{};
// common webgpu handles
WGPUQueue queue{}; WGPUQueue queue{};
WGPUTextureFormat preferredFormat{}; WGPUTextureFormat preferredFormat{};
// external handles (do not release) // external handles (do not release)
@ -49,7 +48,7 @@ struct WgContext {
WGPUSampler samplerLinearMirror{}; WGPUSampler samplerLinearMirror{};
WGPUSampler samplerLinearClamp{}; WGPUSampler samplerLinearClamp{};
void initialize(WGPUInstance instance, WGPUSurface surface); void initialize(WGPUInstance instance, WGPUDevice device);
void release(); void release();
// create common objects // create common objects

View file

@ -56,6 +56,13 @@ void WgRenderer::release()
mCompositorStack.clear(); mCompositorStack.clear();
mRenderStorageStack.clear(); mRenderStorageStack.clear();
mPipelines.release(mContext); mPipelines.release(mContext);
if (gpuOwner) {
if (device) wgpuDeviceRelease(device);
device = nullptr;
if (adapter) wgpuAdapterRelease(adapter);
adapter = nullptr;
gpuOwner = false;
}
} }
@ -246,9 +253,10 @@ bool WgRenderer::clear()
bool WgRenderer::sync() bool WgRenderer::sync()
{ {
disposeObjects(); disposeObjects();
if (!surface) return false;
// get current texture // get current texture
WGPUSurfaceTexture surfaceTexture{}; WGPUSurfaceTexture surfaceTexture{};
wgpuSurfaceGetCurrentTexture(mContext.surface, &surfaceTexture); wgpuSurfaceGetCurrentTexture(surface, &surfaceTexture);
WGPUTextureView dstView = mContext.createTextureView(surfaceTexture.texture); WGPUTextureView dstView = mContext.createTextureView(surfaceTexture.texture);
// create command encoder // create command encoder
@ -271,14 +279,44 @@ bool WgRenderer::sync()
// target for native window handle // target for native window handle
bool WgRenderer::target(WGPUInstance instance, WGPUSurface surface, uint32_t w, uint32_t h) bool WgRenderer::target(WGPUInstance instance, WGPUSurface surface, uint32_t w, uint32_t h, WGPUDevice device)
{ {
// store target surface properties gpuOwner = false;
mTargetSurface.stride = w; this->device = device;
mTargetSurface.w = w > 0 ? w : 1; if (!this->device) {
mTargetSurface.h = h > 0 ? h : 1; // request adapter
const WGPURequestAdapterOptions requestAdapterOptions { .nextInChain = nullptr, .compatibleSurface = surface, .powerPreference = WGPUPowerPreference_HighPerformance, .forceFallbackAdapter = false };
auto onAdapterRequestEnded = [](WGPURequestAdapterStatus status, WGPUAdapter adapter, char const * message, void * pUserData) { *((WGPUAdapter*)pUserData) = adapter; };
wgpuInstanceRequestAdapter(instance, &requestAdapterOptions, onAdapterRequestEnded, &this->adapter);
mContext.initialize(instance, surface); // get adapter and surface properties
WGPUFeatureName featureNames[32]{};
size_t featuresCount = wgpuAdapterEnumerateFeatures(this->adapter, featureNames);
// request device
const WGPUDeviceDescriptor deviceDesc { .nextInChain = nullptr, .label = "The device", .requiredFeatureCount = featuresCount, .requiredFeatures = featureNames };
auto onDeviceRequestEnded = [](WGPURequestDeviceStatus status, WGPUDevice device, char const * message, void * pUserData) { *((WGPUDevice*)pUserData) = device; };
wgpuAdapterRequestDevice(this->adapter, &deviceDesc, onDeviceRequestEnded, &this->device);
gpuOwner = true;
}
mContext.initialize(instance, this->device);
initialize();
target(surface, w, h);
mRenderStoragePool.initialize(mContext, w, h);
mStorageRoot.initialize(mContext, w, h);
mCompositor.initialize(mContext, w, h);
return true;
}
bool WgRenderer::target(WGPUSurface surface, uint32_t w, uint32_t h) {
// store target surface properties
this->surface = surface;
mTargetSurface.stride = w;
mTargetSurface.w = w;
mTargetSurface.h = h;
if (w == 0 || h == 0) return false;
if (!surface) return true;
WGPUSurfaceConfiguration surfaceConfiguration { WGPUSurfaceConfiguration surfaceConfiguration {
.device = mContext.device, .device = mContext.device,
@ -292,10 +330,7 @@ bool WgRenderer::target(WGPUInstance instance, WGPUSurface surface, uint32_t w,
#endif #endif
}; };
wgpuSurfaceConfigure(surface, &surfaceConfiguration); wgpuSurfaceConfigure(surface, &surfaceConfiguration);
initialize();
mRenderStoragePool.initialize(mContext, w, h);
mStorageRoot.initialize(mContext, w, h);
mCompositor.initialize(mContext, w, h);
return true; return true;
} }

View file

@ -45,7 +45,8 @@ public:
bool clear() override; bool clear() override;
bool sync() override; bool sync() override;
bool target(WGPUInstance instance, WGPUSurface surface, uint32_t w, uint32_t h); bool target(WGPUInstance instance, WGPUSurface surface, uint32_t w, uint32_t h, WGPUDevice device);
bool target(WGPUSurface surface, uint32_t w, uint32_t h);
Compositor* target(const RenderRegion& region, ColorSpace cs) override; Compositor* target(const RenderRegion& region, ColorSpace cs) override;
bool beginComposite(Compositor* cmp, CompositeMethod method, uint8_t opacity) override; bool beginComposite(Compositor* cmp, CompositeMethod method, uint8_t opacity) override;
@ -55,6 +56,8 @@ public:
static bool init(uint32_t threads); static bool init(uint32_t threads);
static bool term(); static bool term();
WGPUSurface surface{}; // external handle
private: private:
WgRenderer(); WgRenderer();
~WgRenderer(); ~WgRenderer();
@ -81,6 +84,10 @@ private:
Array<RenderData> mDisposeRenderDatas{}; Array<RenderData> mDisposeRenderDatas{};
Key mDisposeKey{}; Key mDisposeKey{};
WGPUAdapter adapter{};
WGPUDevice device{};
bool gpuOwner{};
}; };
#endif /* _TVG_WG_RENDERER_H_ */ #endif /* _TVG_WG_RENDERER_H_ */