summaryrefslogtreecommitdiff
path: root/examples/model-viewer/shaders.slang
diff options
context:
space:
mode:
authorTim Foley <tfoleyNV@users.noreply.github.com>2018-08-03 08:39:28 -0700
committerGitHub <noreply@github.com>2018-08-03 08:39:28 -0700
commit68d705f6c805c9b4d31b386e065762e6db13ad18 (patch)
tree97ffc0f24358101222d1bc62ac0c50affc55af12 /examples/model-viewer/shaders.slang
parent5ea746a571ced32a8975eb3a238c562b3d487149 (diff)
Major overhaul of Renderer abstraction, to support a new example (#624)
The original goal here was to bring up a second example program: `model-viewer`. While the existing `hello-world` example is enough to get somebody up to speed with the basics of the Slang API (as a drop-in replacement for `D3DCompile` or similar), it doesn't really show any of the big-picture stuff that Slang is meant to enable. There wasn't any use of D3D12/Vulkan descriptor tables/sets, and there wasn't any use of interfaces, generics, or `ParameterBlock`s in the shader code. The `model-viewer` example addresses these issues. Its shader code involves generics, interfaces, and multiple `ParameterBlock`s, and the host-side code demonstrates a few key things for working with Slang: * There is an application-level abstraction for parameter blocks, that combines the graphics-API descriptor set object with Slang type information * There is a shader cache layer used to look up an appropriate variant of a rendering effect by using parameter block types to "plug in" global type variables * There is a clear separation between the phases of compilation: a first phase that does semantic checking and enables reflection-based allocation of graphics API objects, followed by one or more code generation passes for specialized kernels. This example is certainly not perfect, and it will need to be revamped more going forward. In particular: * The output picture is ugly as sin. We need a plan for how to get this to load better content, perhaps even popping up an error message to note that the required input data isn't present in the basic repository. * The shader code is too simplistic. There isn't any real material variety, and the `IMaterial` abstraction is completely wrong. * The use of parameter blocks is facile because there are no resource parameters right now. Fixing that will likely expose issues around interfacing with Slang's reflection API. * The whole example exposes the issue that Slang's current APIs aren't really designed for the benefit of two-phase compilation (since our many client application has been stuck on one-phase compilation). * Global type parameters are actually a Bad Idea that we only did for compatibility with existing codebases. We should not be showing them off in an example of the Right Way to use Slang, but the language support for type parameters on entry points is still not complete. Of course, the majority of the changes here are *not* inside the example applications, and instead involve a major overhaul of the `Renderer` abstraction that is used for both tests and examples. The main thrust of the change is to make the abstraction layer be closer to the D3D12/Vulkan model than to a D3D11-style model. This is important for the `model-viewer` example, since it aspires to show how Slang can be incorporated into a renderer that targets a modern API. The most important bit is actually the use of descriptor sets and "pipeline layouts" a la Vulkan, since without these Slang's `ParameterBlock` abstraction won't make a lot of sense. Implementation of the abstraction for the various APIs has very much been on an as-needed basis. The current implementation is just enough for the two examples to work, plus enough to get all the tests to pass in both debug and release builds on Windows. A big missing feature in the API abstraction right now is memory lifetime management. The code had been trending toward something D3D11-like where a constant buffer could be mapped per-frame with the implementation doing behind-the-scenes allocation for targets like D3D12/Vulkan. I'd like to shift more toward a model of just exposing "transient" allocations that are only valid for one frame, because these are more representation of how an efficient renderer for next-generation APIs will work. That transition isn't actually complete, though, so there are problems with the existing examples where `hello-world` is actually scribbling into memory that the GPU might still be using, while `model-viewer` is doing full-on heavy-weight allocations on a per-frame basis with no real concern for the performance implications. All together, there are a lot of things here that need more work, but this branch has been way too long-lived already, and so I'd like to get this checked in as long as all the tests pass.
Diffstat (limited to 'examples/model-viewer/shaders.slang')
-rw-r--r--examples/model-viewer/shaders.slang178
1 files changed, 178 insertions, 0 deletions
diff --git a/examples/model-viewer/shaders.slang b/examples/model-viewer/shaders.slang
new file mode 100644
index 000000000..b79636d15
--- /dev/null
+++ b/examples/model-viewer/shaders.slang
@@ -0,0 +1,178 @@
+// shaders.slang
+
+//
+// This example builds on the simplistic shaders presented in the
+// "Hello, World" example by adding support for (intentionally
+// simplistic) surface materil and light shading.
+//
+// The code here is not meant to exemplify state-of-the-art material
+// and lighting techniques, but rather to show how a shader
+// library can be developed in a modular fashion without reliance
+// on the C preprocessor manual parameter-binding decorations.
+//
+
+// We will start with a `struct` for per-view parameters that
+// will be allocated into a `ParameterBlock`.
+//
+// As written, this isn't very different from using an HLSL
+// `cbuffer` declaration, but importantly this code will
+// continue to work if we add one or more resources (e.g.,
+// an enironment map texture) to the `PerView` type.
+//
+struct PerView
+{
+ float4x4 viewProjection;
+
+ float3 lightDir;
+ float3 lightColor;
+};
+ParameterBlock<PerView> gViewParams;
+
+// Declaring a block for per-model parameter data is
+// similarly simple.
+//
+struct PerModel
+{
+ float4x4 modelTransform;
+ float4x4 inverseTransposeModelTransform;
+};
+ParameterBlock<PerModel> gModelParams;
+
+
+// Next, we are going to demonstrate a simplistic interface
+// for surface materials. As written, materials can only
+// determine how to compute the diffuse color component
+// of a surface; a more advanced example would fold
+// the entire BRDF into the material interface.
+//
+interface IMaterial
+{
+ float3 getDiffuseColor();
+};
+
+// In order for our shader to be able to take a material
+// as a parameter, we need to declare a `ParameterBlock<M>`
+// for some material type `M`. Rather than hard-code the
+// specific material type to use, or select one via the
+// preprocessor, we will use Slang's support for generics,
+// by defining a "global type parameter":
+//
+type_param TMaterial : IMaterial;
+//
+// This declaration declares a shader parameter `TMaterial`
+// that is a to-be-determined *type*. The `TMaterial`
+// type parameter is *constrained* to only support types
+// that implement our `IMaterial` interface.
+//
+// With the `TMaterial` parameter declared, we can
+// declare that our shader takes as input a parameter block
+// containing material data:
+//
+ParameterBlock<TMaterial> gMaterial;
+
+// For now, we will define only a single implementation
+// of the `IMaterial` interface, which is a simple material
+// with a uniform diffuse color:
+//
+struct SimpleMaterial : IMaterial
+{
+ float3 diffuseColor;
+
+ float3 getDiffuseColor()
+ {
+ return diffuseColor;
+ }
+};
+//
+// Note that no other code in this file statically
+// references the `SimpleMaterial` type, and instead
+// it is up to the application to "plug in" this type,
+// or another `IMaterial` implementation for the
+// `TMaterial` parameter.
+//
+
+// Our vertex shader entry point is only marginally more
+// complicated than the Hello World example. We will
+// start by declaring the various "connector" `struct`s.
+//
+struct AssembledVertex
+{
+ float3 position : POSITION;
+ float3 normal : NORMAL;
+ float2 uv : UV;
+};
+struct CoarseVertex
+{
+ float3 worldPosition;
+ float3 worldNormal;
+ float2 uv;
+};
+struct VertexStageOutput
+{
+ CoarseVertex coarseVertex : CoarseVertex;
+ float4 sv_position : SV_Position;
+};
+
+// Perhaps most interesting new feature of the entry
+// point decalrations is that we use a `[shader(...)]`
+// attribute (as introduced in HLSL Shader Model 6.x)
+// in order to tag our entry points.
+//
+// This attribute informs the Slang compiler which
+// functions are intended to be compiled as shader
+// entry points (and what stage they target), so that
+// the programmer no longer needs to specify the
+// entry point name/stage through the API (or on
+// the command line when using `slangc`).
+//
+// While HLSL added this feature only in newer versions,
+// the Slang compiler supports this attribute across
+// *all* targets, so that it is okay to use whether you
+// want DXBC, DXIL, or SPIR-V output.
+//
+[shader("vertex")]
+VertexStageOutput vertexMain(
+ AssembledVertex assembledVertex)
+{
+ VertexStageOutput output;
+
+ float3 position = assembledVertex.position;
+ float3 normal = assembledVertex.normal;
+ float2 uv = assembledVertex.uv;
+
+ float3 worldPosition = mul(gModelParams.modelTransform, float4(position, 1.0)).xyz;
+ float3 worldNormal = mul(gModelParams.inverseTransposeModelTransform, float4(normal, 0.0)).xyz;
+
+ output.coarseVertex.worldPosition = worldPosition;
+ output.coarseVertex.worldNormal = worldNormal;
+ output.coarseVertex.uv = uv;
+
+ output.sv_position = mul(gViewParams.viewProjection, float4(worldPosition, 1.0));
+
+ return output;
+}
+
+// Our fragment shader is almost trivial, with the most interesting
+// thing being how it uses the `TMaterial` type parameter (through the
+// value stored in the `gMaterial` parameter block) to dispatch to
+// the correct implementation of the `getDiffuseColor()` method
+// in the `IMaterial` interface.
+//
+// The `gMaterial` parameter block declaration thus serves not only
+// to group certain shader parameters for efficient CPU-to-GPU
+// communication, but also to select the code that will execute
+// in specialized versions of the `fragmentMain` entry point.
+//
+[shader("fragment")]
+float4 fragmentMain(
+ CoarseVertex coarseVertex : CoarseVertex) : SV_Target
+{
+ float3 N = normalize(coarseVertex.worldNormal);
+ float3 L = normalize(gViewParams.lightDir);
+
+ float4 color;
+ color.xyz = gMaterial.getDiffuseColor() * max(0, dot(N, L));
+ color.w = 1.0f;
+
+ return color;
+}