summaryrefslogtreecommitdiffstats
path: root/tools/render-test
diff options
context:
space:
mode:
authorTim Foley <tfoleyNV@users.noreply.github.com>2021-03-25 16:40:17 -0700
committerGitHub <noreply@github.com>2021-03-25 16:40:17 -0700
commitabb020b15c4f1057657e5f8f63c02b15615bf6ec (patch)
tree99b51e173fa38cc1dee8cd54cca2c2a34bbf240d /tools/render-test
parente050035e0b7d3f257a46bc1cb644163026cb1b23 (diff)
Clean up render-test handling of input (#1766)
The original goal of this change was to streamline the `TEST_INPUT` system by eliminating options that are no longer relevant once we have eliminated the non-shader-object execution paths. The result is more or less a re-implementation/refactor of the logic around how input is parsed and represented, that tries to set things up for a more general sytem going forward. The main changes isthat the `ShaderInputLayout` no longer tracks a simple flat list of `ShaderInputLayoutEntry` (that is a kind of pseudo-union of the various buffer/texture/value cases), and it instead uses a hierarchical representation composed of `RefObject`-derived classes to represent "values." There are several "simple" cases of values * Textures * Samplers * Uniform/ordinary data (`uniform`) * Buffers composed of uniform/ordinary data (`ubuffer`) Then there are composed/aggregate values that nest other values: * An *aggregate* value is a set of *fields* which are name/value pairs. It can be used to fill in a structure, for example. * An *array* value is a list of values for the elements of an array. It can be used to fill out an array-of-textures parameter, for example. * A combined texture/sampler value is a pair of a texture value and a sampler value (easy enough) * An *object* holds an optional type name for a shader object to allocate (it defaults to the type that is "under" the current shader cursor when binding), and a nested value that describes how to fill in the contents of that object Finally there are cases of values that are just syntactic sugar: * A `cbuffer` is just shorthand for creating an object value with a nested uniform/ordinary data value The big idea with this recursive structure is that it gives us a way to handle more arbitrary data types with name-based binding. Supporting this new capability requires changes to both how input layouts get parsed, and also how they get bound into shader objects. On the parsing side, things have been refactored a bit so that parsing isn't a single monolithic routine. The refactor also tries to make it so that the various options on an input item (e.g., the `size=...` option for textures) are only supported on the relevant type of entry (so you can't specify as many useless options that will be ignored). The bigger change to parsing is that it now supports a hierarchical structure, where certain input elements like `begin_array` can push a new "parent" value onto a stack, and subsequent `TEST_INPUT` lines will be parsed as children of that item until a matching `end` item. This approach means that we can now in principle describe arbitrary hierarchical structures as part of test input without endlessly increasing the complexity of invididual `TEST_INPUT` lines. On the binding side, we now have a central recursive operation called `assign(ShaderCursor, ShaderInputLayout::ValPtr)` that assigns from a parsed `ShaderInputLayout` value to a particular cursor. That operation can then recurse on the fields/elements/contents of whatever the cursor points to. Major open directions: * With this change it is still necessary to use `uniform` entries to set things like individual integers or `float`s and that is a little silly. It would be good to have some streamlines cases for setting individual scalar values. * Further, once we have a hierarchical representation of the values for `TEST_INPUT` lines, it becomes clear that we really ought to move to a format more like `TEST_INPUT: dstLocation = srcValue;` where `srcValue` is some kind of hierarchial expression grammar. Refactoring things in this way should make the binding logic even more clear and easy to understand. The refactored parser should make parsing hierarchical expressions easier to do in the future (even if it uses the push/pop model for now) * One detailed note is that the representation of buffers in this change is kind of a compromise. Just as an "object" value is a thin wrapper around a recursively-contained value for its "content" it seems clear that a buffer could be represented as a wrapper around a content value that could include hierarchical aggregates/objects instead of just flat binary data (this would be important for things like a buffer over a structure type that lays out different on different targets). The main problem right now with changing the representation is actually needing to compute the size of a buffer based on its content, so that can/should be addressed in a subsequent change. Details: * The base `RenderTestApp` class and the `ShaderObjectRenderTestApp` classes have been merged, since the hierarchy no longer serves any purpose. * Disabled the tess that rely on `StructuredBuffer<IWhatever>` because they aren't really supported by our current shader object implementation * Replaced used of `Uniform` and `root_constants` in `TEST_INPUT` lines with just `uniform` * Removed a bunch of uses of `stride` from `cbuffer` inputs, where it wasn't really correct/meaningful * Added the `copyBuffer()` operation to VK/D3D renderers, along with some missing `Usage` cases to support it. * Made `ShaderCursor` handle the logic to look up a name in the entry points of a root shader object, rather than just having that logic in `render-test`. (We probably need to make a clear design choice on this issue)
Diffstat (limited to 'tools/render-test')
-rw-r--r--tools/render-test/render-test-main.cpp488
-rw-r--r--tools/render-test/shader-input-layout.cpp1111
-rw-r--r--tools/render-test/shader-input-layout.h190
-rw-r--r--tools/render-test/shader-renderer-util.cpp13
-rw-r--r--tools/render-test/shader-renderer-util.h1
-rw-r--r--tools/render-test/slang-support.cpp1
6 files changed, 963 insertions, 841 deletions
diff --git a/tools/render-test/render-test-main.cpp b/tools/render-test/render-test-main.cpp
index 02f20dd40..325f12e49 100644
--- a/tools/render-test/render-test-main.cpp
+++ b/tools/render-test/render-test-main.cpp
@@ -74,7 +74,6 @@ struct ShaderOutputPlan
{
struct Item
{
- Index inputLayoutEntryIndex;
ComPtr<IResource> resource;
slang::TypeLayoutReflection* typeLayout = nullptr;
};
@@ -82,25 +81,25 @@ struct ShaderOutputPlan
List<Item> items;
};
-class RenderTestApp : public RefObject
+class RenderTestApp
{
public:
Result update();
// At initialization time, we are going to load and compile our Slang shader
// code, and then create the API objects we need for rendering.
- virtual Result initialize(
+ Result initialize(
SlangSession* session,
IDevice* device,
const Options& options,
- const ShaderCompilerUtil::Input& input) = 0;
+ const ShaderCompilerUtil::Input& input);
void runCompute(IComputeCommandEncoder* encoder);
void renderFrame(IRenderCommandEncoder* encoder);
void finalize();
- virtual void applyBinding(PipelineType pipelineType, ICommandEncoder* encoder) = 0;
- virtual void setProjectionMatrix(IResourceCommandEncoder* encoder) = 0;
- virtual Result writeBindingOutput(const char* fileName) = 0;
+ void applyBinding(PipelineType pipelineType, ICommandEncoder* encoder);
+ void setProjectionMatrix(IResourceCommandEncoder* encoder);
+ Result writeBindingOutput(const char* fileName);
Result writeScreen(const char* filename);
@@ -112,7 +111,6 @@ protected:
Options::ShaderProgramType shaderType,
const ShaderCompilerUtil::Input& input);
void _initializeRenderPass();
- virtual void finalizeImpl();
uint64_t m_startTicks;
@@ -135,256 +133,253 @@ protected:
ShaderInputLayout m_shaderInputLayout; ///< The binding layout
Options m_options;
-};
-
-class ShaderObjectRenderTestApp : public RenderTestApp
-{
-public:
- virtual void applyBinding(PipelineType pipelineType, ICommandEncoder* encoder) SLANG_OVERRIDE;
- virtual void setProjectionMatrix(IResourceCommandEncoder* encoder) SLANG_OVERRIDE;
- virtual Result initialize(
- SlangSession* session,
- IDevice* device,
- const Options& options,
- const ShaderCompilerUtil::Input& input) SLANG_OVERRIDE;
- virtual Result writeBindingOutput(const char* fileName) override;
-
-protected:
- virtual void finalizeImpl() SLANG_OVERRIDE;
ComPtr<IShaderObject> m_programVars;
ShaderOutputPlan m_outputPlan;
};
-SlangResult _assignVarsFromLayout(
- IDevice* device,
- IShaderObject* shaderObject,
- ShaderInputLayout const& layout,
- ShaderOutputPlan& ioOutputPlan,
- slang::ProgramLayout* slangReflection)
+struct AssignValsFromLayoutContext
{
- ShaderCursor rootCursor = ShaderCursor(shaderObject);
-
- const int textureBindFlags = IResource::BindFlag::NonPixelShaderResource | IResource::BindFlag::PixelShaderResource;
-
- Index entryCount = layout.entries.getCount();
- for(Index entryIndex = 0; entryIndex < entryCount; ++entryIndex)
+ IDevice* device;
+ ShaderOutputPlan& outputPlan;
+ slang::ProgramLayout* slangReflection;
+
+ AssignValsFromLayoutContext(
+ IDevice* device,
+ ShaderOutputPlan& outputPlan,
+ slang::ProgramLayout* slangReflection)
+ : device(device)
+ , outputPlan(outputPlan)
+ , slangReflection(slangReflection)
+ {}
+
+ void maybeAddOutput(ShaderCursor const& dstCursor, ShaderInputLayout::Val* srcVal, IResource* resource)
{
- auto& entry = layout.entries[entryIndex];
- if(entry.name.getLength() == 0)
+ if(srcVal->isOutput)
{
- StdWriters::getError().print("error: entries in `ShaderInputLayout` must include a name\n");
- return SLANG_E_INVALID_ARG;
+ ShaderOutputPlan::Item item;
+ item.resource = resource;
+ item.typeLayout = dstCursor.getTypeLayout();
+ outputPlan.items.add(item);
}
+ }
- auto entryCursor = rootCursor.getPath(entry.name.getBuffer());
+ SlangResult assignData(ShaderCursor const& dstCursor, ShaderInputLayout::DataVal* srcVal)
+ {
+ const size_t bufferSize = srcVal->bufferData.getCount() * sizeof(uint32_t);
- if(!entryCursor.isValid())
+ ShaderCursor dataCursor = dstCursor;
+ switch(dataCursor.getTypeLayout()->getKind())
{
- for(gfx::UInt i = 0; i < shaderObject->getEntryPointCount(); i++)
- {
- entryCursor = ShaderCursor(shaderObject->getEntryPoint(i)).getPath(entry.name.getBuffer());
- if(entryCursor.isValid())
- break;
- }
+ case slang::TypeReflection::Kind::ConstantBuffer:
+ case slang::TypeReflection::Kind::ParameterBlock:
+ dataCursor = dataCursor.getDereferenced();
+ break;
+
+ default:
+ break;
+
}
+ SLANG_RETURN_ON_FAIL(dataCursor.setData(srcVal->bufferData.getBuffer(), bufferSize));
+ return SLANG_OK;
+ }
+
+ SlangResult assignBuffer(ShaderCursor const& dstCursor, ShaderInputLayout::BufferVal* srcVal)
+ {
+ const InputBufferDesc& srcBuffer = srcVal->bufferDesc;
+ auto& bufferData = srcVal->bufferData;
+ const size_t bufferSize = bufferData.getCount() * sizeof(uint32_t);
+
+ ComPtr<IBufferResource> bufferResource;
+ SLANG_RETURN_ON_FAIL(ShaderRendererUtil::createBufferResource(srcBuffer, /*entry.isOutput,*/ bufferSize, bufferData.getBuffer(), device, bufferResource));
+
+ IResourceView::Desc viewDesc;
+ viewDesc.type = IResourceView::Type::UnorderedAccess;
+ viewDesc.format = srcBuffer.format;
+ auto bufferView = device->createBufferView(
+ bufferResource,
+ viewDesc);
+ dstCursor.setResource(bufferView);
+ maybeAddOutput(dstCursor, srcVal, bufferResource);
+
+ return SLANG_OK;
+ }
+
+ SlangResult assignCombinedTextureSampler(ShaderCursor const& dstCursor, ShaderInputLayout::CombinedTextureSamplerVal* srcVal)
+ {
+ auto& textureEntry = srcVal->textureVal;
+ auto& samplerEntry = srcVal->samplerVal;
+
+ ComPtr<ITextureResource> texture;
+ SLANG_RETURN_ON_FAIL(ShaderRendererUtil::generateTextureResource(
+ textureEntry->textureDesc, textureBindFlags, device, texture));
- if(!entryCursor.isValid())
+ auto sampler = _createSamplerState(device, samplerEntry->samplerDesc);
+
+ IResourceView::Desc viewDesc;
+ viewDesc.type = IResourceView::Type::ShaderResource;
+ auto textureView = device->createTextureView(
+ texture,
+ viewDesc);
+
+ dstCursor.setCombinedTextureSampler(textureView, sampler);
+ maybeAddOutput(dstCursor, srcVal, texture);
+
+ return SLANG_OK;
+ }
+
+ static const int textureBindFlags = IResource::BindFlag::NonPixelShaderResource | IResource::BindFlag::PixelShaderResource;
+
+ SlangResult assignTexture(ShaderCursor const& dstCursor, ShaderInputLayout::TextureVal* srcVal)
+ {
+ ComPtr<ITextureResource> texture;
+ SLANG_RETURN_ON_FAIL(ShaderRendererUtil::generateTextureResource(
+ srcVal->textureDesc, textureBindFlags, device, texture));
+
+ // TODO: support UAV textures...
+
+ IResourceView::Desc viewDesc;
+ viewDesc.type = IResourceView::Type::ShaderResource;
+ viewDesc.format = texture->getDesc()->format;
+ auto textureView = device->createTextureView(
+ texture,
+ viewDesc);
+
+ if (!textureView)
{
- StdWriters::getError().print("error: could not find shader parameter matching '%s'\n", entry.name.begin());
- return SLANG_E_INVALID_ARG;
+ return SLANG_FAIL;
}
- slang::TypeLayoutReflection* typeLayout = entryCursor.getTypeLayout();
- ComPtr<IResource> resource;
- switch(entry.type)
- {
- case ShaderInputType::Uniform:
- {
- const size_t bufferSize = entry.bufferData.getCount() * sizeof(uint32_t);
+ dstCursor.setResource(textureView);
+ maybeAddOutput(dstCursor, srcVal, texture);
+ return SLANG_OK;
+ }
- ShaderCursor dataCursor = entryCursor;
- switch(dataCursor.getTypeLayout()->getKind())
- {
- case slang::TypeReflection::Kind::ConstantBuffer:
- case slang::TypeReflection::Kind::ParameterBlock:
- dataCursor = dataCursor.getDereferenced();
- break;
+ SlangResult assignSampler(ShaderCursor const& dstCursor, ShaderInputLayout::SamplerVal* srcVal)
+ {
+ auto sampler = _createSamplerState(device, srcVal->samplerDesc);
- default:
- break;
+ dstCursor.setSampler(sampler);
+ return SLANG_OK;
+ }
- }
+ SlangResult assignAggregate(ShaderCursor const& dstCursor, ShaderInputLayout::AggVal* srcVal)
+ {
+ Index fieldCount = srcVal->fields.getCount();
+ for(Index fieldIndex = 0; fieldIndex < fieldCount; ++fieldIndex)
+ {
+ auto& field = srcVal->fields[fieldIndex];
- dataCursor.setData(entry.bufferData.getBuffer(), bufferSize);
+ if(field.name.getLength() == 0)
+ {
+ StdWriters::getError().print("error: entries in `ShaderInputLayout` must include a name\n");
+ return SLANG_E_INVALID_ARG;
}
- break;
- case ShaderInputType::Buffer:
- {
- const InputBufferDesc& srcBuffer = entry.bufferDesc;
- const size_t bufferSize = entry.bufferData.getCount() * sizeof(uint32_t);
+ auto fieldCursor = dstCursor.getPath(field.name.getBuffer());
- switch(srcBuffer.type)
- {
- case InputBufferType::ConstantBuffer:
- {
- // A `cbuffer` input line actually represents the data we
- // want to write *into* the buffer, and shouldn't
- // allocate a buffer itself.
- //
- entryCursor.getDereferenced().setData(entry.bufferData.getBuffer(), bufferSize);
- }
- break;
-
- case InputBufferType::RootConstantBuffer:
- {
- // A `root_constants` input line actually represents the data we
- // want to write *into* the buffer, and shouldn't
- // allocate a buffer itself.
- //
- // Note: we are not doing `.getDereferenced()` here because the
- // `root_constant` line should be referring to a parameter value
- // inside the root-constant range, and not the range/buffer itself.
- //
- entryCursor.setData(entry.bufferData.getBuffer(), bufferSize);
- }
- break;
-
- default:
- {
-
- ComPtr<IBufferResource> bufferResource;
- SLANG_RETURN_ON_FAIL(ShaderRendererUtil::createBufferResource(entry.bufferDesc, entry.isOutput, bufferSize, entry.bufferData.getBuffer(), device, bufferResource));
- resource = bufferResource;
-
- IResourceView::Desc viewDesc;
- viewDesc.type = IResourceView::Type::UnorderedAccess;
- viewDesc.format = srcBuffer.format;
- auto bufferView = device->createBufferView(
- bufferResource,
- viewDesc);
- entryCursor.setResource(bufferView);
- }
- break;
- }
+ if(!fieldCursor.isValid())
+ {
+ StdWriters::getError().print("error: could not find shader parameter matching '%s'\n", field.name.begin());
+ return SLANG_E_INVALID_ARG;
}
- break;
- case ShaderInputType::CombinedTextureSampler:
- {
- ComPtr<ITextureResource> texture;
- SLANG_RETURN_ON_FAIL(ShaderRendererUtil::generateTextureResource(
- entry.textureDesc, textureBindFlags, device, texture));
- resource = texture;
+ assign(fieldCursor, field.val);
+ }
+ return SLANG_OK;
+ }
- auto sampler = _createSamplerState(device, entry.samplerDesc);
+ SlangResult assignObject(ShaderCursor const& dstCursor, ShaderInputLayout::ObjectVal* srcVal)
+ {
+ auto typeName = srcVal->typeName;
+ slang::TypeReflection* slangType = nullptr;
+ if(typeName.getLength() != 0)
+ {
+ // If the input line specified the name of the type
+ // to allocate, then we use it directly.
+ //
+ slangType = slangReflection->findTypeByName(typeName.getBuffer());
+ }
+ else
+ {
+ // if the user did not specify what type to allocate,
+ // then we will infer the type from the type of the
+ // value pointed to by `entryCursor`.
+ //
+ auto slangTypeLayout = dstCursor.getTypeLayout();
+ switch(slangTypeLayout->getKind())
+ {
+ default:
+ break;
+
+ case slang::TypeReflection::Kind::ConstantBuffer:
+ case slang::TypeReflection::Kind::ParameterBlock:
+ // If the cursor is pointing at a constant buffer
+ // or parameter block, then we assume the user
+ // actually means to allocate an object based on
+ // the element type of the block.
+ //
+ slangTypeLayout = slangTypeLayout->getElementTypeLayout();
+ break;
+ }
+ slangType = slangTypeLayout->getType();
+ }
- IResourceView::Desc viewDesc;
- viewDesc.type = IResourceView::Type::ShaderResource;
- auto textureView = device->createTextureView(
- texture,
- viewDesc);
+ ComPtr<IShaderObject> shaderObject = device->createShaderObject(slangType);
- entryCursor.setCombinedTextureSampler(textureView, sampler);
- }
- break;
+ assign(ShaderCursor(shaderObject), srcVal->contentVal);
- case ShaderInputType::Texture:
- {
- ComPtr<ITextureResource> texture;
- SLANG_RETURN_ON_FAIL(ShaderRendererUtil::generateTextureResource(
- entry.textureDesc, textureBindFlags, device, texture));
- resource = texture;
+ dstCursor.setObject(shaderObject);
+ return SLANG_OK;
+ }
- // TODO: support UAV textures...
+ SlangResult assign(ShaderCursor const& dstCursor, ShaderInputLayout::ValPtr const& srcVal)
+ {
+ auto& entryCursor = dstCursor;
+ switch(srcVal->kind)
+ {
+ case ShaderInputType::UniformData:
+ return assignData(dstCursor, (ShaderInputLayout::DataVal*) srcVal.Ptr());
- IResourceView::Desc viewDesc;
- viewDesc.type = IResourceView::Type::ShaderResource;
- viewDesc.format = texture->getDesc()->format;
- auto textureView = device->createTextureView(
- texture,
- viewDesc);
+ case ShaderInputType::Buffer:
+ return assignBuffer(dstCursor, (ShaderInputLayout::BufferVal*) srcVal.Ptr());
- if (!textureView)
- {
- return SLANG_FAIL;
- }
+ case ShaderInputType::CombinedTextureSampler:
+ return assignCombinedTextureSampler(dstCursor, (ShaderInputLayout::CombinedTextureSamplerVal*) srcVal.Ptr());
- entryCursor.setResource(textureView);
- }
- break;
+ case ShaderInputType::Texture:
+ return assignTexture(dstCursor, (ShaderInputLayout::TextureVal*) srcVal.Ptr());
case ShaderInputType::Sampler:
- {
- auto sampler = _createSamplerState(device, entry.samplerDesc);
-
- entryCursor.setSampler(sampler);
- }
- break;
+ return assignSampler(dstCursor, (ShaderInputLayout::SamplerVal*) srcVal.Ptr());
case ShaderInputType::Object:
- {
- auto typeName = entry.objectDesc.typeName;
- slang::TypeReflection* slangType = nullptr;
- if(typeName.getLength() != 0)
- {
- // If the input line specified the name of the type
- // to allocate, then we use it directly.
- //
- slangType = slangReflection->findTypeByName(typeName.getBuffer());
- }
- else
- {
- // if the user did not specify what type to allocate,
- // then we will infer the type from the type of the
- // value pointed to by `entryCursor`.
- //
- auto slangTypeLayout = entryCursor.getTypeLayout();
- switch(slangTypeLayout->getKind())
- {
- default:
- break;
-
- case slang::TypeReflection::Kind::ConstantBuffer:
- case slang::TypeReflection::Kind::ParameterBlock:
- // If the cursor is pointing at a constant buffer
- // or parameter block, then we assume the user
- // actually means to allocate an object based on
- // the element type of the block.
- //
- slangTypeLayout = slangTypeLayout->getElementTypeLayout();
- break;
- }
- slangType = slangTypeLayout->getType();
- }
+ return assignObject(dstCursor, (ShaderInputLayout::ObjectVal*) srcVal.Ptr());
- ComPtr<IShaderObject> shaderObject = device->createShaderObject(slangType);
-
- entryCursor.setObject(shaderObject);
- }
- break;
+ case ShaderInputType::Aggregate:
+ return assignAggregate(dstCursor, (ShaderInputLayout::AggVal*) srcVal.Ptr());
default:
assert(!"Unhandled type");
return SLANG_FAIL;
}
-
- if(entry.isOutput)
- {
- ShaderOutputPlan::Item item;
- item.inputLayoutEntryIndex = entryIndex;
- item.resource = resource;
- item.typeLayout = typeLayout;
- ioOutputPlan.items.add(item);
- }
-
}
- return SLANG_OK;
+};
+
+SlangResult _assignVarsFromLayout(
+ IDevice* device,
+ IShaderObject* shaderObject,
+ ShaderInputLayout const& layout,
+ ShaderOutputPlan& ioOutputPlan,
+ slang::ProgramLayout* slangReflection)
+{
+ AssignValsFromLayoutContext context(device, ioOutputPlan, slangReflection);
+ ShaderCursor rootCursor = ShaderCursor(shaderObject);
+ return context.assign(rootCursor, layout.rootVal);
}
-void ShaderObjectRenderTestApp::applyBinding(PipelineType pipelineType, ICommandEncoder* encoder)
+void RenderTestApp::applyBinding(PipelineType pipelineType, ICommandEncoder* encoder)
{
switch (pipelineType)
{
@@ -409,7 +404,7 @@ void ShaderObjectRenderTestApp::applyBinding(PipelineType pipelineType, ICommand
}
}
-SlangResult ShaderObjectRenderTestApp::initialize(
+SlangResult RenderTestApp::initialize(
SlangSession* session,
IDevice* device,
const Options& options,
@@ -507,12 +502,6 @@ SlangResult ShaderObjectRenderTestApp::initialize(
return m_pipelineState ? SLANG_OK : SLANG_FAIL;
}
-void ShaderObjectRenderTestApp::finalizeImpl()
-{
- m_programVars = nullptr;
- RenderTestApp::finalizeImpl();
-}
-
Result RenderTestApp::_initializeShaders(
SlangSession* session,
IDevice* device,
@@ -602,7 +591,7 @@ void RenderTestApp::_initializeRenderPass()
m_device->createRenderPassLayout(renderPassDesc, m_renderPass.writeRef());
}
-void ShaderObjectRenderTestApp::setProjectionMatrix(IResourceCommandEncoder* encoder)
+void RenderTestApp::setProjectionMatrix(IResourceCommandEncoder* encoder)
{
SLANG_UNUSED(encoder);
auto info = m_device->getDeviceInfo();
@@ -639,8 +628,7 @@ void RenderTestApp::runCompute(IComputeCommandEncoder* encoder)
void RenderTestApp::finalize()
{
- finalizeImpl();
-
+ m_programVars = nullptr;
m_inputLayout = nullptr;
m_vertexBuffer = nullptr;
m_shaderProgram = nullptr;
@@ -653,11 +641,7 @@ void RenderTestApp::finalize()
m_device = nullptr;
}
-void RenderTestApp::finalizeImpl()
-{
-}
-
-Result ShaderObjectRenderTestApp::writeBindingOutput(const char* fileName)
+Result RenderTestApp::writeBindingOutput(const char* fileName)
{
// Wait until everything is complete
m_queue->wait();
@@ -671,23 +655,51 @@ Result ShaderObjectRenderTestApp::writeBindingOutput(const char* fileName)
for(auto outputItem : m_outputPlan.items)
{
- auto& inputEntry = m_shaderInputLayout.entries[outputItem.inputLayoutEntryIndex];
- assert(inputEntry.isOutput);
-
auto resource = outputItem.resource;
if (resource && resource->getType() == IResource::Type::Buffer)
{
IBufferResource* bufferResource = static_cast<IBufferResource*>(resource.get());
- const size_t bufferSize = bufferResource->getDesc()->sizeInBytes;
+ auto bufferDesc = *bufferResource->getDesc();
+ const size_t bufferSize = bufferDesc.sizeInBytes;
ComPtr<ISlangBlob> blob;
- m_device->readBufferResource(bufferResource, 0, bufferSize, blob.writeRef());
+ if(bufferDesc.cpuAccessFlags & IResource::AccessFlag::Read)
+ {
+ // The buffer is already allocated for CPU access, so we can read it back directly.
+ //
+ m_device->readBufferResource(bufferResource, 0, bufferSize, blob.writeRef());
+ }
+ else
+ {
+ // The buffer is not CPU-readable, so we will copy it using a staging buffer.
+
+ auto stagingBufferDesc = bufferDesc;
+ stagingBufferDesc.cpuAccessFlags = IResource::AccessFlag::Read;
+ stagingBufferDesc.bindFlags = 0;
+
+ ComPtr<IBufferResource> stagingBuffer;
+ SLANG_RETURN_ON_FAIL(m_device->createBufferResource(IResource::Usage::CopyDest, stagingBufferDesc, nullptr, stagingBuffer.writeRef()));
+
+ ComPtr<ICommandBuffer> commandBuffer;
+ SLANG_RETURN_ON_FAIL(m_queue->createCommandBuffer(commandBuffer.writeRef()));
+
+ ComPtr<IResourceCommandEncoder> encoder;
+ commandBuffer->encodeResourceCommands(encoder.writeRef());
+ encoder->copyBuffer(stagingBuffer, 0, bufferResource, 0, bufferSize);
+ encoder->endEncoding();
+
+ commandBuffer->close();
+ m_queue->executeCommandBuffer(commandBuffer);
+ m_queue->wait();
+
+ m_device->readBufferResource(stagingBuffer, 0, bufferSize, blob.writeRef());
+ }
+
if (!blob)
{
return SLANG_FAIL;
}
const SlangResult res = ShaderInputLayout::writeBinding(
- inputEntry,
m_options.outputUsingType ? outputItem.typeLayout : nullptr, // TODO: always output using type
blob->getBufferPointer(),
bufferSize,
@@ -696,7 +708,8 @@ Result ShaderObjectRenderTestApp::writeBindingOutput(const char* fileName)
}
else
{
- printf("invalid output type at %d.\n", int(outputItem.inputLayoutEntryIndex));
+ auto typeName = outputItem.typeLayout->getName();
+ printf("invalid output type '%s'.\n", typeName ? typeName : "UNKNOWN");
}
}
return SLANG_OK;
@@ -1101,15 +1114,12 @@ static SlangResult _innerMain(Slang::StdWriters* stdWriters, SlangSession* sessi
}
{
- // TODO: We shouldn't need to heap-allocate the `ShaderObjectRenderTestApp`
- // since there is no longer any meaningful inheritance going on.
- //
- RefPtr<RenderTestApp> app = new ShaderObjectRenderTestApp();
+ RenderTestApp app;
renderDocBeginFrame();
- SLANG_RETURN_ON_FAIL(app->initialize(session, device, options, input));
- app->update();
+ SLANG_RETURN_ON_FAIL(app.initialize(session, device, options, input));
+ app.update();
renderDocEndFrame();
- app->finalize();
+ app.finalize();
return SLANG_OK;
}
}
diff --git a/tools/render-test/shader-input-layout.cpp b/tools/render-test/shader-input-layout.cpp
index 071c694b5..5783c6eb8 100644
--- a/tools/render-test/shader-input-layout.cpp
+++ b/tools/render-test/shader-input-layout.cpp
@@ -42,596 +42,641 @@ namespace renderer_test
return SLANG_SCALAR_TYPE_NONE;
}
- Index ShaderInputLayout::findEntryIndexByName(const String& name) const
+ void ShaderInputLayout::AggVal::addField(ShaderInputLayout::Field const& field)
{
- const Index count = Index(entries.getCount());
- for (Index i = 0; i < count; ++i)
- {
- const auto& entry = entries[i];
- if (entry.name == name)
- {
- return Index(i);
- }
- }
- return -1;
+ fields.add(field);
}
- static bool _isCPULikeBindingTarget(SlangCompileTarget target)
+ void ShaderInputLayout::ArrayVal::addField(ShaderInputLayout::Field const& field)
{
- // CUDA and C++ are 'CPULike' in terms of their binding mechanism
- switch (target)
+ vals.add(field.val);
+ }
+
+ class ShaderInputLayoutFormatException : public Exception
+ {
+ public:
+ ShaderInputLayoutFormatException(String message)
+ : Exception(message)
+ {}
+ };
+
+ struct ShaderInputLayoutParser
+ {
+ ShaderInputLayout* layout;
+ RandomGenerator* rand;
+
+ ShaderInputLayoutParser(ShaderInputLayout* layout, RandomGenerator* rand)
+ : layout(layout)
+ , rand(rand)
+ {}
+
+ RefPtr<ShaderInputLayout::ParentVal> parentVal;
+ List<RefPtr<ShaderInputLayout::ParentVal>> parentValStack;
+
+ SlangResult parseOption(TokenReader& parser, String const& word, ShaderInputLayout::TextureVal* val)
{
- case SLANG_C_SOURCE:
- case SLANG_CPP_SOURCE:
- case SLANG_EXECUTABLE:
- case SLANG_SHARED_LIBRARY:
- case SLANG_HOST_CALLABLE:
- case SLANG_CUDA_SOURCE:
- case SLANG_PTX:
+ if (word == "depth")
+ {
+ val->textureDesc.isDepthTexture = true;
+ }
+ else if (word == "arrayLength")
+ {
+ parser.Read("=");
+ val->textureDesc.arrayLength = parser.ReadInt();
+ }
+ else if (word == "size")
{
- return true;
+ parser.Read("=");
+ auto size = parser.ReadInt();
+ val->textureDesc.size = size;
}
- default: return false;
+ else if (word == "content")
+ {
+ parser.Read("=");
+ auto contentWord = parser.ReadWord();
+ if (contentWord == "zero")
+ val->textureDesc.content = InputTextureContent::Zero;
+ else if (contentWord == "one")
+ val->textureDesc.content = InputTextureContent::One;
+ else if (contentWord == "chessboard")
+ val->textureDesc.content = InputTextureContent::ChessBoard;
+ else
+ val->textureDesc.content = InputTextureContent::Gradient;
+ }
+ else if(word == "mipMaps")
+ {
+ parser.Read("=");
+ val->textureDesc.mipMapCount = int(parser.ReadInt());
+ }
+ else if(word == "format")
+ {
+ val->textureDesc.format = parseFormatOption(parser);
+ }
+ else
+ {
+ return SLANG_FAIL;
+ }
+ return SLANG_OK;
}
- }
- void ShaderInputLayout::updateForTarget(SlangCompileTarget target)
- {
- if (!_isCPULikeBindingTarget(target))
+ SlangResult parseOption(TokenReader& parser, String const& word, ShaderInputLayout::SamplerVal* val)
{
- int count = int(entries.getCount());
- for (int i = 0; i < count; ++i)
+ if (word == "depthCompare")
{
- auto& entry = entries[i];
- if (entry.onlyCPULikeBinding)
- {
- entries.removeAt(i);
- i--;
- count--;
- }
+ val->samplerDesc.isCompareSampler = true;
}
+ else
+ {
+ return SLANG_FAIL;
+ }
+ return SLANG_OK;
}
- }
- void ShaderInputLayout::parse(RandomGenerator* rand, const char * source)
- {
- entries.clear();
- globalSpecializationArgs.clear();
- entryPointSpecializationArgs.clear();
- auto lines = Split(source, '\n');
- for (auto & line : lines)
+ SlangResult parseOption(TokenReader& parser, String const& word, ShaderInputLayout::DataValBase* val)
{
- if (line.startsWith("//TEST_INPUT:"))
+ if (word == "data")
{
- auto lineContent = line.subString(13, line.getLength() - 13);
- TokenReader parser(lineContent);
- try
+ parser.Read("=");
+
+ parser.Read("[");
+ uint32_t offset = 0;
+ while (!parser.IsEnd() && !parser.LookAhead("]"))
{
- if (parser.LookAhead("entryPointSpecializationArg")
- || parser.LookAhead("type")
- || parser.LookAhead("entryPointExistentialType"))
+ bool negate = false;
+ if(parser.NextToken().Type == TokenType::OpSub)
{
parser.ReadToken();
- StringBuilder typeExp;
- while (!parser.IsEnd())
- typeExp << parser.ReadToken().Content;
- entryPointSpecializationArgs.add(typeExp);
+ negate = true;
}
- else if (parser.LookAhead("globalSpecializationArg")
- || parser.LookAhead("global_type")
- || parser.LookAhead("globalExistentialType"))
+
+ if (parser.NextToken().Type == TokenType::IntLiteral)
{
- parser.ReadToken();
- StringBuilder typeExp;
- while (!parser.IsEnd())
- typeExp << parser.ReadToken().Content;
- globalSpecializationArgs.add(typeExp);
+ uint32_t value = parser.ReadUInt();
+ if(negate) value = uint32_t(-int32_t(value));
+ val->bufferData.add(value);
}
else
{
- ShaderInputLayoutEntry entry;
+ auto floatNum = parser.ReadFloat();
+ if(negate) floatNum = -floatNum;
+ val->bufferData.add(*(unsigned int*)&floatNum);
+ }
+ offset += 4;
+ }
+ parser.Read("]");
+ }
+ else
+ {
+ return SLANG_FAIL;
+ }
+ return SLANG_OK;
+ }
- if (parser.LookAhead("array"))
- {
- entry.type = ShaderInputType::Array;
- }
- else if (parser.LookAhead("cbuffer"))
- {
- entry.type = ShaderInputType::Buffer;
- entry.bufferDesc.type = InputBufferType::ConstantBuffer;
- }
- else if (parser.LookAhead("object"))
- {
- entry.type = ShaderInputType::Object;
- }
- else if (parser.LookAhead("root_constants"))
- {
- entry.type = ShaderInputType::Buffer;
- entry.bufferDesc.type = InputBufferType::RootConstantBuffer;
- }
- else if (parser.LookAhead("Uniform"))
- {
- entry.type = ShaderInputType::Uniform;
- }
- else if (parser.LookAhead("ubuffer"))
- {
- entry.type = ShaderInputType::Buffer;
- entry.bufferDesc.type = InputBufferType::StorageBuffer;
- }
- else if (parser.LookAhead("Texture1D"))
- {
- entry.type = ShaderInputType::Texture;
- entry.textureDesc.dimension = 1;
- }
- else if (parser.LookAhead("Texture2D"))
- {
- entry.type = ShaderInputType::Texture;
- entry.textureDesc.dimension = 2;
- }
- else if (parser.LookAhead("Texture3D"))
- {
- entry.type = ShaderInputType::Texture;
- entry.textureDesc.dimension = 3;
- }
- else if (parser.LookAhead("TextureCube"))
- {
- entry.type = ShaderInputType::Texture;
- entry.textureDesc.dimension = 2;
- entry.textureDesc.isCube = true;
- }
- else if (parser.LookAhead("RWTexture1D"))
- {
- entry.type = ShaderInputType::Texture;
- entry.textureDesc.dimension = 1;
- entry.textureDesc.isRWTexture = true;
- }
- else if (parser.LookAhead("RWTexture2D"))
- {
- entry.type = ShaderInputType::Texture;
- entry.textureDesc.dimension = 2;
- entry.textureDesc.isRWTexture = true;
- }
- else if (parser.LookAhead("RWTexture3D"))
+ SlangResult parseOption(TokenReader& parser, String const& word, ShaderInputLayout::BufferVal* val)
+ {
+ if (word == "stride")
+ {
+ parser.Read("=");
+ val->bufferDesc.stride = parser.ReadInt();
+ }
+ else if (word == "random")
+ {
+ parser.Read("(");
+ // Read the type
+ String type = parser.ReadWord();
+ SlangScalarType scalarType = _getScalarType(type.getUnownedSlice());
+ if (scalarType == SLANG_SCALAR_TYPE_NONE)
+ {
+ StringBuilder scalarTypeNames;
+ for (const auto& info : g_scalarTypeInfos)
+ {
+ if (scalarTypeNames.getLength() != 0)
{
- entry.type = ShaderInputType::Texture;
- entry.textureDesc.dimension = 3;
- entry.textureDesc.isRWTexture = true;
+ scalarTypeNames << ", ";
}
- else if (parser.LookAhead("RWTextureCube"))
+ scalarTypeNames << info.name;
+ }
+
+ throw ShaderInputLayoutFormatException(StringBuilder() << "Expecting " << scalarTypeNames << " " << parser.NextToken().Position.Line);
+ }
+
+ parser.Read(",");
+ const int size = int(parser.ReadUInt());
+
+ switch (scalarType)
+ {
+ case SLANG_SCALAR_TYPE_INT32:
+ {
+ bool hasRange = false;
+
+ int32_t minValue = -0x7fffffff - 1;
+ int32_t maxValue = 0x7fffffff;
+
+ if (parser.LookAhead(","))
{
- entry.type = ShaderInputType::Texture;
- entry.textureDesc.dimension = 2;
- entry.textureDesc.isCube = true;
- entry.textureDesc.isRWTexture = true;
+ hasRange = true;
+ parser.ReadToken();
+ minValue = parser.ReadInt();
+
+ if (parser.LookAhead(","))
+ {
+ parser.ReadToken();
+ maxValue = parser.ReadInt();
+ }
}
- else if (parser.LookAhead("Sampler"))
+ SLANG_ASSERT(minValue <= maxValue);
+ maxValue = (maxValue >= minValue) ? maxValue : minValue;
+
+ // Generate the data
+ val->bufferData.setCount(size);
+
+ int32_t* dst = (int32_t*)val->bufferData.getBuffer();
+ for (int i = 0; i < size; ++i)
{
- entry.type = ShaderInputType::Sampler;
+ dst[i] = hasRange ? rand->nextInt32InRange(minValue, maxValue) : rand->nextInt32();
}
- else if (parser.LookAhead("Sampler1D"))
+ break;
+ }
+ case SLANG_SCALAR_TYPE_UINT32:
+ {
+ bool hasRange = false;
+ uint32_t minValue = 0;
+ uint32_t maxValue = 0xffffffff;
+
+ if (parser.LookAhead(","))
{
- entry.type = ShaderInputType::CombinedTextureSampler;
- entry.textureDesc.dimension = 1;
+ parser.ReadToken();
+ minValue = parser.ReadUInt();
+
+ hasRange = true;
+
+ if (parser.LookAhead(","))
+ {
+ parser.ReadToken();
+ maxValue = parser.ReadUInt();
+ }
}
- else if (parser.LookAhead("Sampler2D"))
+
+ SLANG_ASSERT(minValue <= maxValue);
+ maxValue = (maxValue >= minValue) ? maxValue : minValue;
+
+ // Generate the data
+ val->bufferData.setCount(size);
+
+ uint32_t* dst = (uint32_t*)val->bufferData.getBuffer();
+ for (int i = 0; i < size; ++i)
{
- entry.type = ShaderInputType::CombinedTextureSampler;
- entry.textureDesc.dimension = 2;
+ dst[i] = hasRange ? rand->nextUInt32InRange(minValue, maxValue) : rand->nextUInt32();
}
- else if (parser.LookAhead("Sampler3D"))
+
+ break;
+ }
+ case SLANG_SCALAR_TYPE_FLOAT32:
+ {
+ float minValue = -1.0f;
+ float maxValue = 1.0f;
+
+ if (parser.LookAhead(","))
{
- entry.type = ShaderInputType::CombinedTextureSampler;
- entry.textureDesc.dimension = 3;
+ parser.ReadToken();
+ minValue = parser.ReadFloat();
+
+ if (parser.LookAhead(","))
+ {
+ parser.ReadToken();
+ maxValue = parser.ReadFloat();
+ }
}
- else if (parser.LookAhead("SamplerCube"))
+
+ SLANG_ASSERT(minValue <= maxValue);
+ maxValue = (maxValue >= minValue) ? maxValue : minValue;
+
+ // Generate the data
+ val->bufferData.setCount(size);
+
+ float* dst = (float*)val->bufferData.getBuffer();
+ for (int i = 0; i < size; ++i)
{
- entry.type = ShaderInputType::CombinedTextureSampler;
- entry.textureDesc.dimension = 2;
- entry.textureDesc.isCube = true;
+ dst[i] = (rand->nextUnitFloat32() * (maxValue - minValue)) + minValue;
}
- else if (parser.LookAhead("render_targets"))
+ break;
+ }
+ }
+
+ // Read the range
+
+ parser.Read(")");
+ }
+ else if(word == "format")
+ {
+ val->bufferDesc.format = parseFormatOption(parser);
+ }
+ else
+ {
+ return parseOption(parser, word, static_cast<ShaderInputLayout::DataValBase*>(val));
+ }
+ return SLANG_OK;
+ }
+
+ SlangResult parseOption(TokenReader& parser, String const& word, ShaderInputLayout::ObjectVal* val)
+ {
+ if( word == "type" )
+ {
+ parser.Read("=");
+ val->typeName = parser.ReadWord();
+ }
+ else
+ {
+ return SLANG_FAIL;
+ }
+ return SLANG_OK;
+ }
+
+ Format parseFormatOption(TokenReader& parser)
+ {
+ Format format = Format::Unknown;
+
+ parser.Read("=");
+ auto formatWord = parser.ReadWord();
+ if(formatWord == "R_UInt32")
+ {
+ format = Format::R_UInt32;
+ }
+ else if (formatWord == "R_Float32")
+ {
+ format = Format::R_Float32;
+ }
+ else if (formatWord == "RGBA_Unorm_UInt8")
+ {
+ format = Format::RGBA_Unorm_UInt8;
+ }
+ else
+ {
+ // TODO: an error message here
+ }
+ return format;
+ }
+
+ template<typename T>
+ void maybeParseOptions(TokenReader& parser, T* val)
+ {
+ // parse options
+ if (parser.LookAhead("("))
+ {
+ parser.Read("(");
+ while (!parser.IsEnd() && !parser.LookAhead(")"))
+ {
+ auto word = parser.ReadWord();
+ if( SLANG_FAILED(parseOption(parser, word, val)) )
+ {
+ throw ShaderInputLayoutFormatException(String("Unsupported option '") + word + String("' at line ") + String(parser.NextToken().Position.Line));
+ }
+
+ if (parser.LookAhead(","))
+ parser.Read(",");
+ else
+ break;
+ }
+ parser.Read(")");
+ }
+ }
+
+ RefPtr<ShaderInputLayout::Val> parseVal(TokenReader& parser)
+ {
+ auto word = parser.NextToken().Content;
+ if (parser.AdvanceIf("begin_array"))
+ {
+ RefPtr<ShaderInputLayout::ArrayVal> val = new ShaderInputLayout::ArrayVal;
+ pushParentVal(val);
+ return val;
+ }
+ else if (parser.AdvanceIf("begin_object"))
+ {
+ RefPtr<ShaderInputLayout::ObjectVal> val = new ShaderInputLayout::ObjectVal;
+ maybeParseOptions(parser, val.Ptr());
+
+ RefPtr<ShaderInputLayout::AggVal> contentVal = new ShaderInputLayout::AggVal;
+ val->contentVal = contentVal;
+ pushParentVal(contentVal);
+
+ return val;
+ }
+ else if (parser.AdvanceIf("uniform"))
+ {
+ RefPtr<ShaderInputLayout::DataVal> val = new ShaderInputLayout::DataVal;
+ maybeParseOptions(parser, val.Ptr());
+ return val;
+ }
+ else if (parser.AdvanceIf("cbuffer"))
+ {
+ // A `cbuffer` is basically just an object where the content of
+ // the object is being provided by `uniform` data instead.
+
+ RefPtr<ShaderInputLayout::ObjectVal> objVal = new ShaderInputLayout::ObjectVal;
+
+ RefPtr<ShaderInputLayout::DataVal> dataVal = new ShaderInputLayout::DataVal;
+ maybeParseOptions(parser, dataVal.Ptr());
+
+ objVal->contentVal = dataVal;
+
+ return objVal;
+ }
+ else if (parser.AdvanceIf("ubuffer"))
+ {
+ RefPtr<ShaderInputLayout::BufferVal> val = new ShaderInputLayout::BufferVal;
+ val->bufferDesc.type = InputBufferType::StorageBuffer;
+ maybeParseOptions(parser, val.Ptr());
+ return val;
+ }
+ else if (parser.AdvanceIf("Texture1D"))
+ {
+ RefPtr<ShaderInputLayout::TextureVal> val = new ShaderInputLayout::TextureVal;
+ val->textureDesc.dimension = 1;
+ maybeParseOptions(parser, val.Ptr());
+ return val;
+ }
+ else if (parser.AdvanceIf("Texture2D"))
+ {
+ RefPtr<ShaderInputLayout::TextureVal> val = new ShaderInputLayout::TextureVal;
+ val->textureDesc.dimension = 2;
+ maybeParseOptions(parser, val.Ptr());
+ return val;
+ }
+ else if (parser.AdvanceIf("Texture3D"))
+ {
+ RefPtr<ShaderInputLayout::TextureVal> val = new ShaderInputLayout::TextureVal;
+ val->textureDesc.dimension = 3;
+ maybeParseOptions(parser, val.Ptr());
+ return val;
+ }
+ else if (parser.AdvanceIf("TextureCube"))
+ {
+ RefPtr<ShaderInputLayout::TextureVal> val = new ShaderInputLayout::TextureVal;
+ val->textureDesc.dimension = 2;
+ val->textureDesc.isCube = true;
+ maybeParseOptions(parser, val.Ptr());
+ return val;
+ }
+ else if (parser.AdvanceIf("RWTexture1D"))
+ {
+ RefPtr<ShaderInputLayout::TextureVal> val = new ShaderInputLayout::TextureVal;
+ val->textureDesc.dimension = 1;
+ val->textureDesc.isRWTexture = true;
+ maybeParseOptions(parser, val.Ptr());
+ return val;
+ }
+ else if (parser.AdvanceIf("RWTexture2D"))
+ {
+ RefPtr<ShaderInputLayout::TextureVal> val = new ShaderInputLayout::TextureVal;
+ val->textureDesc.dimension = 2;
+ val->textureDesc.isRWTexture = true;
+ maybeParseOptions(parser, val.Ptr());
+ return val;
+ }
+ else if (parser.AdvanceIf("RWTexture3D"))
+ {
+ RefPtr<ShaderInputLayout::TextureVal> val = new ShaderInputLayout::TextureVal;
+ val->textureDesc.dimension = 3;
+ val->textureDesc.isRWTexture = true;
+ maybeParseOptions(parser, val.Ptr());
+ return val;
+ }
+ else if (parser.AdvanceIf("RWTextureCube"))
+ {
+ RefPtr<ShaderInputLayout::TextureVal> val = new ShaderInputLayout::TextureVal;
+ val->textureDesc.dimension = 2;
+ val->textureDesc.isCube = true;
+ val->textureDesc.isRWTexture = true;
+ maybeParseOptions(parser, val.Ptr());
+ return val;
+ }
+ else if (parser.AdvanceIf("Sampler"))
+ {
+ RefPtr<ShaderInputLayout::SamplerVal> val = new ShaderInputLayout::SamplerVal;
+ maybeParseOptions(parser, val.Ptr());
+ return val;
+ }
+ // TODO: We can include combined texture/sampler cases
+ // here, but we don't really have tests for them and
+ // it might be better to save it for a point where
+ // we support hierarchical binding values more directly.
+ else
+ {
+ throw ShaderInputLayoutFormatException(String("Unknown shader input type '") + word + String("' at line") + String(parser.NextToken().Position.Line));
+ }
+ parser.ReadToken();
+ }
+
+ void parseFieldBindings(TokenReader& parser, ShaderInputLayout::Field& ioField)
+ {
+ // parse bindings
+ if (parser.LookAhead(":"))
+ {
+ parser.Read(":");
+ while (!parser.IsEnd())
+ {
+ if (parser.AdvanceIf("out"))
+ {
+ ioField.val->isOutput = true;
+ }
+ else if (parser.AdvanceIf("name"))
+ {
+ // Optionally consume '='
+ if (parser.NextToken().Type == TokenType::OpAssign)
{
- numRenderTargets = parser.ReadInt();
- continue;
+ parser.ReadToken();
}
- else
+
+ StringBuilder builder;
+
+ Token nameToken = parser.ReadToken();
+ if (nameToken.Type != TokenType::Identifier)
{
- throw TextFormatException(String("Invalid input syntax at line ") + String(parser.NextToken().Position.Line));
+ throw ShaderInputLayoutFormatException(StringBuilder() << "Invalid input syntax at line " << parser.NextToken().Position.Line);
}
- parser.ReadToken();
- // parse options
- if (parser.LookAhead("("))
+ builder << nameToken.Content;
+
+ while (!parser.IsEnd())
{
- parser.Read("(");
- while (!parser.IsEnd() && !parser.LookAhead(")"))
+ Token token = parser.NextToken(0);
+
+ if (token.Type == TokenType::LBracket)
{
- auto word = parser.ReadWord();
- if (word == "depth")
- {
- entry.textureDesc.isDepthTexture = true;
- }
- else if (word == "depthCompare")
- {
- entry.samplerDesc.isCompareSampler = true;
- }
- else if (word == "arrayLength")
- {
- parser.Read("=");
- entry.textureDesc.arrayLength = parser.ReadInt();
- }
- else if (word == "stride")
- {
- parser.Read("=");
- entry.bufferDesc.stride = parser.ReadInt();
- }
- else if (word == "size")
- {
- parser.Read("=");
- auto size = parser.ReadInt();
- if (entry.type == ShaderInputType::Array)
- {
- entry.arrayDesc.size = size;
- }
- else
- {
- entry.textureDesc.size = size;
- }
- }
- else if (word == "random")
- {
- parser.Read("(");
- // Read the type
- String type = parser.ReadWord();
- SlangScalarType scalarType = _getScalarType(type.getUnownedSlice());
- if (scalarType == SLANG_SCALAR_TYPE_NONE)
- {
- StringBuilder scalarTypeNames;
- for (const auto& info : g_scalarTypeInfos)
- {
- if (scalarTypeNames.getLength() != 0)
- {
- scalarTypeNames << ", ";
- }
- scalarTypeNames << info.name;
- }
-
- throw TextFormatException(StringBuilder() << "Expecting " << scalarTypeNames << " " << parser.NextToken().Position.Line);
- }
-
- parser.Read(",");
- const int size = int(parser.ReadUInt());
-
- switch (scalarType)
- {
- case SLANG_SCALAR_TYPE_INT32:
- {
- bool hasRange = false;
-
- int32_t minValue = -0x7fffffff - 1;
- int32_t maxValue = 0x7fffffff;
-
- if (parser.LookAhead(","))
- {
- hasRange = true;
- parser.ReadToken();
- minValue = parser.ReadInt();
-
- if (parser.LookAhead(","))
- {
- parser.ReadToken();
- maxValue = parser.ReadInt();
- }
- }
- SLANG_ASSERT(minValue <= maxValue);
- maxValue = (maxValue >= minValue) ? maxValue : minValue;
-
- // Generate the data
- entry.bufferData.setCount(size);
-
- int32_t* dst = (int32_t*)entry.bufferData.getBuffer();
- for (int i = 0; i < size; ++i)
- {
- dst[i] = hasRange ? rand->nextInt32InRange(minValue, maxValue) : rand->nextInt32();
- }
- break;
- }
- case SLANG_SCALAR_TYPE_UINT32:
- {
- bool hasRange = false;
- uint32_t minValue = 0;
- uint32_t maxValue = 0xffffffff;
-
- if (parser.LookAhead(","))
- {
- parser.ReadToken();
- minValue = parser.ReadUInt();
-
- hasRange = true;
-
- if (parser.LookAhead(","))
- {
- parser.ReadToken();
- maxValue = parser.ReadUInt();
- }
- }
-
- SLANG_ASSERT(minValue <= maxValue);
- maxValue = (maxValue >= minValue) ? maxValue : minValue;
-
- // Generate the data
- entry.bufferData.setCount(size);
-
- uint32_t* dst = (uint32_t*)entry.bufferData.getBuffer();
- for (int i = 0; i < size; ++i)
- {
- dst[i] = hasRange ? rand->nextUInt32InRange(minValue, maxValue) : rand->nextUInt32();
- }
-
- break;
- }
- case SLANG_SCALAR_TYPE_FLOAT32:
- {
- float minValue = -1.0f;
- float maxValue = 1.0f;
-
- if (parser.LookAhead(","))
- {
- parser.ReadToken();
- minValue = parser.ReadFloat();
-
- if (parser.LookAhead(","))
- {
- parser.ReadToken();
- maxValue = parser.ReadFloat();
- }
- }
-
- SLANG_ASSERT(minValue <= maxValue);
- maxValue = (maxValue >= minValue) ? maxValue : minValue;
-
- // Generate the data
- entry.bufferData.setCount(size);
-
- float* dst = (float*)entry.bufferData.getBuffer();
- for (int i = 0; i < size; ++i)
- {
- dst[i] = (rand->nextUnitFloat32() * (maxValue - minValue)) + minValue;
- }
- break;
- }
- }
-
- // Read the range
-
- parser.Read(")");
- }
- else if (word == "data")
- {
- parser.Read("=");
-
- parser.Read("[");
- uint32_t offset = 0;
- while (!parser.IsEnd() && !parser.LookAhead("]"))
- {
- RTTIDataEntry rttiEntry;
- if (parser.LookAhead("rtti"))
- {
- parser.ReadToken();
- parser.Read("(");
- rttiEntry.type = RTTIDataEntryType::RTTIObject;
- rttiEntry.typeName = parser.ReadWord();
- rttiEntry.offset = offset;
- parser.Read(")");
- offset += 8;
- entry.rttiEntries.add(rttiEntry);
- entry.bufferData.add(0);
- entry.bufferData.add(0);
- continue;
- }
- else if (parser.LookAhead("witness"))
- {
- parser.ReadToken();
- parser.Read("(");
- rttiEntry.type = RTTIDataEntryType::WitnessTable;
- rttiEntry.typeName = parser.ReadWord();
- parser.Read(",");
- rttiEntry.interfaceName = parser.ReadWord();
- rttiEntry.offset = offset;
- parser.Read(")");
- offset += 8;
- entry.rttiEntries.add(rttiEntry);
- entry.bufferData.add(0);
- entry.bufferData.add(0);
- continue;
- }
- else if (parser.LookAhead("handle"))
- {
- BindlessHandleDataEntry handleEntry;
- parser.ReadToken();
- parser.Read("(");
- handleEntry.name = parser.ReadWord();
- handleEntry.offset = offset;
- parser.Read(")");
- offset += 8;
- entry.bindlessHandleEntry.add(handleEntry);
- entry.bufferData.add(0);
- entry.bufferData.add(0);
- continue;
- }
-
- bool negate = false;
- if(parser.NextToken().Type == TokenType::OpSub)
- {
- parser.ReadToken();
- negate = true;
- }
-
- if (parser.NextToken().Type == TokenType::IntLiteral)
- {
- uint32_t val = parser.ReadUInt();
- if(negate) val = uint32_t(-int32_t(val));
- entry.bufferData.add(val);
- }
- else
- {
- auto floatNum = parser.ReadFloat();
- if(negate) floatNum = -floatNum;
- entry.bufferData.add(*(unsigned int*)&floatNum);
- }
- offset += 4;
- }
- parser.Read("]");
- }
- else if (word == "content")
- {
- parser.Read("=");
- auto contentWord = parser.ReadWord();
- if (contentWord == "zero")
- entry.textureDesc.content = InputTextureContent::Zero;
- else if (contentWord == "one")
- entry.textureDesc.content = InputTextureContent::One;
- else if (contentWord == "chessboard")
- entry.textureDesc.content = InputTextureContent::ChessBoard;
- else
- entry.textureDesc.content = InputTextureContent::Gradient;
- }
- else if(word == "format")
- {
- Format format = Format::Unknown;
-
- parser.Read("=");
- auto formatWord = parser.ReadWord();
- if(formatWord == "R_UInt32")
- {
- format = Format::R_UInt32;
- }
- else if (formatWord == "R_Float32")
- {
- format = Format::R_Float32;
- }
- else if (formatWord == "RGBA_Unorm_UInt8")
- {
- format = Format::RGBA_Unorm_UInt8;
- }
-
- entry.textureDesc.format = format;
- entry.bufferDesc.format = format;
- }
- else if(word == "mipMaps")
- {
- parser.Read("=");
- entry.textureDesc.mipMapCount = int(parser.ReadInt());
- }
- else if( word == "type" )
- {
- parser.Read("=");
- entry.objectDesc.typeName = parser.ReadWord();
- }
-
- if (parser.LookAhead(","))
- parser.Read(",");
- else
- break;
+ parser.ReadToken();
+ int index = parser.ReadInt();
+ SLANG_ASSERT(index >= 0);
+ parser.ReadMatchingToken(TokenType::RBracket);
+
+ builder << "[" << index << "]";
}
- parser.Read(")");
- }
- // parse bindings
- if (parser.LookAhead(":"))
- {
- parser.Read(":");
- while (!parser.IsEnd())
+ else if (token.Type == TokenType::Dot)
+ {
+ parser.ReadToken();
+ Token identifierToken = parser.ReadMatchingToken(TokenType::Identifier);
+
+ builder << "." << identifierToken.Content;
+ }
+ else if (token.Type == TokenType::Comma)
+ {
+ // Break out
+ break;
+ }
+ else
{
- if (parser.LookAhead("onlyCPULikeBinding"))
- {
- entry.onlyCPULikeBinding = true;
- parser.ReadToken();
- }
- else if (parser.LookAhead("out"))
- {
- parser.ReadToken();
- entry.isOutput = true;
- }
- else if (parser.LookAhead("name"))
- {
- parser.ReadToken();
-
- // Optionally consume '='
- if (parser.NextToken().Type == TokenType::OpAssign)
- {
- parser.ReadToken();
- }
-
- StringBuilder builder;
-
- Token nameToken = parser.ReadToken();
- if (nameToken.Type != TokenType::Identifier)
- {
- throw TextFormatException(StringBuilder() << "Invalid input syntax at line " << parser.NextToken().Position.Line);
- }
- builder << nameToken.Content;
-
- while (!parser.IsEnd())
- {
- Token token = parser.NextToken(0);
-
- if (token.Type == TokenType::LBracket)
- {
- parser.ReadToken();
- int index = parser.ReadInt();
- SLANG_ASSERT(index >= 0);
- parser.ReadMatchingToken(TokenType::RBracket);
-
- builder << "[" << index << "]";
- }
- else if (token.Type == TokenType::Dot)
- {
- parser.ReadToken();
- Token identifierToken = parser.ReadMatchingToken(TokenType::Identifier);
-
- builder << "." << identifierToken.Content;
- }
- else if (token.Type == TokenType::Comma)
- {
- // Break out
- break;
- }
- else
- {
- throw TextFormatException(StringBuilder() << "Invalid input syntax at line " << parser.NextToken().Position.Line);
- }
- }
-
- entry.name = builder;
- }
- else if (parser.LookAhead("bindless"))
- {
- parser.ReadToken();
- entry.isBindlessObject = true;
- }
- else
- {
- fprintf(stderr, "Invalid TEST_INPUT syntax '%s'\n", parser.NextToken().Content.getBuffer());
- break;
- }
-
- if (parser.LookAhead(","))
- parser.Read(",");
+ throw ShaderInputLayoutFormatException(StringBuilder() << "Invalid input syntax at line " << parser.NextToken().Position.Line);
}
}
- entries.add(entry);
+
+ ioField.name = builder;
}
+ else
+ {
+ fprintf(stderr, "Invalid TEST_INPUT syntax '%s'\n", parser.NextToken().Content.getBuffer());
+ break;
+ }
+
+ if (parser.LookAhead(","))
+ parser.Read(",");
}
- catch (const TextFormatException&)
+ }
+ }
+
+ void pushParentVal(ShaderInputLayout::ParentVal* val)
+ {
+ parentValStack.add(parentVal);
+ parentVal = val;
+ }
+
+ void parseValEntry(TokenReader& parser)
+ {
+ auto parentForNewVal = parentVal;
+
+ ShaderInputLayout::Field field;
+ field.val = parseVal(parser);
+ parseFieldBindings(parser, field);
+
+ parentForNewVal->addField(field);
+ }
+
+ void parseLine(TokenReader& parser)
+ {
+ if (parser.LookAhead("entryPointSpecializationArg")
+ || parser.LookAhead("type")
+ || parser.LookAhead("entryPointExistentialType"))
+ {
+ parser.ReadToken();
+ StringBuilder typeExp;
+ while (!parser.IsEnd())
+ typeExp << parser.ReadToken().Content;
+ layout->entryPointSpecializationArgs.add(typeExp);
+ }
+ else if (parser.LookAhead("globalSpecializationArg")
+ || parser.LookAhead("global_type")
+ || parser.LookAhead("globalExistentialType"))
+ {
+ parser.ReadToken();
+ StringBuilder typeExp;
+ while (!parser.IsEnd())
+ typeExp << parser.ReadToken().Content;
+ layout->globalSpecializationArgs.add(typeExp);
+ }
+ else if (parser.AdvanceIf("render_targets"))
+ {
+ layout->numRenderTargets = parser.ReadInt();
+ }
+ else if( parser.AdvanceIf("end") )
+ {
+ parentVal = parentValStack.getLast();
+ parentValStack.removeLast();
+ }
+ else
+ {
+ parseValEntry(parser);
+ }
+ }
+
+ RefPtr<ShaderInputLayout::AggVal> parse(const char * source)
+ {
+ RefPtr<ShaderInputLayout::AggVal> rootVal = new ShaderInputLayout::AggVal;
+ parentVal = rootVal;
+
+ auto lines = Split(source, '\n');
+ for (auto & line : lines)
+ {
+ if (line.startsWith("//TEST_INPUT:"))
{
- StringBuilder msg;
- msg << "Invalid input syntax at line " << parser.NextToken().Position.Line;
- throw TextFormatException(msg);
+ auto lineContent = line.subString(13, line.getLength() - 13);
+ TokenReader parser(lineContent);
+ try
+ {
+ parseLine(parser);
+ }
+ catch (const TextFormatException&)
+ {
+ StringBuilder msg;
+ msg << "Invalid input syntax at line " << parser.NextToken().Position.Line;
+ throw ShaderInputLayoutFormatException(msg);
+ }
}
}
+
+ // TODO: check that stack has been maintained correctly...
+
+ return rootVal;
}
+ };
+
+ void ShaderInputLayout::parse(RandomGenerator* rand, const char * source)
+ {
+ rootVal = nullptr;
+ globalSpecializationArgs.clear();
+ entryPointSpecializationArgs.clear();
+
+ ShaderInputLayoutParser parser(this, rand);
+ rootVal = parser.parse(source);
}
- /* static */SlangResult ShaderInputLayout::writeBinding(const ShaderInputLayoutEntry& entry, slang::TypeLayoutReflection* typeLayout, const void* data, size_t sizeInBytes, WriterHelper writer)
+ /* static */SlangResult ShaderInputLayout::writeBinding(slang::TypeLayoutReflection* typeLayout, const void* data, size_t sizeInBytes, WriterHelper writer)
{
typedef slang::TypeReflection::ScalarType ScalarType;
diff --git a/tools/render-test/shader-input-layout.h b/tools/render-test/shader-input-layout.h
index 01ef5c443..60582aed8 100644
--- a/tools/render-test/shader-input-layout.h
+++ b/tools/render-test/shader-input-layout.h
@@ -14,8 +14,14 @@ using namespace gfx;
enum class ShaderInputType
{
- Buffer, Texture, Sampler, CombinedTextureSampler, Array, Uniform,
+ Buffer,
+ Texture,
+ Sampler,
+ CombinedTextureSampler,
+ Array,
+ UniformData,
Object,
+ Aggregate,
};
enum class InputTextureContent
@@ -40,13 +46,14 @@ struct InputTextureDesc
enum class InputBufferType
{
- ConstantBuffer, StorageBuffer,
- RootConstantBuffer,
+// ConstantBuffer,
+ StorageBuffer,
+// RootConstantBuffer,
};
struct InputBufferDesc
{
- InputBufferType type = InputBufferType::ConstantBuffer;
+ InputBufferType type = InputBufferType::StorageBuffer;
int stride = 0; // stride == 0 indicates an unstructured buffer.
Format format = Format::Unknown;
};
@@ -56,52 +63,6 @@ struct InputSamplerDesc
bool isCompareSampler = false;
};
-struct ArrayDesc
-{
- int size = 0;
-};
-
-enum class RTTIDataEntryType
-{
- RTTIObject, WitnessTable
-};
-struct RTTIDataEntry
-{
- RTTIDataEntryType type;
- Slang::String typeName;
- Slang::String interfaceName;
- unsigned int offset;
-};
-
-struct BindlessHandleDataEntry
-{
- unsigned int offset;
- Slang::String name;
-};
-
-struct InputObjectDesc
-{
- Slang::String typeName;
-};
-
-class ShaderInputLayoutEntry
-{
-public:
- ShaderInputType type;
- Slang::List<unsigned int> bufferData;
- Slang::List<RTTIDataEntry> rttiEntries;
- Slang::List<BindlessHandleDataEntry> bindlessHandleEntry;
- InputTextureDesc textureDesc;
- InputBufferDesc bufferDesc;
- InputSamplerDesc samplerDesc;
- ArrayDesc arrayDesc;
- InputObjectDesc objectDesc;
- bool isOutput = false;
- bool onlyCPULikeBinding = false; ///< If true, only use on targets that have 'uniform' or 'CPU like' binding, like CPU and CUDA
- bool isBindlessObject = false; ///< If true, this is a bindless object with no associated binding point in the shader.
- Slang::String name; ///< Optional name. Useful for binding through reflection.
-};
-
struct TextureData
{
Slang::List<Slang::List<unsigned int>> dataBuffer;
@@ -113,19 +74,140 @@ struct TextureData
class ShaderInputLayout
{
public:
- Slang::List<ShaderInputLayoutEntry> entries;
+ class Val : public Slang::RefObject
+ {
+ public:
+ Val(ShaderInputType kind)
+ : kind(kind)
+ {}
+
+ ShaderInputType kind;
+ bool isOutput = false;
+ };
+ typedef Slang::RefPtr<Val> ValPtr;
+
+ class TextureVal : public Val
+ {
+ public:
+ TextureVal()
+ : Val(ShaderInputType::Texture)
+ {}
+
+ InputTextureDesc textureDesc;
+ };
+
+ class DataValBase : public Val
+ {
+ public:
+ DataValBase(ShaderInputType kind)
+ : Val(kind)
+ {}
+
+ Slang::List<unsigned int> bufferData;
+ };
+
+ class BufferVal : public DataValBase
+ {
+ public:
+ BufferVal()
+ : DataValBase(ShaderInputType::Buffer)
+ {}
+
+ InputBufferDesc bufferDesc;
+ };
+
+ class DataVal : public DataValBase
+ {
+ public:
+ DataVal()
+ : DataValBase(ShaderInputType::UniformData)
+ {}
+ };
+
+ class SamplerVal : public Val
+ {
+ public:
+ SamplerVal()
+ : Val(ShaderInputType::Sampler)
+ {}
+
+ InputSamplerDesc samplerDesc;
+ };
+
+ class CombinedTextureSamplerVal : public Val
+ {
+ public:
+ CombinedTextureSamplerVal()
+ : Val(ShaderInputType::CombinedTextureSampler)
+ {}
+
+ Slang::RefPtr<TextureVal> textureVal;
+ Slang::RefPtr<SamplerVal> samplerVal;
+ };
+
+ struct Field
+ {
+ Slang::String name;
+ ValPtr val;
+ };
+ typedef Field Entry;
+
+ class ParentVal : public Val
+ {
+ public:
+ ParentVal(ShaderInputType kind)
+ : Val(kind)
+ {}
+
+ virtual void addField(Field const& field) = 0;
+ };
+
+ class AggVal : public ParentVal
+ {
+ public:
+ AggVal(ShaderInputType kind = ShaderInputType::Aggregate)
+ : ParentVal(kind)
+ {}
+
+ Slang::List<Field> fields;
+
+ virtual void addField(Field const& field) override;
+ };
+
+ class ObjectVal : public Val
+ {
+ public:
+ ObjectVal()
+ : Val(ShaderInputType::Object)
+ {}
+
+ Slang::String typeName;
+ ValPtr contentVal;
+ };
+
+ class ArrayVal : public ParentVal
+ {
+ public:
+ ArrayVal()
+ : ParentVal(ShaderInputType::Array)
+ {}
+
+ Slang::List<ValPtr> vals;
+
+ virtual void addField(Field const& field) override;
+ };
+
+ Slang::RefPtr<AggVal> rootVal;
Slang::List<Slang::String> globalSpecializationArgs;
Slang::List<Slang::String> entryPointSpecializationArgs;
int numRenderTargets = 1;
Slang::Index findEntryIndexByName(const Slang::String& name) const;
- void updateForTarget(SlangCompileTarget target);
-
void parse(Slang::RandomGenerator* rand, const char* source);
/// Writes a binding, if bindRoot is set, will try to honor the underlying type when outputting. If not will dump as uint32_t hex.
- static SlangResult writeBinding(const ShaderInputLayoutEntry& entry, slang::TypeLayoutReflection* typeLayout, const void* data, size_t sizeInBytes, Slang::WriterHelper writer);
+ static SlangResult writeBinding(slang::TypeLayoutReflection* typeLayout, const void* data, size_t sizeInBytes, Slang::WriterHelper writer);
};
void generateTextureDataRGB8(TextureData& output, const InputTextureDesc& desc);
diff --git a/tools/render-test/shader-renderer-util.cpp b/tools/render-test/shader-renderer-util.cpp
index ede744445..d6441d3ac 100644
--- a/tools/render-test/shader-renderer-util.cpp
+++ b/tools/render-test/shader-renderer-util.cpp
@@ -93,7 +93,6 @@ using Slang::Result;
/* static */ Result ShaderRendererUtil::createBufferResource(
const InputBufferDesc& inputDesc,
- bool isOutput,
size_t bufferSize,
const void* initData,
IDevice* device,
@@ -106,24 +105,12 @@ using Slang::Result;
srcDesc.format = inputDesc.format;
int bindFlags = 0;
- if (inputDesc.type == InputBufferType::ConstantBuffer)
- {
- bindFlags |= IResource::BindFlag::ConstantBuffer;
- srcDesc.cpuAccessFlags |= IResource::AccessFlag::Write;
- initialUsage = IResource::Usage::ConstantBuffer;
- }
- else
{
bindFlags |= IResource::BindFlag::UnorderedAccess | IResource::BindFlag::PixelShaderResource | IResource::BindFlag::NonPixelShaderResource;
srcDesc.elementSize = inputDesc.stride;
initialUsage = IResource::Usage::UnorderedAccess;
}
- if (isOutput)
- {
- srcDesc.cpuAccessFlags |= IResource::AccessFlag::Read;
- }
-
srcDesc.bindFlags = bindFlags;
ComPtr<IBufferResource> bufferResource =
diff --git a/tools/render-test/shader-renderer-util.h b/tools/render-test/shader-renderer-util.h
index 8771d21f6..b4028fd06 100644
--- a/tools/render-test/shader-renderer-util.h
+++ b/tools/render-test/shader-renderer-util.h
@@ -31,7 +31,6 @@ struct ShaderRendererUtil
/// Create the BufferResource using the renderer from the contents of inputDesc
static Slang::Result createBufferResource(
const InputBufferDesc& inputDesc,
- bool isOutput,
size_t bufferSize,
const void* initData,
IDevice* device,
diff --git a/tools/render-test/slang-support.cpp b/tools/render-test/slang-support.cpp
index 72732363e..aca354763 100644
--- a/tools/render-test/slang-support.cpp
+++ b/tools/render-test/slang-support.cpp
@@ -363,7 +363,6 @@ void ShaderCompilerUtil::Output::reset()
// Parse the layout
layout.parse(rand, sourceText.getBuffer());
- layout.updateForTarget(input.target);
// Setup SourceInfo
ShaderCompileRequest::SourceInfo sourceInfo;