Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
15 changes: 13 additions & 2 deletions packages/dev/core/src/Engines/Native/nativeInterfaces.ts
Original file line number Diff line number Diff line change
Expand Up @@ -61,7 +61,17 @@ export interface INativeEngine {
getAttributes(shaderProgram: NativeProgram, attributeNames: string[]): number[];

createTexture(): NativeTexture;
initializeTexture(texture: NativeTexture, width: number, height: number, hasMips: boolean, format: number, renderTarget: boolean, srgb: boolean, samples: number): void;
initializeTexture(
texture: NativeTexture,
width: number,
height: number,
hasMips: boolean,
format: number,
renderTarget: boolean,
srgb: boolean,
samples: number,
isCube?: boolean
): void;
loadTexture(texture: NativeTexture, data: ArrayBufferView, generateMips: boolean, invertY: boolean, srgb: boolean, onSuccess: () => void, onError: () => void): void;
loadRawTexture(texture: NativeTexture, data: ArrayBufferView, width: number, height: number, format: number, generateMips: boolean, invertY: boolean): void;
loadRawTexture2DArray(
Expand Down Expand Up @@ -100,7 +110,8 @@ export interface INativeEngine {
height: number,
generateStencilBuffer: boolean,
generateDepthBuffer: boolean,
samples: number
samples: number,
layer?: number
): NativeFramebuffer;

getRenderWidth(): number;
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -11,6 +11,9 @@ export class NativeRenderTargetWrapper extends RenderTargetWrapper {
private __framebuffer: Nullable<NativeFramebuffer> = null;
// eslint-disable-next-line @typescript-eslint/naming-convention
private __framebufferDepthStencil: Nullable<NativeFramebuffer> = null;
// Per-face framebuffers for cube render targets (index = cube face 0..5).
// eslint-disable-next-line @typescript-eslint/naming-convention
private __framebuffers: Nullable<NativeFramebuffer[]> = null;

public get _framebuffer(): Nullable<NativeFramebuffer> {
return this.__framebuffer;
Expand All @@ -23,6 +26,21 @@ export class NativeRenderTargetWrapper extends RenderTargetWrapper {
this.__framebuffer = framebuffer;
}

public get _framebuffers(): Nullable<NativeFramebuffer[]> {
return this.__framebuffers;
}

public set _framebuffers(framebuffers: Nullable<NativeFramebuffer[]>) {
if (this.__framebuffers) {
for (const framebuffer of this.__framebuffers) {
this._engine._releaseFramebufferObjects(framebuffer);
}
}
this.__framebuffers = framebuffers;
// Keep _framebuffer pointing at face 0 so single-target code paths still work.
this.__framebuffer = framebuffers ? framebuffers[0] : null;
}

public get _framebufferDepthStencil(): Nullable<NativeFramebuffer> {
return this.__framebufferDepthStencil;
}
Expand All @@ -40,7 +58,13 @@ export class NativeRenderTargetWrapper extends RenderTargetWrapper {
}

public override dispose(disposeOnlyFramebuffers = false): void {
this._framebuffer = null;
if (this.__framebuffers) {
// Releases all six per-face framebuffers (face 0 is aliased by __framebuffer, so
// clear that alias here without releasing it again).
this._framebuffers = null;
} else {
this._framebuffer = null;
}
Comment thread
bkaradzic-microsoft marked this conversation as resolved.
this._framebufferDepthStencil = null;

super.dispose(disposeOnlyFramebuffers);
Expand Down
143 changes: 130 additions & 13 deletions packages/dev/core/src/Engines/thinNativeEngine.pure.ts
Original file line number Diff line number Diff line change
Expand Up @@ -2345,6 +2345,90 @@ export class ThinNativeEngine extends ThinEngine {
return rtWrapper;
}

public override createRenderTargetCubeTexture(size: number, options?: RenderTargetCreationOptions): RenderTargetWrapper {
const rtWrapper = this._createHardwareRenderTargetWrapper(false, true, size) as NativeRenderTargetWrapper;

let generateDepthBuffer = true;
let generateStencilBuffer = false;
let generateMipMaps = false;
let type = Constants.TEXTURETYPE_UNSIGNED_BYTE;
let samplingMode = Constants.TEXTURE_TRILINEAR_SAMPLINGMODE;
let format = Constants.TEXTUREFORMAT_RGBA;
let samples = 1;
let label: string | undefined;
if (options !== undefined && typeof options === "object") {
generateDepthBuffer = options.generateDepthBuffer ?? true;
generateStencilBuffer = !!options.generateStencilBuffer;
generateMipMaps = !!options.generateMipMaps;
type = options.type ?? Constants.TEXTURETYPE_UNSIGNED_BYTE;
samplingMode = options.samplingMode ?? Constants.TEXTURE_TRILINEAR_SAMPLINGMODE;
format = options.format ?? Constants.TEXTUREFORMAT_RGBA;
samples = options.samples ?? 1;
label = options.label;
}

// Match _createInternalTexture: float/half-float RTTs that the platform can't linearly filter fall
// back to NEAREST so the cube RTT never carries an unsupported sampling mode.
if (type === Constants.TEXTURETYPE_FLOAT && !this._caps.textureFloatLinearFiltering) {
samplingMode = Constants.TEXTURE_NEAREST_SAMPLINGMODE;
} else if (type === Constants.TEXTURETYPE_HALF_FLOAT && !this._caps.textureHalfFloatLinearFiltering) {
samplingMode = Constants.TEXTURE_NEAREST_SAMPLINGMODE;
}
if (type === Constants.TEXTURETYPE_FLOAT && !this._caps.textureFloat) {
type = Constants.TEXTURETYPE_UNSIGNED_BYTE;
Logger.Warn("Float textures are not supported. Type forced to TEXTURETYPE_UNSIGNED_BYTE");
}

const texture = new InternalTexture(this, InternalTextureSource.RenderTarget);
texture.isCube = true;
texture.baseWidth = size;
texture.baseHeight = size;
texture.width = size;
texture.height = size;
texture.isReady = true;
texture.samples = samples;
texture.generateMipMaps = generateMipMaps;
texture.samplingMode = samplingMode;
texture.type = type;
texture.format = format;
Comment thread
bkaradzic-microsoft marked this conversation as resolved.
texture.label = label;

const nativeTexture = texture._hardwareTexture!.underlyingResource;
const nativeTextureFormat = getNativeTextureFormat(format, type);
// See the createRenderTargetTexture MSAA/mips note: avoid the mips + samples combo on bgfx.
const hasMips = samples > 1 ? false : generateMipMaps;
this._engine.initializeTexture(nativeTexture, size, size, hasMips, nativeTextureFormat, /*renderTarget*/ true, /*srgb*/ false, samples, /*isCube*/ true);
Comment thread
bkaradzic-microsoft marked this conversation as resolved.
this._setTextureSampling(nativeTexture, getNativeSamplingMode(samplingMode));

// The native engine cannot render to all six faces through one framebuffer, so create one
// framebuffer per face (the C++ side binds the matching cube layer); bindFramebuffer(faceIndex)
// then selects the right one.
const framebuffers: NativeFramebuffer[] = [];
for (let face = 0; face < 6; face++) {
framebuffers.push(this._engine.createFrameBuffer(nativeTexture, size, size, generateStencilBuffer, generateDepthBuffer, samples, face));
}

rtWrapper._framebuffers = framebuffers;
rtWrapper._generateDepthBuffer = generateDepthBuffer;
rtWrapper._generateStencilBuffer = generateStencilBuffer;
rtWrapper._samples = samples;

rtWrapper.setTextures(texture);
Comment thread
bkaradzic-microsoft marked this conversation as resolved.

// Track the hand-built cube RTT texture the same way _createInternalTexture tracks 2D textures so it
// participates in engine-wide lifecycle management (dispose iteration, context rebuild, stats).
this._internalTexturesCache.push(texture);

return rtWrapper;
}

public override generateMipMapsForCubemap(_texture: InternalTexture, _unbind = true): void {
// The WebGL path rebinds gl.TEXTURE_CUBE_MAP and calls gl.generateMipmap; both deref _gl, which is
// null on Native. bgfx auto-generates the mip chain when a render target texture created with mips is
// resolved (the same way 2D RTTs get their mips here -- unBindFramebuffer issues no explicit mipgen),
// so this is a no-op on Native.
}

public override updateRenderTargetTextureSampleCount(rtWrapper: RenderTargetWrapper, samples: number): number {
if (rtWrapper.samples === samples) {
return samples;
Expand Down Expand Up @@ -2390,19 +2474,51 @@ export class ThinNativeEngine extends ThinEngine {
// underlying bgfx resource has 1 mip level. Remove this guard once a fixed bgfx is in stable BN npm.
const hasMips = samples > 1 ? false : texture.generateMipMaps;
const nativeTextureFormat = getNativeTextureFormat(texture.format, texture.type);
this._engine.initializeTexture(nativeTexture, texture.baseWidth, texture.baseHeight, hasMips, nativeTextureFormat, /*renderTarget*/ true, texture._useSRGBBuffer, samples);

// NativeRenderTargetWrapper._framebuffer setter releases the old framebuffer before assigning,
// so no manual _releaseFramebufferObjects call is needed (and would double-delete the handle).
nativeRTWrapper._framebuffer = this._engine.createFrameBuffer(
const isCube = texture.isCube;
this._engine.initializeTexture(
nativeTexture,
texture.baseWidth,
texture.baseHeight,
rtWrapper._generateStencilBuffer,
rtWrapper._generateDepthBuffer,
samples
hasMips,
nativeTextureFormat,
/*renderTarget*/ true,
texture._useSRGBBuffer,
samples,
isCube
);

if (isCube) {
// Cube RTTs render through one framebuffer per face (see createRenderTargetCubeTexture). The
// underlying bgfx handle was just rotated, so recreate all six attachments; the _framebuffers
// setter releases the stale ones and keeps _framebuffer aliased to face 0 for single-target paths.
const framebuffers: NativeFramebuffer[] = [];
for (let face = 0; face < 6; face++) {
framebuffers.push(
this._engine.createFrameBuffer(
nativeTexture,
texture.baseWidth,
texture.baseHeight,
rtWrapper._generateStencilBuffer,
rtWrapper._generateDepthBuffer,
samples,
face
)
);
}
nativeRTWrapper._framebuffers = framebuffers;
} else {
// NativeRenderTargetWrapper._framebuffer setter releases the old framebuffer before assigning,
// so no manual _releaseFramebufferObjects call is needed (and would double-delete the handle).
nativeRTWrapper._framebuffer = this._engine.createFrameBuffer(
nativeTexture,
texture.baseWidth,
texture.baseHeight,
rtWrapper._generateStencilBuffer,
rtWrapper._generateDepthBuffer,
samples
);
}

rtWrapper._samples = samples;
texture.samples = samples;
return samples;
Expand All @@ -2426,15 +2542,16 @@ export class ThinNativeEngine extends ThinEngine {

this._currentRenderTarget = texture;

if (faceIndex) {
throw new Error("Cuboid frame buffers are not yet supported in NativeEngine.");
}

if (requiredWidth || requiredHeight) {
throw new Error("Required width/height for frame buffers not yet supported in NativeEngine.");
}

if (nativeRTWrapper._framebufferDepthStencil) {
if (nativeRTWrapper._framebuffers) {
// Cube render target: bind the framebuffer for the requested face.
this._bindUnboundFramebuffer(nativeRTWrapper._framebuffers[faceIndex ?? 0]);
} else if (faceIndex) {
throw new Error("Cuboid frame buffers are not yet supported in NativeEngine.");
} else if (nativeRTWrapper._framebufferDepthStencil) {
this._bindUnboundFramebuffer(nativeRTWrapper._framebufferDepthStencil);
} else {
this._bindUnboundFramebuffer(nativeRTWrapper._framebuffer);
Expand Down
Loading