diff --git a/Plugins/NativeEngine/Source/NativeEngine.cpp b/Plugins/NativeEngine/Source/NativeEngine.cpp index ae5dde25b..410caaf80 100644 --- a/Plugins/NativeEngine/Source/NativeEngine.cpp +++ b/Plugins/NativeEngine/Source/NativeEngine.cpp @@ -456,6 +456,7 @@ namespace Babylon InstanceMethod("updateDynamicVertexBuffer", &NativeEngine::UpdateDynamicVertexBuffer), InstanceMethod("createProgram", &NativeEngine::CreateProgram), + InstanceMethod("createProgramAsync", &NativeEngine::CreateProgramAsync), InstanceMethod("getUniforms", &NativeEngine::GetUniforms), InstanceMethod("getAttributes", &NativeEngine::GetAttributes), @@ -685,22 +686,11 @@ namespace Babylon return vertexSource; } - Napi::Value NativeEngine::CreateProgram(const Napi::CallbackInfo& info) + std::unique_ptr NativeEngine::CreateProgramInternal(const std::string vertexSource, const std::string fragmentSource) { - const std::string vertexSource = info[0].As().Utf8Value(); - const std::string fragmentSource = info[1].As().Utf8Value(); + ShaderCompiler::BgfxShaderInfo shaderInfo = m_shaderCompiler.Compile(ProcessShaderCoordinates(vertexSource), ProcessSamplerFlip(fragmentSource)); - ProgramData* program = new ProgramData{}; - ShaderCompiler::BgfxShaderInfo shaderInfo{}; - - try - { - shaderInfo = m_shaderCompiler.Compile(ProcessShaderCoordinates(vertexSource), ProcessSamplerFlip(fragmentSource)); - } - catch (const std::exception& ex) - { - throw Napi::Error::New(info.Env(), ex.what()); - } + std::unique_ptr program = std::make_unique(); static auto InitUniformInfos{ [](bgfx::ShaderHandle shader, const std::unordered_map& uniformStages, std::unordered_map& uniformInfos, std::unordered_map& uniformNameToIndex) { @@ -722,13 +712,67 @@ namespace Babylon auto vertexShader = bgfx::createShader(bgfx::copy(shaderInfo.VertexBytes.data(), static_cast(shaderInfo.VertexBytes.size()))); InitUniformInfos(vertexShader, shaderInfo.UniformStages, program->UniformInfos, program->UniformNameToIndex); - program->VertexAttributeLocations = std::move(shaderInfo.VertexAttributeLocations); auto fragmentShader = bgfx::createShader(bgfx::copy(shaderInfo.FragmentBytes.data(), static_cast(shaderInfo.FragmentBytes.size()))); InitUniformInfos(fragmentShader, shaderInfo.UniformStages, program->UniformInfos, program->UniformNameToIndex); program->Handle = bgfx::createProgram(vertexShader, fragmentShader, true); - return Napi::Pointer::Create(info.Env(), program, Napi::NapiPointerDeleter(program)); + program->VertexAttributeLocations = std::move(shaderInfo.VertexAttributeLocations); + + return program; + } + + Napi::Value NativeEngine::CreateProgram(const Napi::CallbackInfo& info) + { + const std::string vertexSource = info[0].As().Utf8Value(); + const std::string fragmentSource = info[1].As().Utf8Value(); + ProgramData* program = new ProgramData{}; + Napi::Value jsProgram = Napi::Pointer::Create(info.Env(), program, Napi::NapiPointerDeleter(program)); + try + { + *program = std::move(*CreateProgramInternal(vertexSource, fragmentSource)); + } + catch (const std::exception& ex) + { + throw Napi::Error::New(info.Env(), ex.what()); + } + return jsProgram; + } + + Napi::Value NativeEngine::CreateProgramAsync(const Napi::CallbackInfo& info) + { + const std::string vertexSource = info[0].As().Utf8Value(); + const std::string fragmentSource = info[1].As().Utf8Value(); + const Napi::Function onSuccess = info[2].As(); + const Napi::Function onError = info[3].As(); + + ProgramData* program = new ProgramData{}; + Napi::Value jsProgram = Napi::Pointer::Create(info.Env(), program, Napi::NapiPointerDeleter(program)); + + arcana::make_task(arcana::threadpool_scheduler, *m_cancellationSource, + [this, vertexSource, fragmentSource, cancellationSource{m_cancellationSource}]() -> std::unique_ptr + { + return CreateProgramInternal(vertexSource, fragmentSource); + }) + .then(m_runtimeScheduler, *m_cancellationSource, + [program, + jsProgramRef{Napi::Persistent(jsProgram)}, + onSuccessRef{Napi::Persistent(onSuccess)}, + onErrorRef{Napi::Persistent(onError)}, + cancellationSource{m_cancellationSource}](const arcana::expected, std::exception_ptr>& result) + { + if (result.has_error()) + { + onErrorRef.Call({Napi::Error::New(onErrorRef.Env(), result.error()).Value()}); + } + else + { + *program = std::move(*result.value()); + onSuccessRef.Call({}); + } + }); + + return jsProgram; } Napi::Value NativeEngine::GetUniforms(const Napi::CallbackInfo& info) diff --git a/Plugins/NativeEngine/Source/NativeEngine.h b/Plugins/NativeEngine/Source/NativeEngine.h index 1b86b577d..385e64547 100644 --- a/Plugins/NativeEngine/Source/NativeEngine.h +++ b/Plugins/NativeEngine/Source/NativeEngine.h @@ -43,10 +43,29 @@ namespace Babylon struct ProgramData final { ProgramData() = default; - ProgramData(ProgramData&& other) = delete; ProgramData(const ProgramData&) = delete; - ProgramData& operator=(ProgramData&& other) = delete; - ProgramData& operator=(const ProgramData& other) = delete; + ProgramData& operator=(const ProgramData&) = delete; + + ProgramData(ProgramData&& other) noexcept: + Handle{other.Handle}, + Uniforms{std::move(other.Uniforms)}, + UniformNameToIndex{std::move(other.UniformNameToIndex)}, + UniformInfos{std::move(other.UniformInfos)}, + VertexAttributeLocations{std::move(other.VertexAttributeLocations)} + { + other.Handle = BGFX_INVALID_HANDLE; + } + + ProgramData& operator=(ProgramData&& other) noexcept + { + Handle = std::move(other.Handle); + other.Handle = BGFX_INVALID_HANDLE; + Uniforms = std::move(other.Uniforms); + UniformNameToIndex = std::move(other.UniformNameToIndex); + UniformInfos = std::move(other.UniformInfos); + VertexAttributeLocations = std::move(other.VertexAttributeLocations); + return *this; + } ~ProgramData() { @@ -55,15 +74,14 @@ namespace Babylon void Dispose() { - if (!Disposed && bgfx::isValid(Handle)) + if (bgfx::isValid(Handle)) { bgfx::destroy(Handle); + Handle = BGFX_INVALID_HANDLE; } - Disposed = true; } bgfx::ProgramHandle Handle{bgfx::kInvalidHandle}; - bool Disposed{false}; struct UniformValue { @@ -120,7 +138,9 @@ namespace Babylon void DeleteVertexBuffer(NativeDataStream::Reader& data); void RecordVertexBuffer(const Napi::CallbackInfo& info); void UpdateDynamicVertexBuffer(const Napi::CallbackInfo& info); + std::unique_ptr CreateProgramInternal(const std::string vertexSource, const std::string fragmentSource); Napi::Value CreateProgram(const Napi::CallbackInfo& info); + Napi::Value CreateProgramAsync(const Napi::CallbackInfo& info); Napi::Value GetUniforms(const Napi::CallbackInfo& info); Napi::Value GetAttributes(const Napi::CallbackInfo& info); void SetProgram(NativeDataStream::Reader& data);