diff --git a/Plugins/NativeEngine/Source/NativeEngine.cpp b/Plugins/NativeEngine/Source/NativeEngine.cpp index 864031710..744065f9b 100644 --- a/Plugins/NativeEngine/Source/NativeEngine.cpp +++ b/Plugins/NativeEngine/Source/NativeEngine.cpp @@ -77,6 +77,8 @@ namespace Babylon constexpr uint64_t MULTIPLY = BGFX_STATE_BLEND_FUNC_SEPARATE(BGFX_STATE_BLEND_DST_COLOR, BGFX_STATE_BLEND_ZERO, BGFX_STATE_BLEND_ONE, BGFX_STATE_BLEND_ONE); constexpr uint64_t MAXIMIZED = BGFX_STATE_BLEND_FUNC_SEPARATE(BGFX_STATE_BLEND_SRC_ALPHA, BGFX_STATE_BLEND_INV_SRC_COLOR, BGFX_STATE_BLEND_ONE, BGFX_STATE_BLEND_ONE); constexpr uint64_t ONEONE = BGFX_STATE_BLEND_FUNC_SEPARATE(BGFX_STATE_BLEND_ONE, BGFX_STATE_BLEND_ONE, BGFX_STATE_BLEND_ZERO, BGFX_STATE_BLEND_ONE); + constexpr uint64_t ONEONE_ONEONE = BGFX_STATE_BLEND_FUNC_SEPARATE(BGFX_STATE_BLEND_ONE, BGFX_STATE_BLEND_ONE, BGFX_STATE_BLEND_ONE, BGFX_STATE_BLEND_ONE); + constexpr uint64_t LAYER_ACCUMULATE = BGFX_STATE_BLEND_FUNC_SEPARATE(BGFX_STATE_BLEND_SRC_ALPHA, BGFX_STATE_BLEND_INV_SRC_ALPHA, BGFX_STATE_BLEND_ONE, BGFX_STATE_BLEND_INV_SRC_ALPHA); constexpr uint64_t PREMULTIPLIED = BGFX_STATE_BLEND_FUNC_SEPARATE(BGFX_STATE_BLEND_ONE, BGFX_STATE_BLEND_INV_SRC_ALPHA, BGFX_STATE_BLEND_ONE, BGFX_STATE_BLEND_ONE); constexpr uint64_t PREMULTIPLIED_PORTERDUFF = BGFX_STATE_BLEND_FUNC_SEPARATE(BGFX_STATE_BLEND_ONE, BGFX_STATE_BLEND_INV_SRC_ALPHA, BGFX_STATE_BLEND_ONE, BGFX_STATE_BLEND_INV_SRC_ALPHA); constexpr uint64_t INTERPOLATE = BGFX_STATE_BLEND_FUNC_SEPARATE(BGFX_STATE_BLEND_FACTOR, BGFX_STATE_BLEND_INV_FACTOR, BGFX_STATE_BLEND_FACTOR, BGFX_STATE_BLEND_INV_FACTOR); @@ -598,6 +600,8 @@ namespace Babylon StaticValue("ALPHA_MULTIPLY", Napi::Number::From(env, AlphaMode::MULTIPLY)), StaticValue("ALPHA_MAXIMIZED", Napi::Number::From(env, AlphaMode::MAXIMIZED)), StaticValue("ALPHA_ONEONE", Napi::Number::From(env, AlphaMode::ONEONE)), + StaticValue("ALPHA_ONEONE_ONEONE", Napi::Number::From(env, AlphaMode::ONEONE_ONEONE)), + StaticValue("ALPHA_LAYER_ACCUMULATE", Napi::Number::From(env, AlphaMode::LAYER_ACCUMULATE)), StaticValue("ALPHA_PREMULTIPLIED", Napi::Number::From(env, AlphaMode::PREMULTIPLIED)), StaticValue("ALPHA_PREMULTIPLIED_PORTERDUFF", Napi::Number::From(env, AlphaMode::PREMULTIPLIED_PORTERDUFF)), StaticValue("ALPHA_INTERPOLATE", Napi::Number::From(env, AlphaMode::INTERPOLATE)), @@ -726,6 +730,7 @@ namespace Babylon InstanceMethod("resizeImageBitmap", &NativeEngine::ResizeImageBitmap), InstanceMethod("createFrameBuffer", &NativeEngine::CreateFrameBuffer), + InstanceMethod("createMultiFrameBuffer", &NativeEngine::CreateMultiFrameBuffer), InstanceMethod("getRenderWidth", &NativeEngine::GetRenderWidth), InstanceMethod("getRenderHeight", &NativeEngine::GetRenderHeight), @@ -1853,12 +1858,57 @@ namespace Babylon const bool generateDepth = info[4].As(); const uint32_t samples = info[5].IsUndefined() ? 1 : info[5].As().Uint32Value(); - std::array attachments{}; + // A single render target is just the zero-or-one color attachment case of the shared implementation. + Graphics::Texture* const colorTextures[]{texture}; + const gsl::span colorAttachments{colorTextures, texture != nullptr ? 1u : 0u}; + + return CreateFrameBufferImpl(info.Env(), colorAttachments, width, height, generateStencilBuffer, generateDepth, samples); + } + + Napi::Value NativeEngine::CreateMultiFrameBuffer(const Napi::CallbackInfo& info) + { + const auto colorTexturesArray = info[0].As(); + const uint16_t width = static_cast(info[1].As().Uint32Value()); + const uint16_t height = static_cast(info[2].As().Uint32Value()); + const bool generateStencilBuffer = info[3].As(); + const bool generateDepth = info[4].As(); + const uint32_t samples = info[5].IsUndefined() ? 1 : info[5].As().Uint32Value(); + + const uint32_t colorCount = colorTexturesArray.Length(); + // BGFX_CONFIG_MAX_FRAME_BUFFER_ATTACHMENTS is 8, so at most 8 color attachments fit alongside the depth attachment. + std::array colorTextures{}; + if (colorCount > colorTextures.size()) + { + throw Napi::Error::New(info.Env(), "Invalid number of color attachments for multi render target frame buffer"); + } + + for (uint32_t i = 0; i < colorCount; ++i) + { + colorTextures[i] = colorTexturesArray.Get(i).As>().Get(); + } + + return CreateFrameBufferImpl(info.Env(), gsl::span{colorTextures.data(), colorCount}, width, height, generateStencilBuffer, generateDepth, samples); + } + + Napi::Value NativeEngine::CreateFrameBufferImpl(Napi::Env env, gsl::span colorTextures, uint16_t width, uint16_t height, bool generateStencilBuffer, bool generateDepth, uint32_t samples) + { + const bgfx::Caps* caps = bgfx::getCaps(); + const uint32_t colorCount = static_cast(colorTextures.size()); + // One slot per color attachment, plus a single depth/stencil attachment only when one is + // generated. bgfx caps the total via maxFBAttachments; reject out-of-range counts up front + // rather than relying on the validation assert. + const uint32_t depthStencilCount = (generateStencilBuffer || generateDepth) ? 1u : 0u; + if (colorCount + depthStencilCount > caps->limits.maxFBAttachments) + { + throw Napi::Error::New(env, "Invalid number of color attachments for frame buffer"); + } + + // BGFX_CONFIG_MAX_FRAME_BUFFER_ATTACHMENTS is 8, so 8 color + 1 depth fits in a fixed array. + std::array attachments{}; uint8_t numAttachments = 0; - if (texture != nullptr) + for (Graphics::Texture* texture : colorTextures) { - const bgfx::Caps* caps = bgfx::getCaps(); // bgfx validation now asserts when trying to use BGFX_RESOLVE_AUTO_GEN_MIPS with a texture that doesn't have the BGFX_CAPS_FORMAT_TEXTURE_MIP_AUTOGEN flag, // but before it would just ignore the flag and not generate mips without any warning. This prevents validation assert, but rendering might be broken if autogen // mips were expected. Basically this change preserves previous behavior. @@ -1873,7 +1923,7 @@ namespace Babylon { if (generateStencilBuffer && !generateDepth) { - JsConsoleLogger::LogWarn(info.Env(), "Stencil without depth is not supported, assuming depth and stencil"); + JsConsoleLogger::LogWarn(env, "Stencil without depth is not supported, assuming depth and stencil"); } auto flags = BGFX_TEXTURE_RT_WRITE_ONLY | RenderTargetSamplesToBgfxMsaaFlag(samples); @@ -1905,11 +1955,11 @@ namespace Babylon bgfx::destroy(depthStencilTextureHandle); } - throw Napi::Error::New(info.Env(), "Failed to create frame buffer"); + throw Napi::Error::New(env, "Failed to create frame buffer"); } Graphics::FrameBuffer* frameBuffer = new Graphics::FrameBuffer(m_deviceContext, frameBufferHandle, width, height, false, generateDepth, generateStencilBuffer, depthStencilAttachmentIndex); - return Napi::Pointer::Create(info.Env(), frameBuffer, Napi::NapiPointerDeleter(frameBuffer)); + return Napi::Pointer::Create(env, frameBuffer, Napi::NapiPointerDeleter(frameBuffer)); } // TODO: This doesn't get called when an Engine instance is disposed. diff --git a/Plugins/NativeEngine/Source/NativeEngine.h b/Plugins/NativeEngine/Source/NativeEngine.h index 229ef0e3a..8c250eb57 100644 --- a/Plugins/NativeEngine/Source/NativeEngine.h +++ b/Plugins/NativeEngine/Source/NativeEngine.h @@ -33,6 +33,11 @@ namespace Babylon { + namespace Graphics + { + class Texture; + } + class NativeEngine final : public Napi::ObjectWrap { static constexpr auto JS_CLASS_NAME = "_NativeEngine"; @@ -115,6 +120,8 @@ namespace Babylon void DeleteTexture(const Napi::CallbackInfo& info); Napi::Value ReadTexture(const Napi::CallbackInfo& info); Napi::Value CreateFrameBuffer(const Napi::CallbackInfo& info); + Napi::Value CreateMultiFrameBuffer(const Napi::CallbackInfo& info); + Napi::Value CreateFrameBufferImpl(Napi::Env env, gsl::span colorTextures, uint16_t width, uint16_t height, bool generateStencilBuffer, bool generateDepth, uint32_t samples); void DeleteFrameBuffer(NativeDataStream::Reader& data); void BindFrameBuffer(NativeDataStream::Reader& data); void UnbindFrameBuffer(NativeDataStream::Reader& data);