diff options
Diffstat (limited to 'tools/render-test')
| -rw-r--r-- | tools/render-test/render-test-main.cpp | 488 | ||||
| -rw-r--r-- | tools/render-test/shader-input-layout.cpp | 1111 | ||||
| -rw-r--r-- | tools/render-test/shader-input-layout.h | 190 | ||||
| -rw-r--r-- | tools/render-test/shader-renderer-util.cpp | 13 | ||||
| -rw-r--r-- | tools/render-test/shader-renderer-util.h | 1 | ||||
| -rw-r--r-- | tools/render-test/slang-support.cpp | 1 |
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; |
