Skip to content

Commit

Permalink
Adapt dynamic shaders to support layered rendering
Browse files Browse the repository at this point in the history
This commit changes the signature of dynamic shader functions to
support layered rendering. Dynamic shaders now take a framebuffer
signature and topology as input instead of a EffectConfig.

Related changes:
  - Adds Effect.link
  - Deprecates IRuntime.AssembleModule in favor of Effect.link
  - Removes EffectConfig utilities for IFramebufferSignature. Use Effect.link instead.
  • Loading branch information
hyazinthh committed Jun 12, 2024
1 parent 9f5ce83 commit f5b109b
Show file tree
Hide file tree
Showing 15 changed files with 130 additions and 126 deletions.
2 changes: 2 additions & 0 deletions RELEASE_NOTES.md
Original file line number Diff line number Diff line change
Expand Up @@ -4,6 +4,8 @@
- Removed ISurface interface. Was redundant apart from being a common interface for backend surfaces and effects via the FShadeSurface proxy type. Either use IBackendSurface or Surface instead.
- Renamed Surface.FShadeSimple to Surface.Effect
- Renamed Surface.FShade to Surface.Dynamic
- Added support for layered rendering with dynamic shaders. The signature of dynamic shader functions changed from `EffectConfig -> DynamicSurface` to `IFramebufferSignature -> IndexedGeomtryMode -> DynamicSurface`.
- Added Effect.link (use instead of deprecated IRuntime.AssembleModule and removed EffectConfig utilities)

