diff options
| author | Yong He <yonghe@outlook.com> | 2021-04-05 13:31:05 -0700 |
|---|---|---|
| committer | GitHub <noreply@github.com> | 2021-04-05 13:31:05 -0700 |
| commit | 086ecf41fa21138899960bb9805bc8ced91690f0 (patch) | |
| tree | c98af81ffc28371a9334e71987a85f9e88bce678 /examples | |
| parent | dd662f5cda97e7a6720ef526509a772a06112d4a (diff) | |
Transient root shader object. (#1782)
Diffstat (limited to 'examples')
| -rw-r--r-- | examples/gpu-printing/main.cpp | 4 | ||||
| -rw-r--r-- | examples/hello-world/main.cpp | 77 | ||||
| -rw-r--r-- | examples/shader-object/main.cpp | 74 | ||||
| -rw-r--r-- | examples/shader-toy/main.cpp | 12 |
4 files changed, 79 insertions, 88 deletions
diff --git a/examples/gpu-printing/main.cpp b/examples/gpu-printing/main.cpp index a0acb8159..265b4c5ff 100644 --- a/examples/gpu-printing/main.cpp +++ b/examples/gpu-printing/main.cpp @@ -121,9 +121,7 @@ Result execute() auto queue = gDevice->createCommandQueue(queueDesc); auto commandBuffer = transientHeap->createCommandBuffer(); auto encoder = commandBuffer->encodeComputeCommands(); - auto rootShaderObject = gDevice->createRootShaderObject(gProgram); - encoder->setPipelineState(gPipelineState); - encoder->bindRootShaderObject(rootShaderObject); + auto rootShaderObject = encoder->bindPipeline(gPipelineState); encoder->dispatchCompute(1, 1, 1); encoder->endEncoding(); commandBuffer->close(); diff --git a/examples/hello-world/main.cpp b/examples/hello-world/main.cpp index 19413948a..6b9104072 100644 --- a/examples/hello-world/main.cpp +++ b/examples/hello-world/main.cpp @@ -206,7 +206,6 @@ gfx::Result loadShaderProgram( // building an example program. // ComPtr<gfx::IPipelineState> gPipelineState; -ComPtr<gfx::IShaderObject> gRootObject; ComPtr<gfx::IBufferResource> gVertexBuffer; // Now that we've covered the function that actually loads and @@ -251,38 +250,6 @@ Slang::Result initialize() ComPtr<IShaderProgram> shaderProgram; SLANG_RETURN_ON_FAIL(loadShaderProgram(gDevice, shaderProgram.writeRef())); - // In order to bind shader parameters to the pipeline, we need - // to know how those parameters were assigned to locations/bindings/registers - // for the target graphics API. - // - // The Slang compiler assigns locations to parameters in a deterministic - // fashion, so it is possible for a programmer to hard-code locations - // into their application code that will match up with their shaders. - // - // Hard-coding of locations can become intractable as an application needs - // to support more different target platforms and graphics APIs, as well - // as more shaders with different specialized variants. - // - // Rather than rely on hard-coded locations, our examples will make use of - // reflection information provided by the Slang compiler (see `programLayout` - // above), and our example graphics API layer will translate that reflection - // information into a layout for a "root shader object." - // - // The root object will store values/bindings for all of the parameters in - // the `shaderProgram`. At a conceptual level we can think of `rootObject` as - // representing the "global scope" of the shader program that was loaded; - // it has entries for each global shader parameter that was declared. - // - // Multiple root objects can be created from the same program, and will have - // separate storage for parameter values. - // - // Readers who are familiar with D3D12 or Vulkan might think of this root - // layout as being similar in spirit to a "root signature" or "pipeline layout." - // - ComPtr<IShaderObject> rootObject; - SLANG_RETURN_ON_FAIL(gDevice->createRootShaderObject(shaderProgram, rootObject.writeRef())); - gRootObject = rootObject; - // Following the D3D12/Vulkan style of API, we need a pipeline state object // (PSO) to encapsulate the configuration of the overall graphics pipeline. // @@ -315,6 +282,38 @@ virtual void renderFrame(int frameBufferIndex) override viewport.extentY = (float)windowHeight; renderEncoder->setViewportAndScissor(viewport); + // In order to bind shader parameters to the pipeline, we need + // to know how those parameters were assigned to locations/bindings/registers + // for the target graphics API. + // + // The Slang compiler assigns locations to parameters in a deterministic + // fashion, so it is possible for a programmer to hard-code locations + // into their application code that will match up with their shaders. + // + // Hard-coding of locations can become intractable as an application needs + // to support more different target platforms and graphics APIs, as well + // as more shaders with different specialized variants. + // + // Rather than rely on hard-coded locations, our examples will make use of + // reflection information provided by the Slang compiler (see `programLayout` + // above), and our example graphics API layer will translate that reflection + // information into a layout for a "root shader object." + // + // The root object will store values/bindings for all of the parameters in + // the `IShaderProgram` used to create the pipeline state. At a conceptual + // level we can think of `rootObject` as representing the "global scope" of + // the shader program that was loaded; it has entries for each global shader + // parameter that was declared. + // + // Readers who are familiar with D3D12 or Vulkan might think of this root + // layout as being similar in spirit to a "root signature" or "pipeline layout." + // + // We start parameter binding by binding the pipeline state in command encoder. + // This method will return a transient root shader object for us to write our + // shader parameters into. + // + auto rootObject = renderEncoder->bindPipeline(gPipelineState); + // We will update the model-view-projection matrix that is passed // into the shader code via the `Uniforms` buffer on a per-frame // basis, even though the data that is loaded does not change @@ -322,8 +321,7 @@ virtual void renderFrame(int frameBufferIndex) override // auto deviceInfo = gDevice->getDeviceInfo(); - // - // We know that `gRootObject` is a root shader object created + // We know that `rootObject` is a root shader object created // from our program, and that it is set up to hold values for // all the parameter of that program. In order to actually // set values, we need to be able to look up the location @@ -341,7 +339,7 @@ virtual void renderFrame(int frameBufferIndex) override // a diretory path of `/` for the root directory in a file // system. // - ShaderCursor rootCursor(gRootObject); + ShaderCursor rootCursor(rootObject); // // Next, we use a convenience overload of `operator[]` to // navigate from the root cursor down to the parameter we @@ -375,13 +373,6 @@ virtual void renderFrame(int frameBufferIndex) override // hard-coded even in cross-platform code. // - // Now we configure our graphics pipeline state by setting the - // PSO, binding our root shader object to it (which references - // the `Uniforms` buffer that will filled in above). - // - renderEncoder->setPipelineState(gPipelineState); - renderEncoder->bindRootShaderObject(gRootObject); - // We also need to set up a few pieces of fixed-function pipeline // state that are not bound by the pipeline state above. // diff --git a/examples/shader-object/main.cpp b/examples/shader-object/main.cpp index 9329a5418..6efe2f97d 100644 --- a/examples/shader-object/main.cpp +++ b/examples/shader-object/main.cpp @@ -136,7 +136,6 @@ int main() // interacting with the graphics API. Slang::ComPtr<gfx::IDevice> device; IDevice::Desc deviceDesc = {}; - deviceDesc.deviceType = DeviceType::Vulkan; SLANG_RETURN_ON_FAIL(gfxCreateDevice(&deviceDesc, device.writeRef())); Slang::ComPtr<gfx::ITransientResourceHeap> transientHeap; @@ -184,46 +183,51 @@ int main() viewDesc.format = gfx::Format::Unknown; SLANG_RETURN_ON_FAIL(device->createBufferView(numbersBuffer, viewDesc, bufferView.writeRef())); - // Now comes the interesting part: binding the shader parameter for the - // compute kernel that we about to launch. We would like to construct - // a shader object that represents a `f(x)=x+1` transformation and apply - // it to the numbers in `numbersBuffer`. - // To start, we create a root shader object that represents the root level - // scope of the shader parameters. - ComPtr<gfx::IShaderObject> rootObject; - SLANG_RETURN_ON_FAIL(device->createRootShaderObject(shaderProgram, rootObject.writeRef())); - // We can set parameters directly with `rootObject`, but that requires us to use - // the Slang reflection API to obtain the proper offsets into the root object for each parameter. - // We implemented these logic in the `ShaderCursor` helper class, which simplifies the user - // code to find shader parameters. Here we demonstrate how to set parameters with `ShaderCursor`. - gfx::ShaderCursor entryPointCursor(rootObject->getEntryPoint(0)); // get a cursor the the first entry-point. - // Bind buffer view to the entry point. - entryPointCursor.getPath("buffer").setResource(bufferView); - - // Next, we create a shader object that represents the transformer we want to use. - // To do so, we first need to lookup for the `AddTransformer` type defined in the shader code. - slang::TypeReflection* addTransformerType = slangReflection->findTypeByName("AddTransformer"); - - // Now we can use this type to create a shader object that can be bound to the root object. - ComPtr<gfx::IShaderObject> transformer; - SLANG_RETURN_ON_FAIL(device->createShaderObject(addTransformerType, transformer.writeRef())); - // Set the `c` field of the `AddTransformer`. - float c = 1.0f; - gfx::ShaderCursor(transformer).getPath("c").setData(&c, sizeof(float)); - - // Now the transformer object is ready, we can bind it to root object. - entryPointCursor.getPath("transformer").setObject(transformer); - - // We have set up all required parameters in entry-point object, now it is time - // to bind the pipeline and root object and launch the kernel. + // We have done all the set up work, now it is time to start recording a command buffer for + // GPU execution. { ICommandQueue::Desc queueDesc = {ICommandQueue::QueueType::Graphics}; auto queue = device->createCommandQueue(queueDesc); auto commandBuffer = transientHeap->createCommandBuffer(); auto encoder = commandBuffer->encodeComputeCommands(); - encoder->setPipelineState(pipelineState); - encoder->bindRootShaderObject(rootObject); + + + // Now comes the interesting part: binding the shader parameter for the + // compute kernel that we about to launch. We would like to construct + // a shader object that represents a `f(x)=x+1` transformation and apply + // it to the numbers in `numbersBuffer`. + + // First, obtain a root shader object from command encoder to start parameter binding. + auto rootObject = encoder->bindPipeline(pipelineState); + + // Next, we create a shader object that represents the transformer we want to use. + // To do so, we first need to lookup for the `AddTransformer` type defined in the shader + // code. + slang::TypeReflection* addTransformerType = + slangReflection->findTypeByName("AddTransformer"); + + // Now we can use this type to create a shader object that can be bound to the root object. + ComPtr<gfx::IShaderObject> transformer; + SLANG_RETURN_ON_FAIL( + device->createShaderObject(addTransformerType, transformer.writeRef())); + // Set the `c` field of the `AddTransformer`. + float c = 1.0f; + gfx::ShaderCursor(transformer).getPath("c").setData(&c, sizeof(float)); + + // We can set parameters directly with `rootObject`, but that requires us to use + // the Slang reflection API to obtain the proper offsets into the root object for each + // parameter. We implemented these logic in the `ShaderCursor` helper class, which + // simplifies the user code to find shader parameters. Here we demonstrate how to set + // parameters with `ShaderCursor`. + gfx::ShaderCursor entryPointCursor( + rootObject->getEntryPoint(0)); // get a cursor the the first entry-point. + // Bind buffer view to the entry point. + entryPointCursor.getPath("buffer").setResource(bufferView); + + // Bind the previously created transformer object to root object. + entryPointCursor.getPath("transformer").setObject(transformer); + encoder->dispatchCompute(1, 1, 1); encoder->endEncoding(); commandBuffer->close(); diff --git a/examples/shader-toy/main.cpp b/examples/shader-toy/main.cpp index a142c3c15..40c97e0f4 100644 --- a/examples/shader-toy/main.cpp +++ b/examples/shader-toy/main.cpp @@ -286,7 +286,6 @@ Result loadShaderProgram(gfx::IDevice* device, ComPtr<gfx::IShaderProgram>& outS } ComPtr<IShaderProgram> gShaderProgram; -ComPtr<gfx::IShaderObject> gRootObject[kSwapchainImageCount]; ComPtr<gfx::IPipelineState> gPipelineState; ComPtr<gfx::IBufferResource> gVertexBuffer; @@ -371,10 +370,7 @@ virtual void renderFrame(int frameIndex) override uniforms.iResolution[1] = float(windowHeight); } - gRootObject[frameIndex] = gDevice->createRootShaderObject(gShaderProgram); - auto constantBuffer = gRootObject[frameIndex]->getObject(ShaderOffset()); - constantBuffer->setData(ShaderOffset(), &uniforms, sizeof(uniforms)); - + // Encode render commands. auto encoder = commandBuffer->encodeRenderCommands(gRenderPass, gFramebuffers[frameIndex]); @@ -383,8 +379,10 @@ virtual void renderFrame(int frameIndex) override viewport.extentX = (float)windowWidth; viewport.extentY = (float)windowHeight; encoder->setViewportAndScissor(viewport); - encoder->setPipelineState(gPipelineState); - encoder->bindRootShaderObject(gRootObject[frameIndex]); + auto rootObject = encoder->bindPipeline(gPipelineState); + auto constantBuffer = rootObject->getObject(ShaderOffset()); + constantBuffer->setData(ShaderOffset(), &uniforms, sizeof(uniforms)); + encoder->setVertexBuffer(0, gVertexBuffer, sizeof(FullScreenTriangle::Vertex)); encoder->setPrimitiveTopology(PrimitiveTopology::TriangleList); encoder->draw(3); |
