@@ -18,6 +18,7 @@ import {
1818 type InternalTextureCreationOptions ,
1919} from "../Materials/Textures/textureCreationOptions" ;
2020import { type IPipelineContext } from "./IPipelineContext" ;
21+ import { type IMultiRenderTargetOptions } from "../Materials/Textures/multiRenderTarget.pure" ;
2122import { type IColor3Like , type IColor4Like , type IViewportLike } from "../Maths/math.like" ;
2223import { Logger } from "../Misc/logger" ;
2324import { Constants } from "./constants" ;
@@ -330,7 +331,7 @@ export class ThinNativeEngine extends ThinEngine {
330331 textureHalfFloatRender : true ,
331332 textureLOD : true ,
332333 texelFetch : false ,
333- drawBuffersExtension : false ,
334+ drawBuffersExtension : true ,
334335 depthTextureExtension : false ,
335336 vertexArrayObject : true ,
336337 instancedArrays : true ,
@@ -556,8 +557,11 @@ export class ThinNativeEngine extends ThinEngine {
556557 }
557558
558559 public override clear ( color : Nullable < IColor4Like > , backBuffer : boolean , depth : boolean , stencil : boolean = false , stencilClearValue = 0 ) : void {
559- if ( this . useReverseDepthBuffer ) {
560- throw new Error ( "reverse depth buffer is not currently implemented" ) ;
560+ if ( depth && this . useReverseDepthBuffer ) {
561+ // Reverse-Z: the scene is rendered with a flipped projection (near maps to 1, far to 0), so the
562+ // depth buffer is cleared to 0 and the comparison must accept greater values. Mirror the WebGL
563+ // engine, which sets the depth-culling comparison to GEQUAL here and clears depth to 0.
564+ this . _depthCullingState . depthFunc = Constants . GEQUAL ;
561565 }
562566
563567 this . _commandBufferEncoder . startEncodingCommand ( _native . Engine . COMMAND_CLEAR ) ;
@@ -567,7 +571,7 @@ export class ThinNativeEngine extends ThinEngine {
567571 this . _commandBufferEncoder . encodeCommandArgAsFloat32 ( color ? color . b : 0 ) ;
568572 this . _commandBufferEncoder . encodeCommandArgAsFloat32 ( color ? color . a : 1 ) ;
569573 this . _commandBufferEncoder . encodeCommandArgAsUInt32 ( depth ? 1 : 0 ) ;
570- this . _commandBufferEncoder . encodeCommandArgAsFloat32 ( 1 ) ;
574+ this . _commandBufferEncoder . encodeCommandArgAsFloat32 ( depth && this . useReverseDepthBuffer ? 0 : 1 ) ;
571575 this . _commandBufferEncoder . encodeCommandArgAsUInt32 ( stencil ? 1 : 0 ) ;
572576 this . _commandBufferEncoder . encodeCommandArgAsUInt32 ( stencilClearValue ) ;
573577 this . _commandBufferEncoder . finishEncodingCommand ( ) ;
@@ -1060,6 +1064,20 @@ export class ThinNativeEngine extends ThinEngine {
10601064 this . _commandBufferEncoder . finishEncodingCommand ( ) ;
10611065 }
10621066
1067+ public override applyStates ( ) : void {
1068+ // The base ThinEngine.applyStates() drives the WebGL context (this._gl) directly, which is null on
1069+ // Native. Flush the depth-culling state through the native command path instead, so callers that
1070+ // mutate engine.depthCullingState directly and then call applyStates() (e.g. the depth-peeling / OIT
1071+ // renderer) take effect. Alpha and stencil state are applied on Native via their dedicated setters
1072+ // (setAlphaMode / setStencil*), which encode their commands when called.
1073+ const depthCullingState = this . _depthCullingState ;
1074+ if ( depthCullingState . depthFunc !== null && depthCullingState . depthFunc !== undefined ) {
1075+ this . setDepthFunction ( depthCullingState . depthFunc ) ;
1076+ }
1077+ this . setDepthBuffer ( depthCullingState . depthTest ) ;
1078+ this . setDepthWrite ( depthCullingState . depthMask ) ;
1079+ }
1080+
10631081 /**
10641082 * Gets a boolean indicating if depth writing is enabled
10651083 * @returns the current depth writing state
@@ -2303,6 +2321,161 @@ export class ThinNativeEngine extends ThinEngine {
23032321 return rtWrapper ;
23042322 }
23052323
2324+ public override createMultipleRenderTarget ( size : TextureSize , options : IMultiRenderTargetOptions , _initializeBuffers = true ) : RenderTargetWrapper {
2325+ const rtWrapper = this . _createHardwareRenderTargetWrapper ( true , false , size ) as NativeRenderTargetWrapper ;
2326+
2327+ let generateMipMaps = false ;
2328+ let generateDepthBuffer = true ;
2329+ let generateStencilBuffer = false ;
2330+ let generateDepthTexture = false ;
2331+ let textureCount = 1 ;
2332+ let samples = 1 ;
2333+ let types : number [ ] = [ ] ;
2334+ let samplingModes : number [ ] = [ ] ;
2335+ let formats : number [ ] = [ ] ;
2336+ let targets : number [ ] = [ ] ;
2337+ let faceIndex : number [ ] = [ ] ;
2338+ let layerIndex : number [ ] = [ ] ;
2339+ let labels : string [ ] = [ ] ;
2340+ let dontCreateTextures = false ;
2341+
2342+ if ( options !== undefined ) {
2343+ generateMipMaps = options . generateMipMaps ?? false ;
2344+ generateDepthBuffer = options . generateDepthBuffer ?? true ;
2345+ generateStencilBuffer = options . generateStencilBuffer ?? false ;
2346+ generateDepthTexture = options . generateDepthTexture ?? false ;
2347+ textureCount = options . textureCount ?? 1 ;
2348+ samples = options . samples ?? 1 ;
2349+ types = options . types || types ;
2350+ samplingModes = options . samplingModes || samplingModes ;
2351+ formats = options . formats || formats ;
2352+ targets = options . targetTypes || targets ;
2353+ faceIndex = options . faceIndex || faceIndex ;
2354+ layerIndex = options . layerIndex || layerIndex ;
2355+ labels = options . labels || labels ;
2356+ dontCreateTextures = options . dontCreateTextures ?? false ;
2357+ }
2358+
2359+ rtWrapper . label = options ?. label ?? "MultiRenderTargetWrapper" ;
2360+
2361+ const width = ( < { width : number ; height : number } > size ) . width ?? < number > size ;
2362+ const height = ( < { width : number ; height : number } > size ) . height ?? < number > size ;
2363+
2364+ const textures : InternalTexture [ ] = [ ] ;
2365+ const attachments : number [ ] = [ ] ;
2366+ const colorHandles : NativeTexture [ ] = [ ] ;
2367+
2368+ for ( let i = 0 ; i < textureCount ; i ++ ) {
2369+ const samplingMode = samplingModes [ i ] || Constants . TEXTURE_TRILINEAR_SAMPLINGMODE ;
2370+ let type = types [ i ] || Constants . TEXTURETYPE_UNSIGNED_BYTE ;
2371+ const format = formats [ i ] || Constants . TEXTUREFORMAT_RGBA ;
2372+ const target = targets [ i ] || Constants . TEXTURE_2D ;
2373+
2374+ attachments . push ( i ) ;
2375+
2376+ // target === -1 marks an attachment with no engine-created texture; dontCreateTextures defers them.
2377+ if ( target === - 1 || dontCreateTextures ) {
2378+ continue ;
2379+ }
2380+
2381+ if ( type === Constants . TEXTURETYPE_FLOAT && ! this . _caps . textureFloat ) {
2382+ type = Constants . TEXTURETYPE_UNSIGNED_BYTE ;
2383+ Logger . Warn ( "Float textures are not supported. Multi render target attachment forced to TEXTURETYPE_UNSIGNED_BYTE type" ) ;
2384+ }
2385+
2386+ const texture = new InternalTexture ( this , InternalTextureSource . MultiRenderTarget ) ;
2387+ const nativeTexture = texture . _hardwareTexture ! . underlyingResource ;
2388+ if ( target !== Constants . TEXTURE_2D ) {
2389+ // Native multi render targets currently only attach 2D color textures; cube / 2D-array
2390+ // attachments are created as 2D so the attachment still exists and mrt.textures[i] is populated.
2391+ Logger . Warn ( "Multi render target attachment target " + target + " is not supported on Native; using a 2D texture." ) ;
2392+ }
2393+ // bgfx auto-generates the mip chain on resolve; avoid the mips + MSAA combo (see createRenderTargetTexture).
2394+ const hasMips = samples > 1 ? false : generateMipMaps ;
2395+ this . _engine . initializeTexture ( nativeTexture , width , height , hasMips , getNativeTextureFormat ( format , type ) , /*renderTarget*/ true , /*srgb*/ false , samples ) ;
2396+ this . _setTextureSampling ( nativeTexture , getNativeSamplingMode ( samplingMode ) ) ;
2397+
2398+ texture . baseWidth = width ;
2399+ texture . baseHeight = height ;
2400+ texture . width = width ;
2401+ texture . height = height ;
2402+ texture . isReady = true ;
2403+ texture . samples = samples ;
2404+ texture . generateMipMaps = generateMipMaps ;
2405+ texture . samplingMode = samplingMode ;
2406+ texture . type = type ;
2407+ texture . format = format ;
2408+ texture . label = labels [ i ] ?? rtWrapper . label + "-Texture" + i ;
2409+
2410+ textures [ i ] = texture ;
2411+ colorHandles . push ( nativeTexture ) ;
2412+ this . _internalTexturesCache . push ( texture ) ;
2413+ }
2414+
2415+ // The native engine creates one framebuffer with all the color attachments (bgfx writes to every
2416+ // attachment of the bound framebuffer, so there is no drawBuffers equivalent to issue per draw).
2417+ if ( colorHandles . length > 0 ) {
2418+ rtWrapper . _framebuffer = this . _engine . createMultiFrameBuffer ( colorHandles , width , height , generateStencilBuffer , generateDepthBuffer || generateDepthTexture , samples ) ;
2419+ }
2420+
2421+ rtWrapper . _generateDepthBuffer = generateDepthBuffer || generateDepthTexture ;
2422+ rtWrapper . _generateStencilBuffer = generateStencilBuffer ;
2423+ rtWrapper . _samples = samples ;
2424+ rtWrapper . _attachments = attachments ;
2425+
2426+ rtWrapper . setTextures ( textures ) ;
2427+ rtWrapper . setLayerAndFaceIndices ( layerIndex , faceIndex ) ;
2428+
2429+ return rtWrapper ;
2430+ }
2431+
2432+ public override bindAttachments ( _attachments : number [ ] ) : void {
2433+ // No-op on Native: bgfx renders to every color attachment of the bound framebuffer, so there is
2434+ // no gl.drawBuffers equivalent to select a subset.
2435+ }
2436+
2437+ public override buildTextureLayout ( textureStatus : boolean [ ] , _backBufferLayout = false ) : number [ ] {
2438+ // Native has no gl draw-buffer enums; return a per-attachment index list (consumers only use the
2439+ // length/order, and bindAttachments is a no-op).
2440+ const result : number [ ] = [ ] ;
2441+ for ( let i = 0 ; i < textureStatus . length ; i ++ ) {
2442+ result . push ( textureStatus [ i ] ? i : - 1 ) ;
2443+ }
2444+ return result ;
2445+ }
2446+
2447+ public override restoreSingleAttachment ( ) : void {
2448+ // No-op on Native (see bindAttachments).
2449+ }
2450+
2451+ public override restoreSingleAttachmentForRenderTarget ( ) : void {
2452+ // No-op on Native (see bindAttachments).
2453+ }
2454+
2455+ public override generateMipMapsMultiFramebuffer ( _texture : RenderTargetWrapper ) : void {
2456+ // No-op on Native: bgfx auto-generates mips on render-target resolve (as for 2D/cube RTTs).
2457+ }
2458+
2459+ public override resolveMultiFramebuffer ( _texture : RenderTargetWrapper ) : void {
2460+ // No-op on Native: bgfx resolves MSAA render targets automatically.
2461+ }
2462+
2463+ public override unBindMultiColorAttachmentFramebuffer ( _rtWrapper : RenderTargetWrapper , _disableGenerateMipMaps = false , onBeforeUnbind ?: ( ) => void ) : void {
2464+ this . _currentRenderTarget = null ;
2465+ if ( onBeforeUnbind ) {
2466+ onBeforeUnbind ( ) ;
2467+ }
2468+ this . _bindUnboundFramebuffer ( null ) ;
2469+ }
2470+
2471+ public override updateMultipleRenderTargetTextureSampleCount ( rtWrapper : Nullable < RenderTargetWrapper > , samples : number , _initializeBuffers = true ) : number {
2472+ // Native MSAA is configured at texture creation; just record the requested sample count.
2473+ if ( rtWrapper ) {
2474+ ( rtWrapper as NativeRenderTargetWrapper ) . _samples = samples ;
2475+ }
2476+ return samples ;
2477+ }
2478+
23062479 public override updateRenderTargetTextureSampleCount ( rtWrapper : RenderTargetWrapper , samples : number ) : number {
23072480 if ( rtWrapper . samples === samples ) {
23082481 return samples ;
0 commit comments