@@ -19,6 +19,7 @@ import {
1919 type InternalTextureCreationOptions ,
2020} from "../Materials/Textures/textureCreationOptions" ;
2121import { type IPipelineContext } from "./IPipelineContext" ;
22+ import { type IMultiRenderTargetOptions } from "../Materials/Textures/multiRenderTarget.pure" ;
2223import { type IColor3Like , type IColor4Like , type IViewportLike } from "../Maths/math.like" ;
2324import { Logger } from "../Misc/logger" ;
2425import { Constants } from "./constants" ;
@@ -344,7 +345,7 @@ export class ThinNativeEngine extends ThinEngine {
344345 textureHalfFloatRender : true ,
345346 textureLOD : true ,
346347 texelFetch : false ,
347- drawBuffersExtension : false ,
348+ drawBuffersExtension : true ,
348349 depthTextureExtension : false ,
349350 vertexArrayObject : true ,
350351 instancedArrays : true ,
@@ -570,8 +571,11 @@ export class ThinNativeEngine extends ThinEngine {
570571 }
571572
572573 public override clear ( color : Nullable < IColor4Like > , backBuffer : boolean , depth : boolean , stencil : boolean = false , stencilClearValue = 0 ) : void {
573- if ( this . useReverseDepthBuffer ) {
574- throw new Error ( "reverse depth buffer is not currently implemented" ) ;
574+ if ( depth && this . useReverseDepthBuffer ) {
575+ // Reverse-Z: the scene is rendered with a flipped projection (near maps to 1, far to 0), so the
576+ // depth buffer is cleared to 0 and the comparison must accept greater values. Mirror the WebGL
577+ // engine, which sets the depth-culling comparison to GEQUAL here and clears depth to 0.
578+ this . _depthCullingState . depthFunc = Constants . GEQUAL ;
575579 }
576580
577581 this . _commandBufferEncoder . startEncodingCommand ( _native . Engine . COMMAND_CLEAR ) ;
@@ -581,7 +585,7 @@ export class ThinNativeEngine extends ThinEngine {
581585 this . _commandBufferEncoder . encodeCommandArgAsFloat32 ( color ? color . b : 0 ) ;
582586 this . _commandBufferEncoder . encodeCommandArgAsFloat32 ( color ? color . a : 1 ) ;
583587 this . _commandBufferEncoder . encodeCommandArgAsUInt32 ( depth ? 1 : 0 ) ;
584- this . _commandBufferEncoder . encodeCommandArgAsFloat32 ( 1 ) ;
588+ this . _commandBufferEncoder . encodeCommandArgAsFloat32 ( depth && this . useReverseDepthBuffer ? 0 : 1 ) ;
585589 this . _commandBufferEncoder . encodeCommandArgAsUInt32 ( stencil ? 1 : 0 ) ;
586590 this . _commandBufferEncoder . encodeCommandArgAsUInt32 ( stencilClearValue ) ;
587591 this . _commandBufferEncoder . finishEncodingCommand ( ) ;
@@ -1074,6 +1078,20 @@ export class ThinNativeEngine extends ThinEngine {
10741078 this . _commandBufferEncoder . finishEncodingCommand ( ) ;
10751079 }
10761080
1081+ public override applyStates ( ) : void {
1082+ // The base ThinEngine.applyStates() drives the WebGL context (this._gl) directly, which is null on
1083+ // Native. Flush the depth-culling state through the native command path instead, so callers that
1084+ // mutate engine.depthCullingState directly and then call applyStates() (e.g. the depth-peeling / OIT
1085+ // renderer) take effect. Alpha and stencil state are applied on Native via their dedicated setters
1086+ // (setAlphaMode / setStencil*), which encode their commands when called.
1087+ const depthCullingState = this . _depthCullingState ;
1088+ if ( depthCullingState . depthFunc !== null && depthCullingState . depthFunc !== undefined ) {
1089+ this . setDepthFunction ( depthCullingState . depthFunc ) ;
1090+ }
1091+ this . setDepthBuffer ( depthCullingState . depthTest ) ;
1092+ this . setDepthWrite ( depthCullingState . depthMask ) ;
1093+ }
1094+
10771095 /**
10781096 * Gets a boolean indicating if depth writing is enabled
10791097 * @returns the current depth writing state
@@ -2317,6 +2335,161 @@ export class ThinNativeEngine extends ThinEngine {
23172335 return rtWrapper ;
23182336 }
23192337
2338+ public override createMultipleRenderTarget ( size : TextureSize , options : IMultiRenderTargetOptions , _initializeBuffers = true ) : RenderTargetWrapper {
2339+ const rtWrapper = this . _createHardwareRenderTargetWrapper ( true , false , size ) as NativeRenderTargetWrapper ;
2340+
2341+ let generateMipMaps = false ;
2342+ let generateDepthBuffer = true ;
2343+ let generateStencilBuffer = false ;
2344+ let generateDepthTexture = false ;
2345+ let textureCount = 1 ;
2346+ let samples = 1 ;
2347+ let types : number [ ] = [ ] ;
2348+ let samplingModes : number [ ] = [ ] ;
2349+ let formats : number [ ] = [ ] ;
2350+ let targets : number [ ] = [ ] ;
2351+ let faceIndex : number [ ] = [ ] ;
2352+ let layerIndex : number [ ] = [ ] ;
2353+ let labels : string [ ] = [ ] ;
2354+ let dontCreateTextures = false ;
2355+
2356+ if ( options !== undefined ) {
2357+ generateMipMaps = options . generateMipMaps ?? false ;
2358+ generateDepthBuffer = options . generateDepthBuffer ?? true ;
2359+ generateStencilBuffer = options . generateStencilBuffer ?? false ;
2360+ generateDepthTexture = options . generateDepthTexture ?? false ;
2361+ textureCount = options . textureCount ?? 1 ;
2362+ samples = options . samples ?? 1 ;
2363+ types = options . types || types ;
2364+ samplingModes = options . samplingModes || samplingModes ;
2365+ formats = options . formats || formats ;
2366+ targets = options . targetTypes || targets ;
2367+ faceIndex = options . faceIndex || faceIndex ;
2368+ layerIndex = options . layerIndex || layerIndex ;
2369+ labels = options . labels || labels ;
2370+ dontCreateTextures = options . dontCreateTextures ?? false ;
2371+ }
2372+
2373+ rtWrapper . label = options ?. label ?? "MultiRenderTargetWrapper" ;
2374+
2375+ const width = ( < { width : number ; height : number } > size ) . width ?? < number > size ;
2376+ const height = ( < { width : number ; height : number } > size ) . height ?? < number > size ;
2377+
2378+ const textures : InternalTexture [ ] = [ ] ;
2379+ const attachments : number [ ] = [ ] ;
2380+ const colorHandles : NativeTexture [ ] = [ ] ;
2381+
2382+ for ( let i = 0 ; i < textureCount ; i ++ ) {
2383+ const samplingMode = samplingModes [ i ] || Constants . TEXTURE_TRILINEAR_SAMPLINGMODE ;
2384+ let type = types [ i ] || Constants . TEXTURETYPE_UNSIGNED_BYTE ;
2385+ const format = formats [ i ] || Constants . TEXTUREFORMAT_RGBA ;
2386+ const target = targets [ i ] || Constants . TEXTURE_2D ;
2387+
2388+ attachments . push ( i ) ;
2389+
2390+ // target === -1 marks an attachment with no engine-created texture; dontCreateTextures defers them.
2391+ if ( target === - 1 || dontCreateTextures ) {
2392+ continue ;
2393+ }
2394+
2395+ if ( type === Constants . TEXTURETYPE_FLOAT && ! this . _caps . textureFloat ) {
2396+ type = Constants . TEXTURETYPE_UNSIGNED_BYTE ;
2397+ Logger . Warn ( "Float textures are not supported. Multi render target attachment forced to TEXTURETYPE_UNSIGNED_BYTE type" ) ;
2398+ }
2399+
2400+ const texture = new InternalTexture ( this , InternalTextureSource . MultiRenderTarget ) ;
2401+ const nativeTexture = texture . _hardwareTexture ! . underlyingResource ;
2402+ if ( target !== Constants . TEXTURE_2D ) {
2403+ // Native multi render targets currently only attach 2D color textures; cube / 2D-array
2404+ // attachments are created as 2D so the attachment still exists and mrt.textures[i] is populated.
2405+ Logger . Warn ( "Multi render target attachment target " + target + " is not supported on Native; using a 2D texture." ) ;
2406+ }
2407+ // bgfx auto-generates the mip chain on resolve; avoid the mips + MSAA combo (see createRenderTargetTexture).
2408+ const hasMips = samples > 1 ? false : generateMipMaps ;
2409+ this . _engine . initializeTexture ( nativeTexture , width , height , hasMips , getNativeTextureFormat ( format , type ) , /*renderTarget*/ true , /*srgb*/ false , samples ) ;
2410+ this . _setTextureSampling ( nativeTexture , getNativeSamplingMode ( samplingMode ) ) ;
2411+
2412+ texture . baseWidth = width ;
2413+ texture . baseHeight = height ;
2414+ texture . width = width ;
2415+ texture . height = height ;
2416+ texture . isReady = true ;
2417+ texture . samples = samples ;
2418+ texture . generateMipMaps = generateMipMaps ;
2419+ texture . samplingMode = samplingMode ;
2420+ texture . type = type ;
2421+ texture . format = format ;
2422+ texture . label = labels [ i ] ?? rtWrapper . label + "-Texture" + i ;
2423+
2424+ textures [ i ] = texture ;
2425+ colorHandles . push ( nativeTexture ) ;
2426+ this . _internalTexturesCache . push ( texture ) ;
2427+ }
2428+
2429+ // The native engine creates one framebuffer with all the color attachments (bgfx writes to every
2430+ // attachment of the bound framebuffer, so there is no drawBuffers equivalent to issue per draw).
2431+ if ( colorHandles . length > 0 ) {
2432+ rtWrapper . _framebuffer = this . _engine . createMultiFrameBuffer ( colorHandles , width , height , generateStencilBuffer , generateDepthBuffer || generateDepthTexture , samples ) ;
2433+ }
2434+
2435+ rtWrapper . _generateDepthBuffer = generateDepthBuffer || generateDepthTexture ;
2436+ rtWrapper . _generateStencilBuffer = generateStencilBuffer ;
2437+ rtWrapper . _samples = samples ;
2438+ rtWrapper . _attachments = attachments ;
2439+
2440+ rtWrapper . setTextures ( textures ) ;
2441+ rtWrapper . setLayerAndFaceIndices ( layerIndex , faceIndex ) ;
2442+
2443+ return rtWrapper ;
2444+ }
2445+
2446+ public override bindAttachments ( _attachments : number [ ] ) : void {
2447+ // No-op on Native: bgfx renders to every color attachment of the bound framebuffer, so there is
2448+ // no gl.drawBuffers equivalent to select a subset.
2449+ }
2450+
2451+ public override buildTextureLayout ( textureStatus : boolean [ ] , _backBufferLayout = false ) : number [ ] {
2452+ // Native has no gl draw-buffer enums; return a per-attachment index list (consumers only use the
2453+ // length/order, and bindAttachments is a no-op).
2454+ const result : number [ ] = [ ] ;
2455+ for ( let i = 0 ; i < textureStatus . length ; i ++ ) {
2456+ result . push ( textureStatus [ i ] ? i : - 1 ) ;
2457+ }
2458+ return result ;
2459+ }
2460+
2461+ public override restoreSingleAttachment ( ) : void {
2462+ // No-op on Native (see bindAttachments).
2463+ }
2464+
2465+ public override restoreSingleAttachmentForRenderTarget ( ) : void {
2466+ // No-op on Native (see bindAttachments).
2467+ }
2468+
2469+ public override generateMipMapsMultiFramebuffer ( _texture : RenderTargetWrapper ) : void {
2470+ // No-op on Native: bgfx auto-generates mips on render-target resolve (as for 2D/cube RTTs).
2471+ }
2472+
2473+ public override resolveMultiFramebuffer ( _texture : RenderTargetWrapper ) : void {
2474+ // No-op on Native: bgfx resolves MSAA render targets automatically.
2475+ }
2476+
2477+ public override unBindMultiColorAttachmentFramebuffer ( _rtWrapper : RenderTargetWrapper , _disableGenerateMipMaps = false , onBeforeUnbind ?: ( ) => void ) : void {
2478+ this . _currentRenderTarget = null ;
2479+ if ( onBeforeUnbind ) {
2480+ onBeforeUnbind ( ) ;
2481+ }
2482+ this . _bindUnboundFramebuffer ( null ) ;
2483+ }
2484+
2485+ public override updateMultipleRenderTargetTextureSampleCount ( rtWrapper : Nullable < RenderTargetWrapper > , samples : number , _initializeBuffers = true ) : number {
2486+ // Native MSAA is configured at texture creation; just record the requested sample count.
2487+ if ( rtWrapper ) {
2488+ ( rtWrapper as NativeRenderTargetWrapper ) . _samples = samples ;
2489+ }
2490+ return samples ;
2491+ }
2492+
23202493 public override updateRenderTargetTextureSampleCount ( rtWrapper : RenderTargetWrapper , samples : number ) : number {
23212494 if ( rtWrapper . samples === samples ) {
23222495 return samples ;
0 commit comments