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_ */