Skip to content

Commit

Permalink
binding mappings
Browse files Browse the repository at this point in the history
  • Loading branch information
YunHsiao committed Jan 20, 2022
1 parent 29b9ead commit 3cde8a0
Show file tree
Hide file tree
Showing 11 changed files with 123 additions and 29 deletions.
40 changes: 33 additions & 7 deletions cocos/core/gfx/base/define.ts
Original file line number Diff line number Diff line change
Expand Up @@ -89,6 +89,7 @@ export enum API {
GLES3,
METAL,
VULKAN,
NVN,
WEBGL,
WEBGL2,
WEBGPU,
Expand Down Expand Up @@ -953,19 +954,44 @@ export class Color {
}
}

/**
* For non-vulkan backends, to maintain compatibility and maximize
* descriptor cache-locality, descriptor-set-based binding numbers need
* to be mapped to backend-specific bindings based on maximum limit
* of available descriptor slots in each set.
*
* The GFX layer assumes the binding numbers for each descriptor type inside each set
* are guaranteed to be consecutive, so the mapping procedure is reduced
* to a simple shifting operation. This data structure specifies the
* capacity for each descriptor type in each set.
*
* The `setIndices` field defines the binding ordering between different sets.
* The last set index is treated as the 'flexible set', whose capacity is dynamically
* assigned based on the total available descriptor slots on the runtime device.
*/
export class BindingMappingInfo {
declare private _token: never; // to make sure all usages must be an instance of this exact class, not assembled from plain object

constructor (
public bufferOffsets: number[] = [],
public samplerOffsets: number[] = [],
public flexibleSet: number = 0,
public maxBlockCounts: number[] = [0],
public maxSamplerTextureCounts: number[] = [0],
public maxSamplerCounts: number[] = [0],
public maxTextureCounts: number[] = [0],
public maxBufferCounts: number[] = [0],
public maxImageCounts: number[] = [0],
public maxSubpassInputCounts: number[] = [0],
public setIndices: number[] = [0],
) {}

public copy (info: Readonly<BindingMappingInfo>) {
this.bufferOffsets = info.bufferOffsets.slice();
this.samplerOffsets = info.samplerOffsets.slice();
this.flexibleSet = info.flexibleSet;
this.maxBlockCounts = info.maxBlockCounts.slice();
this.maxSamplerTextureCounts = info.maxSamplerTextureCounts.slice();
this.maxSamplerCounts = info.maxSamplerCounts.slice();
this.maxTextureCounts = info.maxTextureCounts.slice();
this.maxBufferCounts = info.maxBufferCounts.slice();
this.maxImageCounts = info.maxImageCounts.slice();
this.maxSubpassInputCounts = info.maxSubpassInputCounts.slice();
this.setIndices = info.setIndices.slice();
return this;
}
}
Expand Down Expand Up @@ -1712,7 +1738,7 @@ export class QueryPoolInfo {

constructor (
public type: QueryType = QueryType.OCCLUSION,
public maxQueryObjects: number = 65536,
public maxQueryObjects: number = 32767,
public forceWait: boolean = true,
) {}

Expand Down
2 changes: 0 additions & 2 deletions cocos/core/gfx/empty/empty-device.ts
Original file line number Diff line number Diff line change
Expand Up @@ -67,8 +67,6 @@ export class EmptyDevice extends Device {
this._gfxAPI = API.UNKNOWN;

this._bindingMappingInfo = info.bindingMappingInfo;
if (!this._bindingMappingInfo.bufferOffsets.length) this._bindingMappingInfo.bufferOffsets.push(0);
if (!this._bindingMappingInfo.samplerOffsets.length) this._bindingMappingInfo.samplerOffsets.push(0);

this._queue = this.createQueue(new QueueInfo(QueueType.GRAPHICS));
this._cmdBuff = this.createCommandBuffer(new CommandBufferInfo(this._queue));
Expand Down
2 changes: 1 addition & 1 deletion cocos/core/gfx/webgl/webgl-command-buffer.ts
Original file line number Diff line number Diff line change
Expand Up @@ -64,7 +64,7 @@ export class WebGLCommandBuffer extends CommandBuffer {
this._type = info.type;
this._queue = info.queue;

const setCount = WebGLDeviceManager.instance.bindingMappingInfo.bufferOffsets.length;
const setCount = WebGLDeviceManager.instance.bindingMappings.blockOffsets.length;
for (let i = 0; i < setCount; i++) {
this._curGPUDescriptorSets.push(null!);
}
Expand Down
8 changes: 4 additions & 4 deletions cocos/core/gfx/webgl/webgl-commands.ts
Original file line number Diff line number Diff line change
Expand Up @@ -1432,12 +1432,12 @@ export function WebGLCmdFuncCreateShader (device: WebGLDevice, gpuShader: IWebGL
// texture unit index mapping optimization
const glActiveSamplers: IWebGLGPUUniformSamplerTexture[] = [];
const glActiveSamplerLocations: WebGLUniformLocation[] = [];
const { bindingMappingInfo } = device;
const { bindingMappings } = device;
const { texUnitCacheMap } = device.stateCache;

let flexibleSetBaseOffset = 0;
for (let i = 0; i < gpuShader.blocks.length; ++i) {
if (gpuShader.blocks[i].set === bindingMappingInfo.flexibleSet) {
if (gpuShader.blocks[i].set === bindingMappings.flexibleSet) {
flexibleSetBaseOffset++;
}
}
Expand All @@ -1451,8 +1451,8 @@ export function WebGLCmdFuncCreateShader (device: WebGLDevice, gpuShader: IWebGL
glActiveSamplerLocations.push(glLoc);
}
if (texUnitCacheMap[sampler.name] === undefined) {
let binding = sampler.binding + bindingMappingInfo.samplerOffsets[sampler.set] + arrayOffset;
if (sampler.set === bindingMappingInfo.flexibleSet) { binding -= flexibleSetBaseOffset; }
let binding = sampler.binding + bindingMappings.samplerTextureOffsets[sampler.set] + arrayOffset;
if (sampler.set === bindingMappings.flexibleSet) { binding -= flexibleSetBaseOffset; }
texUnitCacheMap[sampler.name] = binding % device.capabilities.maxTextureUnits;
arrayOffset += sampler.count - 1;
}
Expand Down
32 changes: 29 additions & 3 deletions cocos/core/gfx/webgl/webgl-device.ts
Original file line number Diff line number Diff line change
Expand Up @@ -64,6 +64,7 @@ import { TextureBarrier } from '../base/states/texture-barrier';
import { debug } from '../../platform/debug';
import { Swapchain } from '../base/swapchain';
import { WebGLDeviceManager } from './webgl-define';
import { IWebGLBindingMapping } from './webgl-gpu-objects';

export class WebGLDevice extends Device {
get gl () {
Expand All @@ -86,16 +87,41 @@ export class WebGLDevice extends Device {
return this._swapchain!.nullTexCube;
}

get bindingMappings () {
return this._bindingMappings!;
}

private _swapchain: WebGLSwapchain | null = null;
private _context: WebGLRenderingContext | null = null;
private _bindingMappings: IWebGLBindingMapping | null = null;

public initialize (info: DeviceInfo): boolean {
WebGLDeviceManager.setInstance(this);
this._gfxAPI = API.WEBGL;

this._bindingMappingInfo = info.bindingMappingInfo;
if (!this._bindingMappingInfo.bufferOffsets.length) this._bindingMappingInfo.bufferOffsets.push(0);
if (!this._bindingMappingInfo.samplerOffsets.length) this._bindingMappingInfo.samplerOffsets.push(0);
const mapping = this._bindingMappingInfo = info.bindingMappingInfo;
const blockOffsets: number[] = [];
const samplerTextureOffsets: number[] = [];
const firstSet = mapping.setIndices[0];
blockOffsets[firstSet] = 0;
samplerTextureOffsets[firstSet] = 0;
for (let i = 1; i < mapping.setIndices.length; ++i) {
const curSet = mapping.setIndices[i];
const prevSet = mapping.setIndices[i - 1];
// accumulate the per set offset according to the specified capacity
blockOffsets[curSet] = mapping.maxBlockCounts[prevSet] + blockOffsets[prevSet];
samplerTextureOffsets[curSet] = mapping.maxSamplerTextureCounts[prevSet] + samplerTextureOffsets[prevSet];
}
for (let i = 0; i < mapping.setIndices.length; ++i) {
const curSet = mapping.setIndices[i];
// textures always come after UBOs
samplerTextureOffsets[curSet] -= mapping.maxBlockCounts[curSet];
}
this._bindingMappings = {
blockOffsets,
samplerTextureOffsets,
flexibleSet: mapping.setIndices[mapping.setIndices.length - 1],
};

const gl = this._context = getContext(Device.canvas);

Expand Down
6 changes: 6 additions & 0 deletions cocos/core/gfx/webgl/webgl-gpu-objects.ts
Original file line number Diff line number Diff line change
Expand Up @@ -102,6 +102,12 @@ export interface IWebGLGPUUniformInfo {
isDirty: boolean;
}

export interface IWebGLBindingMapping {
blockOffsets: number[];
samplerTextureOffsets: number[];
flexibleSet: number;
}

export interface IWebGLGPUBufferView {
gpuBuffer: IWebGLGPUBuffer;
offset: number;
Expand Down
2 changes: 1 addition & 1 deletion cocos/core/gfx/webgl2/webgl2-command-buffer.ts
Original file line number Diff line number Diff line change
Expand Up @@ -76,7 +76,7 @@ export class WebGL2CommandBuffer extends CommandBuffer {
this._type = info.type;
this._queue = info.queue;

const setCount = WebGL2DeviceManager.instance.bindingMappingInfo.bufferOffsets.length;
const setCount = WebGL2DeviceManager.instance.bindingMappings.blockOffsets.length;
for (let i = 0; i < setCount; i++) {
this._curGPUDescriptorSets.push(null!);
}
Expand Down
8 changes: 4 additions & 4 deletions cocos/core/gfx/webgl2/webgl2-commands.ts
Original file line number Diff line number Diff line change
Expand Up @@ -1552,7 +1552,7 @@ export function WebGL2CmdFuncCreateShader (device: WebGL2Device, gpuShader: IWeb
// blockIdx = gl.getUniformBlockIndex(gpuShader.glProgram, blockName);
blockIdx = b;
blockSize = gl.getActiveUniformBlockParameter(gpuShader.glProgram, blockIdx, gl.UNIFORM_BLOCK_DATA_SIZE);
const glBinding = block.binding + (device.bindingMappingInfo.bufferOffsets[block.set] || 0);
const glBinding = block.binding + (device.bindingMappings.blockOffsets[block.set] || 0);

gl.uniformBlockBinding(gpuShader.glProgram, blockIdx, glBinding);

Expand Down Expand Up @@ -1603,7 +1603,7 @@ export function WebGL2CmdFuncCreateShader (device: WebGL2Device, gpuShader: IWeb

let flexibleSetBaseOffset = 0;
for (let i = 0; i < gpuShader.blocks.length; ++i) {
if (gpuShader.blocks[i].set === device.bindingMappingInfo.flexibleSet) {
if (gpuShader.blocks[i].set === device.bindingMappings.flexibleSet) {
flexibleSetBaseOffset++;
}
}
Expand All @@ -1618,8 +1618,8 @@ export function WebGL2CmdFuncCreateShader (device: WebGL2Device, gpuShader: IWeb
glActiveSamplerLocations.push(glLoc);
}
if (texUnitCacheMap[sampler.name] === undefined) {
let binding = sampler.binding + device.bindingMappingInfo.samplerOffsets[sampler.set] + arrayOffset;
if (sampler.set === device.bindingMappingInfo.flexibleSet) { binding -= flexibleSetBaseOffset; }
let binding = sampler.binding + device.bindingMappings.samplerTextureOffsets[sampler.set] + arrayOffset;
if (sampler.set === device.bindingMappings.flexibleSet) { binding -= flexibleSetBaseOffset; }
texUnitCacheMap[sampler.name] = binding % device.capabilities.maxTextureUnits;
arrayOffset += sampler.count - 1;
}
Expand Down
32 changes: 29 additions & 3 deletions cocos/core/gfx/webgl2/webgl2-device.ts
Original file line number Diff line number Diff line change
Expand Up @@ -64,6 +64,7 @@ import { TextureBarrier } from '../base/states/texture-barrier';
import { debug } from '../../platform/debug';
import { Swapchain } from '../base/swapchain';
import { WebGL2DeviceManager } from './webgl2-define';
import { IWebGL2BindingMapping } from './webgl2-gpu-objects';

export class WebGL2Device extends Device {
get gl () {
Expand All @@ -86,16 +87,41 @@ export class WebGL2Device extends Device {
return this._swapchain!.nullTexCube;
}

get bindingMappings () {
return this._bindingMappings!;
}

private _swapchain: WebGL2Swapchain | null = null;
private _context: WebGL2RenderingContext | null = null;
private _bindingMappings: IWebGL2BindingMapping | null = null;

public initialize (info: DeviceInfo): boolean {
WebGL2DeviceManager.setInstance(this);
this._gfxAPI = API.WEBGL2;

this._bindingMappingInfo = info.bindingMappingInfo;
if (!this._bindingMappingInfo.bufferOffsets.length) this._bindingMappingInfo.bufferOffsets.push(0);
if (!this._bindingMappingInfo.samplerOffsets.length) this._bindingMappingInfo.samplerOffsets.push(0);
const mapping = this._bindingMappingInfo = info.bindingMappingInfo;
const blockOffsets: number[] = [];
const samplerTextureOffsets: number[] = [];
const firstSet = mapping.setIndices[0];
blockOffsets[firstSet] = 0;
samplerTextureOffsets[firstSet] = 0;
for (let i = 1; i < mapping.setIndices.length; ++i) {
const curSet = mapping.setIndices[i];
const prevSet = mapping.setIndices[i - 1];
// accumulate the per set offset according to the specified capacity
blockOffsets[curSet] = mapping.maxBlockCounts[prevSet] + blockOffsets[prevSet];
samplerTextureOffsets[curSet] = mapping.maxSamplerTextureCounts[prevSet] + samplerTextureOffsets[prevSet];
}
for (let i = 0; i < mapping.setIndices.length; ++i) {
const curSet = mapping.setIndices[i];
// textures always come after UBOs
samplerTextureOffsets[curSet] -= mapping.maxBlockCounts[curSet];
}
this._bindingMappings = {
blockOffsets,
samplerTextureOffsets,
flexibleSet: mapping.setIndices[mapping.setIndices.length - 1],
};

const gl = this._context = getContext(Device.canvas);

Expand Down
6 changes: 6 additions & 0 deletions cocos/core/gfx/webgl2/webgl2-gpu-objects.ts
Original file line number Diff line number Diff line change
Expand Up @@ -92,6 +92,12 @@ export class WebGL2IndirectDrawInfos {
}
}

export interface IWebGL2BindingMapping {
blockOffsets: number[];
samplerTextureOffsets: number[];
flexibleSet: number;
}

export interface IWebGL2GPUUniformInfo {
name: string;
type: Type;
Expand Down
14 changes: 10 additions & 4 deletions cocos/core/pipeline/define.ts
Original file line number Diff line number Diff line change
Expand Up @@ -156,10 +156,16 @@ export enum SetIndex {
LOCAL,
}
// parameters passed to GFX Device
export const bindingMappingInfo = new BindingMappingInfo();
bindingMappingInfo.bufferOffsets = [0, GLOBAL_UBO_COUNT + LOCAL_UBO_COUNT, GLOBAL_UBO_COUNT];
bindingMappingInfo.samplerOffsets = [-GLOBAL_UBO_COUNT, GLOBAL_SAMPLER_COUNT + LOCAL_SAMPLER_COUNT, GLOBAL_SAMPLER_COUNT - LOCAL_UBO_COUNT];
bindingMappingInfo.flexibleSet = 1;
export const bindingMappingInfo = new BindingMappingInfo(
[GLOBAL_UBO_COUNT, 0, LOCAL_UBO_COUNT], // Uniform Buffer Counts
[GLOBAL_SAMPLER_COUNT, 0, LOCAL_SAMPLER_COUNT], // Combined Sampler Texture Counts
[0, 0, 0], // Sampler Counts
[0, 0, 0], // Texture Counts
[0, 0, 0], // Storage Buffer Counts
[0, 0, 0], // Storage Image Counts
[0, 0, 0], // Subpass Input Counts
[0, 2, 1], // Set Order Indices
);

/**
* @en The global uniform buffer object
Expand Down

0 comments on commit 3cde8a0

Please sign in to comment.