### 5.4.10
- [OpenVR] changed GL texture submit to 2 textures (previously side by side, issue with Quest 3)
Expand Down
6 changes: 3 additions & 3 deletions src/Aardvark.Rendering.GL/Resources/Program.fs
Original file line number Diff line number Diff line change
Expand Up @@ -928,7 +928,7 @@ module ProgramExtensions =
let glsl =
lazy (
try
let module_ = key.layout.Link(key.effect, key.deviceCount, Range1d(-1.0, 1.0), false, key.topology, not x.SupportsLayeredEffects)
let module_ = Effect.link signature key.topology false key.effect
ModuleCompiler.compileGLSL x.FShadeBackend module_
with exn ->
Log.error "%s" exn.Message
Expand All @@ -942,11 +942,11 @@ module ProgramExtensions =
| Success prog -> Success (prog.Interface, AVal.constant prog)
| Error err -> Error err

| Surface.Dynamic create ->
| Surface.Dynamic compile ->
// Use surface reference as key rather than create, since equality is undefined behavior for F# functions
// See F# specification: 6.9.24 Values with Underspecified Object Identity and Type Identity
x.ShaderCache.GetOrAdd(surface, signature, fun _ ->
let (inputLayout, module_) = create (signature.EffectConfig(Range1d(-1.0, 1.0), false))
let (inputLayout, module_) = compile signature topology

let initial = AVal.force module_
let layoutHash = inputLayout.ComputeHash()
Expand Down
3 changes: 1 addition & 2 deletions src/Aardvark.Rendering.GL/Runtime/GeometryPool.fs
Original file line number Diff line number Diff line change
Expand Up @@ -988,9 +988,8 @@ module GeometryPoolData =

let shader =
lazy (
let cfg = signature.EffectConfig(Range1d(-1.0, 1.0), false)
effect
|> Effect.toModule cfg
|> Effect.link signature IndexedGeometryMode.TriangleList false
|> ModuleCompiler.compileGLSL ctx.FShadeBackend
)

Expand Down
12 changes: 6 additions & 6 deletions src/Aardvark.Rendering.GL/Runtime/Runtime.fs
Original file line number Diff line number Diff line change
Expand Up @@ -25,6 +25,10 @@ type Runtime(debug : IDebugConfig) =

member x.ContextLock = ctx.ResourceLock :> IDisposable

member x.ShaderDepthRange = Range1d(-1.0, 1.0)

member x.SupportsLayeredShaderInputs = ctx.SupportsLayeredEffects

member x.DebugConfig = debug

member x.ShaderCachePath
Expand Down Expand Up @@ -88,6 +92,8 @@ type Runtime(debug : IDebugConfig) =
interface IRuntime with

member x.DeviceCount = 1
member x.ShaderDepthRange = x.ShaderDepthRange
member x.SupportsLayeredShaderInputs = x.SupportsLayeredShaderInputs
member x.DebugConfig = x.DebugConfig
member x.ContextLock = x.ContextLock

Expand All @@ -100,9 +106,6 @@ type Runtime(debug : IDebugConfig) =

member x.OnDispose = x.OnDispose

member x.AssembleModule (effect, signature, topology) =
x.AssembleModule(effect, signature, topology)

member x.ResourceManager = manager :> IResourceManager

member x.CreateFramebufferSignature(colorAttachments : Map<int, AttachmentSignature>,
Expand Down Expand Up @@ -394,9 +397,6 @@ type Runtime(debug : IDebugConfig) =

member x.OnDispose = onDispose.Publish

member x.AssembleModule (effect : Effect, signature : IFramebufferSignature, topology : IndexedGeometryMode) =
signature.Link(effect, Range1d(-1.0, 1.0), false, topology, not x.Context.SupportsLayeredEffects)

member x.ResourceManager = manager

member x.CompileRender (signature : IFramebufferSignature, set : aset<IRenderObject>) =
Expand Down
11 changes: 5 additions & 6 deletions src/Aardvark.Rendering.Vulkan/Management/ResourceManager.fs
Original file line number Diff line number Diff line change
Expand Up @@ -1913,13 +1913,12 @@ type ResourceManager(device : Device) =

program.PipelineLayout, resource

member private x.CreateDynamicShaderProgram(key : obj, pass : RenderPass, compile : FShade.EffectConfig -> FShade.EffectInputLayout * aval<FShade.Imperative.Module>) =
member private x.CreateDynamicShaderProgram(key : obj, pass : RenderPass, top : IndexedGeometryMode,
compile : IFramebufferSignature -> IndexedGeometryMode -> DynamicSurface) =
dynamicProgramCache.GetOrCreate(
[key; pass.Layout :> obj],
[key; top :> obj; pass.Layout :> obj],
fun cache key ->
let effectConfig = pass.EffectConfig(FShadeConfig.depthRange, false)

let _, module_ = compile effectConfig
let _, module_ = compile pass top
use initialProgram = device.CreateShaderProgram(AVal.force module_)

let program = new DynamicShaderProgramResource(cache, key, device, initialProgram.PipelineLayout, module_)
Expand All @@ -1936,7 +1935,7 @@ type ResourceManager(device : Device) =
| Surface.Dynamic compile ->
// Use surface itself as key rather than compile function, since equality is undefined behavior for F# functions.
// See F# specification: 6.9.24 Values with Underspecified Object Identity and Type Identity
let program = x.CreateDynamicShaderProgram(data, pass, compile)
let program = x.CreateDynamicShaderProgram(data, pass, top, compile)
program.Layout, program

| Surface.Backend (:? ShaderProgram as program) ->
Expand Down
3 changes: 2 additions & 1 deletion src/Aardvark.Rendering.Vulkan/Resources/ShaderProgram.fs
Original file line number Diff line number Diff line change
Expand Up @@ -576,7 +576,8 @@ module ShaderProgram =
let compile() =
let glsl =
try
key.layout.Link(key.effect, key.deviceCount, FShadeConfig.depthRange, false, key.topology)
key.effect
|> Effect.link pass key.topology false
|> FShade.Imperative.ModuleCompiler.compile FShadeConfig.backend
|> FShade.GLSL.Assembler.assemble FShadeConfig.backend
with exn ->
Expand Down
10 changes: 8 additions & 2 deletions src/Aardvark.Rendering.Vulkan/Runtime/Runtime.fs
Original file line number Diff line number Diff line change
Expand Up @@ -35,6 +35,10 @@ type Runtime(device : Device) as this =

member x.DeviceCount = device.PhysicalDevices.Length

member x.ShaderDepthRange = FShadeConfig.depthRange

member x.SupportsLayeredShaderInputs = true

member x.DebugConfig = debug

member x.CreateStreamingTexture (mipMaps : bool) : IStreamingTexture =
Expand Down Expand Up @@ -513,6 +517,10 @@ type Runtime(device : Device) as this =

member x.DeviceCount = x.DeviceCount

member x.ShaderDepthRange = x.ShaderDepthRange

member x.SupportsLayeredShaderInputs = x.SupportsLayeredShaderInputs

member x.MaxLocalSize = x.MaxLocalSize

member x.CreateComputeShader(shader : FShade.ComputeShader) =
Expand Down Expand Up @@ -545,8 +553,6 @@ type Runtime(device : Device) as this =
x.ReadPixels(src, sem, offset, size)

member x.OnDispose = onDispose.Publish
member x.AssembleModule (effect : FShade.Effect, signature : IFramebufferSignature, topology : IndexedGeometryMode) =
signature.Link(effect, FShadeConfig.depthRange, false, topology)

member x.ResourceManager = failf "not implemented"

Expand Down
119 changes: 51 additions & 68 deletions src/Aardvark.Rendering/Effects/Interop/FShade.fs
Original file line number Diff line number Diff line change
Expand Up @@ -416,74 +416,55 @@ module FShadeInterop =
IndexedGeometryMode.QuadList, InputTopology.Patch 4
]

// Used as part of the key in shader caches
type FramebufferLayout =
{
Samples : int
ColorAttachments : Map<int, AttachmentSignature>
DepthStencilAttachment : Option<TextureFormat>
LayerCount : int
PerLayerUniforms : Set<string>
}
module Effect =

member x.EffectConfig(depthRange : Range1d, flip : bool) =
let outputs =
x.ColorAttachments
|> Map.toList
|> List.map (fun (slot, att) -> string att.Name, att.Type, slot)

{ EffectConfig.ofList outputs with
depthRange = depthRange
flipHandedness = flip
}
let link (signature: IFramebufferSignature) (topology: IndexedGeometryMode) (flipHandedness: bool) (effect: Effect) =
let runtime = signature.Runtime
let depthRange = runtime.ShaderDepthRange
let deviceCount = runtime.DeviceCount
let supportsLayeredShaderInputs = runtime.SupportsLayeredShaderInputs

member x.Link(effect : Effect, deviceCount : int, depthRange : Range1d, flip : bool, top : IndexedGeometryMode, useCustomSemantic : bool) =
let outputs =
x.ColorAttachments
signature.ColorAttachments
|> Map.toList
|> List.map (fun (slot, att) -> string att.Name, att.Type, slot)

let top = toInputTopology top
let top = toInputTopology topology

let config =
{ EffectConfig.ofList outputs with
depthRange = depthRange
flipHandedness = flip
flipHandedness = flipHandedness
}

if deviceCount > 1 then
if x.LayerCount > 1 then
let semantic = Intrinsics.Layer
let customSemantic = if useCustomSemantic then "GeometryInvocationId" else semantic

effect
// TODO: other topologies????
|> Effect.toLayered semantic customSemantic x.LayerCount (x.PerLayerUniforms |> Seq.map (fun n -> n, n) |> Map.ofSeq) top
|> withDeviceIndex deviceCount
|> Effect.toModule config
else
let semantic = Intrinsics.ViewportIndex
let customSemantic = if useCustomSemantic then "GeometryInvocationId" else semantic

effect
// TODO: other topologies????
|> Effect.toLayered semantic customSemantic deviceCount Map.empty top
|> withDeviceIndex deviceCount
|> Effect.toModule config
if deviceCount = 1 && signature.LayerCount = 1 then
effect |> Effect.toModule config
else
if x.LayerCount > 1 then
let semantic = Intrinsics.Layer
let customSemantic = if useCustomSemantic then "GeometryInvocationId" else semantic

effect
// TODO: other topologies????
|> Effect.toLayered semantic customSemantic x.LayerCount (x.PerLayerUniforms |> Seq.map (fun n -> n, n) |> Map.ofSeq) top
|> Effect.toModule config
else
effect |> Effect.toModule config
let semantic, layerCount, perLayerUniforms =
if deviceCount > 1 && signature.LayerCount = 1 then
Intrinsics.ViewportIndex, deviceCount, Map.empty
else
let uniforms = signature.PerLayerUniforms |> Seq.map (fun n -> n, n) |> Map.ofSeq
Intrinsics.Layer, signature.LayerCount, uniforms

member x.Link(effect : Effect, deviceCount : int, depthRange : Range1d, flip : bool, top : IndexedGeometryMode) =
x.Link(effect, deviceCount, depthRange, flip, top, false)
let customSemantic =
if supportsLayeredShaderInputs then semantic else"GeometryInvocationId"

effect
// TODO: other topologies????
|> Effect.toLayered semantic customSemantic layerCount perLayerUniforms top
|> withDeviceIndex deviceCount
|> Effect.toModule config

// Used as part of the key in shader caches
type FramebufferLayout =
{
Samples : int
ColorAttachments : Map<int, AttachmentSignature>
DepthStencilAttachment : Option<TextureFormat>
LayerCount : int
PerLayerUniforms : Set<string>
}

type IFramebufferSignature with
member x.Layout : FramebufferLayout =
Expand All @@ -495,23 +476,13 @@ module FShadeInterop =
PerLayerUniforms = x.PerLayerUniforms
}

member x.EffectConfig(depthRange : Range1d, flip : bool) =
x.Layout.EffectConfig(depthRange, flip)

member x.Link(effect : Effect, depthRange : Range1d, flip : bool, top : IndexedGeometryMode) =
x.Layout.Link(effect, x.Runtime.DeviceCount, depthRange, flip, top, false)

member x.Link(effect : Effect, depthRange : Range1d, flip : bool, top : IndexedGeometryMode, useCustomSemantic : bool) =
x.Layout.Link(effect, x.Runtime.DeviceCount, depthRange, flip, top, useCustomSemantic)


let inline toEffect a = Effect.ofFunction a

module Surface =
let effectPool (effects : Effect[]) (active : aval<int>) =
let compile (cfg : EffectConfig) =
let compile (signature: IFramebufferSignature) (topology: IndexedGeometryMode) =
let layout, modules =
let modules = effects |> Array.map (Effect.toModule cfg)
let modules = effects |> Array.map (Effect.link signature topology false)
let layout = EffectInputLayout.ofModules modules
layout, modules |> Array.map (EffectInputLayout.apply layout)

Expand All @@ -523,7 +494,7 @@ module FShadeInterop =
| Some hooked ->
hooked |> AVal.map (fun e ->
try
let m = e |> Effect.toModule cfg |> EffectInputLayout.apply layout
let m = e |> Effect.link signature topology false |> EffectInputLayout.apply layout
m.Entries |> ignore // Evaluate lazy entries here to trigger potential exceptions
m
with exn ->
Expand All @@ -541,4 +512,16 @@ module FShadeInterop =

layout, current

Surface.Dynamic compile
Surface.Dynamic compile

type RuntimeCommand with
static member Geometries(effects : FShade.Effect[], activeEffect : aval<int>, pipeline : PipelineState, geometries : aset<Geometry>) =
let surface = Surface.effectPool effects activeEffect
RuntimeCommand.GeometriesCmd(surface, pipeline, geometries)

[<Extension; AbstractClass; Sealed>]
type RuntimeFShadeInteropExtensions() =

[<Extension; Obsolete("Use Effect.link from auto-open module FShadeInterop instead.")>]
static member AssembleModule (runtime: IRuntime, effect: Effect, signature: IFramebufferSignature, topology: IndexedGeometryMode) =
Effect.link signature topology false effect
8 changes: 8 additions & 0 deletions src/Aardvark.Rendering/Resources/Framebuffer/Framebuffer.fs
Original file line number Diff line number Diff line change
Expand Up @@ -44,6 +44,14 @@ and IFramebufferRuntime =

abstract member DeviceCount : int

/// Target depth range of shaders.
/// The final depth will be mapped from [-1, 1] to the target range.
abstract member ShaderDepthRange : Range1d

/// Returns whether the runtime supports shader inputs for layered and multiviewport rendering.
/// If false, shaders must use custom inputs.
abstract member SupportsLayeredShaderInputs : bool

///<summary>Creates a framebuffer signature with the given attachment signatures.</summary>
///<param name="colorAttachments">The color attachment signatures. The keys determine the slot of the corrsponding attachment.</param>
///<param name="depthStencilAttachment">The optional depth-stencil attachment signature.</param>
Expand Down
2 changes: 0 additions & 2 deletions src/Aardvark.Rendering/Runtime/Runtime.fs
Original file line number Diff line number Diff line change
Expand Up @@ -76,8 +76,6 @@ and IRuntime =
[<Obsolete("To be removed.")>]
abstract member ResourceManager : IResourceManager

abstract member AssembleModule : FShade.Effect * IFramebufferSignature * IndexedGeometryMode -> FShade.Imperative.Module

abstract member PrepareEffect : IFramebufferSignature * FShade.Effect -> IBackendSurface

abstract member PrepareRenderObject : IFramebufferSignature * IRenderObject -> IPreparedRenderObject
Expand Down
11 changes: 0 additions & 11 deletions src/Aardvark.Rendering/Runtime/RuntimeCommand.fs
Original file line number Diff line number Diff line change
Expand Up @@ -38,17 +38,6 @@ type RuntimeCommand =
static member Geometries(surface : FShade.Effect, pipeline : PipelineState, geometries : aset<IndexedGeometry>) =
RuntimeCommand.GeometriesSimpleCmd(surface, pipeline, geometries)

static member Geometries(effects : FShade.Effect[], activeEffect : aval<int>, pipeline : PipelineState, geometries : aset<Geometry>) =
let surface =
Surface.Dynamic (fun cfg ->
let modules = effects |> Array.map (FShade.Effect.toModule cfg)
let signature = FShade.EffectInputLayout.ofModules modules
let modules = modules |> Array.map (FShade.EffectInputLayout.apply signature)

signature, activeEffect |> AVal.map (Array.get modules)
)
RuntimeCommand.GeometriesCmd(surface, pipeline, geometries)

static member LodTree(surface : Surface, pipeline : PipelineState, geometries : LodTreeLoader<Geometry>) =
RuntimeCommand.LodTreeCmd(surface, pipeline, geometries)

Expand Down
15 changes: 8 additions & 7 deletions src/Aardvark.Rendering/Surfaces/Surface.fs
Original file line number Diff line number Diff line change
Expand Up @@ -13,26 +13,27 @@ type DynamicSurface = EffectInputLayout * aval<Imperative.Module>
[<RequireQualifiedAccess; ReferenceEquality>]
type Surface =
| Effect of effect: Effect
| Dynamic of compile: (EffectConfig -> DynamicSurface)
| Dynamic of compile: (IFramebufferSignature -> IndexedGeometryMode -> DynamicSurface)
| Backend of surface: IBackendSurface
| None

[<Obsolete("Use Surface.Effect instead.")>]
static member FShadeSimple(effect: Effect) = Surface.Effect effect

[<Obsolete("Use Surface.Dynamic instead.")>]
static member FShade(compile: EffectConfig -> DynamicSurface) = Surface.Dynamic compile
static member FShade(compile: IFramebufferSignature -> IndexedGeometryMode -> DynamicSurface) = Surface.Dynamic compile

[<CompilationRepresentation(CompilationRepresentationFlags.ModuleSuffix)>]
module Surface =

[<Sealed; AbstractClass>]
type Converter() =
static member inline ToSurface(surface: Surface) = surface
static member inline ToSurface(effect: Effect) = Surface.Effect effect
static member inline ToSurface(effects: #seq<Effect>) = Surface.Effect <| FShade.Effect.compose effects
static member inline ToSurface(compile: EffectConfig -> DynamicSurface) = Surface.Dynamic compile
static member inline ToSurface(surface: IBackendSurface) = Surface.Backend surface
static member inline ToSurface(surface: Surface) = surface
static member inline ToSurface(effect: Effect) = Surface.Effect effect
static member inline ToSurface(effects: #seq<Effect>) = Surface.Effect <| FShade.Effect.compose effects
static member inline ToSurface(compile) = Surface.Dynamic compile
static member inline ToSurface(compile: Func<_, _, _>) = Surface.Dynamic (fun s t -> compile.Invoke(s, t))
static member inline ToSurface(surface: IBackendSurface) = Surface.Backend surface

let inline private toSurface (_ : ^Z) (data: ^T) =
((^Z or ^T) : (static member ToSurface : ^T -> Surface)(data))
Expand Down
Loading

0 comments on commit f5b109b

Please sign in to comment.