From 785a75a1cad09e230bd9e718db8a3e1ed1c382cb Mon Sep 17 00:00:00 2001 From: Sergii Liebodkin Date: Fri, 6 Sep 2024 07:08:58 +0300 Subject: [PATCH] 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 --- examples/Example.h | 2 +- inc/thorvg.h | 4 +- src/renderer/tvgWgCanvas.cpp | 9 ++-- src/renderer/wg_engine/tvgWgCommon.cpp | 37 ++-------------- src/renderer/wg_engine/tvgWgCommon.h | 5 +-- src/renderer/wg_engine/tvgWgRenderer.cpp | 55 +++++++++++++++++++----- src/renderer/wg_engine/tvgWgRenderer.h | 11 ++++- 7 files changed, 69 insertions(+), 54 deletions(-) diff --git a/examples/Example.h b/examples/Example.h index 00e32e66..a52839c0 100644 --- a/examples/Example.h +++ b/examples/Example.h @@ -539,4 +539,4 @@ int main(Example* example, int argc, char **argv, uint32_t width = 800, uint32_t return 0; } -}; +}; \ No newline at end of file diff --git a/inc/thorvg.h b/inc/thorvg.h index 6f32f88d..d2ec6ba1 100644 --- a/inc/thorvg.h +++ b/inc/thorvg.h @@ -1751,7 +1751,7 @@ public: * @param[in] surface WGPUSurface, handle to a presentable surface. * @param[in] w The width 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::NonSupport In case the wg engine is not supported. * @@ -1760,7 +1760,7 @@ public: * @see Canvas::viewport() * @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. diff --git a/src/renderer/tvgWgCanvas.cpp b/src/renderer/tvgWgCanvas.cpp index c094ae4d..9b3394b0 100644 --- a/src/renderer/tvgWgCanvas.cpp +++ b/src/renderer/tvgWgCanvas.cpp @@ -50,11 +50,14 @@ WgCanvas::WgCanvas() : Canvas(nullptr), pImpl(nullptr) WgCanvas::~WgCanvas() { - delete pImpl; +#ifdef THORVG_WG_RASTER_SUPPORT + auto renderer = static_cast(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 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(Canvas::pImpl->renderer); 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}; renderer->viewport(Canvas::pImpl->vport); diff --git a/src/renderer/wg_engine/tvgWgCommon.cpp b/src/renderer/wg_engine/tvgWgCommon.cpp index 3e5af9a3..e89c283f 100755 --- a/src/renderer/wg_engine/tvgWgCommon.cpp +++ b/src/renderer/wg_engine/tvgWgCommon.cpp @@ -25,43 +25,16 @@ #endif #include "tvgWgCommon.h" #include "tvgArray.h" -#include -void WgContext::initialize(WGPUInstance instance, WGPUSurface surface) +void WgContext::initialize(WGPUInstance instance, WGPUDevice device) { assert(instance); - assert(surface); + assert(device); // store global instance and surface this->instance = instance; - this->surface = surface; - - // 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); + this->device = device; + this->preferredFormat = WGPUTextureFormat_BGRA8Unorm; // get current queue queue = wgpuDeviceGetQueue(device); @@ -87,8 +60,6 @@ void WgContext::release() releaseSampler(samplerLinearRepeat); releaseSampler(samplerNearestRepeat); releaseBuffer(bufferIndexFan); - wgpuDeviceRelease(device); - wgpuAdapterRelease(adapter); } diff --git a/src/renderer/wg_engine/tvgWgCommon.h b/src/renderer/wg_engine/tvgWgCommon.h index a73fd256..89632e47 100755 --- a/src/renderer/wg_engine/tvgWgCommon.h +++ b/src/renderer/wg_engine/tvgWgCommon.h @@ -34,10 +34,9 @@ class WgPipelines; struct WgContext { // external webgpu handles WGPUInstance instance{}; - WGPUSurface surface{}; - // common webgpu handles WGPUAdapter adapter{}; WGPUDevice device{}; + // common webgpu handles WGPUQueue queue{}; WGPUTextureFormat preferredFormat{}; // external handles (do not release) @@ -49,7 +48,7 @@ struct WgContext { WGPUSampler samplerLinearMirror{}; WGPUSampler samplerLinearClamp{}; - void initialize(WGPUInstance instance, WGPUSurface surface); + void initialize(WGPUInstance instance, WGPUDevice device); void release(); // create common objects diff --git a/src/renderer/wg_engine/tvgWgRenderer.cpp b/src/renderer/wg_engine/tvgWgRenderer.cpp index 3bb66561..d748d3da 100755 --- a/src/renderer/wg_engine/tvgWgRenderer.cpp +++ b/src/renderer/wg_engine/tvgWgRenderer.cpp @@ -56,6 +56,13 @@ void WgRenderer::release() mCompositorStack.clear(); mRenderStorageStack.clear(); 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() { disposeObjects(); + if (!surface) return false; // get current texture WGPUSurfaceTexture surfaceTexture{}; - wgpuSurfaceGetCurrentTexture(mContext.surface, &surfaceTexture); + wgpuSurfaceGetCurrentTexture(surface, &surfaceTexture); WGPUTextureView dstView = mContext.createTextureView(surfaceTexture.texture); // create command encoder @@ -271,14 +279,44 @@ bool WgRenderer::sync() // 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) { + gpuOwner = false; + this->device = device; + if (!this->device) { + // 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); + + // 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 > 0 ? w : 1; - mTargetSurface.h = h > 0 ? h : 1; - - mContext.initialize(instance, surface); + mTargetSurface.w = w; + mTargetSurface.h = h; + if (w == 0 || h == 0) return false; + if (!surface) return true; WGPUSurfaceConfiguration surfaceConfiguration { .device = mContext.device, @@ -292,10 +330,7 @@ bool WgRenderer::target(WGPUInstance instance, WGPUSurface surface, uint32_t w, #endif }; wgpuSurfaceConfigure(surface, &surfaceConfiguration); - initialize(); - mRenderStoragePool.initialize(mContext, w, h); - mStorageRoot.initialize(mContext, w, h); - mCompositor.initialize(mContext, w, h); + return true; } diff --git a/src/renderer/wg_engine/tvgWgRenderer.h b/src/renderer/wg_engine/tvgWgRenderer.h index 77a83c7e..6f6ba7db 100755 --- a/src/renderer/wg_engine/tvgWgRenderer.h +++ b/src/renderer/wg_engine/tvgWgRenderer.h @@ -45,7 +45,8 @@ public: bool clear() 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; bool beginComposite(Compositor* cmp, CompositeMethod method, uint8_t opacity) override; @@ -55,6 +56,8 @@ public: static bool init(uint32_t threads); static bool term(); + WGPUSurface surface{}; // external handle + private: WgRenderer(); ~WgRenderer(); @@ -74,13 +77,17 @@ private: WgContext mContext; WgPipelines mPipelines; WgCompositor mCompositor; - + Surface mTargetSurface; BlendMethod mBlendMethod{}; RenderRegion mViewport{}; Array mDisposeRenderDatas{}; Key mDisposeKey{}; + + WGPUAdapter adapter{}; + WGPUDevice device{}; + bool gpuOwner{}; }; #endif /* _TVG_WG_RENDERER_H_ */