From 44c0a56974b664e50b2cb8cb6f10740b19c4e02f Mon Sep 17 00:00:00 2001 From: Yong He Date: Thu, 3 Dec 2020 08:23:05 -0800 Subject: Add shader object parameter binding to renderer_test. (#1622) * Add shader object parameter binding to renderer_test. * remove multiple-definitions.hlsl * Fix cuda implementation. Co-authored-by: Tim Foley --- tools/gfx/d3d11/render-d3d11.cpp | 327 +--- tools/gfx/d3d12/render-d3d12.cpp | 532 +------ tools/gfx/open-gl/render-gl.cpp | 124 -- tools/gfx/render.h | 24 + tools/gfx/vulkan/render-vk.cpp | 138 -- tools/render-test/cpu-compute-util.cpp | 11 +- tools/render-test/cuda/cuda-compute-util.cpp | 12 +- tools/render-test/options.cpp | 4 + tools/render-test/options.h | 6 + tools/render-test/render-test-main.cpp | 2113 +++++++++++++++++++++++++- tools/render-test/shader-input-layout.cpp | 17 + tools/render-test/shader-input-layout.h | 9 +- tools/render-test/shader-renderer-util.cpp | 2 +- tools/render-test/shader-renderer-util.h | 4 + tools/render-test/slang-support.cpp | 60 +- tools/render-test/slang-support.h | 30 +- 16 files changed, 2317 insertions(+), 1096 deletions(-) (limited to 'tools') diff --git a/tools/gfx/d3d11/render-d3d11.cpp b/tools/gfx/d3d11/render-d3d11.cpp index 30c982ab3..6a58501fd 100644 --- a/tools/gfx/d3d11/render-d3d11.cpp +++ b/tools/gfx/d3d11/render-d3d11.cpp @@ -1019,6 +1019,11 @@ Result D3D11Renderer::createBufferResource(Resource::Usage initialUsage, const B } } + if( bufferDesc.Usage == D3D11_USAGE_DYNAMIC ) + { + bufferDesc.CPUAccessFlags |= D3D11_CPU_ACCESS_WRITE; + } + D3D11_SUBRESOURCE_DATA subResourceData = { 0 }; subResourceData.pSysMem = initData; @@ -1394,7 +1399,7 @@ void* D3D11Renderer::map(BufferResource* bufferIn, MapFlavor flavor) mapType = D3D11_MAP_WRITE_DISCARD; break; case MapFlavor::HostWrite: - mapType = D3D11_MAP_WRITE; + mapType = D3D11_MAP_WRITE_NO_OVERWRITE; break; case MapFlavor::HostRead: mapType = D3D11_MAP_READ; @@ -2104,299 +2109,6 @@ Result D3D11Renderer::createDescriptorSet(DescriptorSetLayout* layout, Descripto } -#if 0 -BindingState* D3D11Renderer::createBindingState(const BindingState::Desc& bindingStateDesc) -{ - RefPtr bindingState(new BindingStateImpl(bindingStateDesc)); - - const auto& srcBindings = bindingStateDesc.m_bindings; - const int numBindings = int(srcBindings.Count()); - - auto& dstDetails = bindingState->m_bindingDetails; - dstDetails.SetSize(numBindings); - - for (int i = 0; i < numBindings; ++i) - { - auto& dstDetail = dstDetails[i]; - const auto& srcBinding = srcBindings[i]; - - assert(srcBinding.registerRange.isSingle()); - - switch (srcBinding.bindingType) - { - case BindingType::Buffer: - { - assert(srcBinding.resource && srcBinding.resource->isBuffer()); - - BufferResourceImpl* buffer = static_cast(srcBinding.resource.Ptr()); - const BufferResource::Desc& desc = buffer->getDesc(); - - const int elemSize = bufferDesc.elementSize <= 0 ? 1 : bufferDesc.elementSize; - - if (bufferDesc.bindFlags & Resource::BindFlag::UnorderedAccess) - { - D3D11_UNORDERED_ACCESS_VIEW_DESC viewDesc; - memset(&viewDesc, 0, sizeof(viewDesc)); - viewDesc.Buffer.FirstElement = 0; - viewDesc.Buffer.NumElements = (UINT)(bufferDesc.sizeInBytes / elemSize); - viewDesc.Buffer.Flags = 0; - viewDesc.ViewDimension = D3D11_UAV_DIMENSION_BUFFER; - viewDesc.Format = D3DUtil::getMapFormat(bufferDesc.format); - - if (bufferDesc.elementSize == 0 && bufferDesc.format == Format::Unknown) - { - viewDesc.Buffer.Flags |= D3D11_BUFFER_UAV_FLAG_RAW; - viewDesc.Format = DXGI_FORMAT_R32_TYPELESS; - } - - SLANG_RETURN_NULL_ON_FAIL(m_device->CreateUnorderedAccessView(buffer->m_buffer, &viewDesc, dstDetail.m_uav.writeRef())); - } - if (bufferDesc.bindFlags & (Resource::BindFlag::NonPixelShaderResource | Resource::BindFlag::PixelShaderResource)) - { - D3D11_SHADER_RESOURCE_VIEW_DESC viewDesc; - memset(&viewDesc, 0, sizeof(viewDesc)); - viewDesc.Buffer.FirstElement = 0; - viewDesc.Buffer.ElementWidth = elemSize; - viewDesc.Buffer.NumElements = (UINT)(bufferDesc.sizeInBytes / elemSize); - viewDesc.Buffer.ElementOffset = 0; - viewDesc.ViewDimension = D3D11_SRV_DIMENSION_BUFFER; - viewDesc.Format = DXGI_FORMAT_UNKNOWN; - - if (bufferDesc.elementSize == 0) - { - viewDesc.Format = DXGI_FORMAT_R32_FLOAT; - } - - SLANG_RETURN_NULL_ON_FAIL(m_device->CreateShaderResourceView(buffer->m_buffer, &viewDesc, dstDetail.m_srv.writeRef())); - } - break; - } - case BindingType::Texture: - case BindingType::CombinedTextureSampler: - { - assert(srcBinding.resource && srcBinding.resource->isTexture()); - - TextureResourceImpl* texture = static_cast(srcBinding.resource.Ptr()); - - const TextureResource::Desc& textureDesc = texture->getDesc(); - - D3D11_SHADER_RESOURCE_VIEW_DESC viewDesc; - viewDesc.Format = D3DUtil::getMapFormat(textureDesc.format); - - switch (texture->getType()) - { - case Resource::Type::Texture1D: - { - if (textureDesc.arraySize <= 0) - { - viewDesc.ViewDimension = D3D11_SRV_DIMENSION_TEXTURE1D; - viewDesc.Texture1D.MipLevels = textureDesc.numMipLevels; - viewDesc.Texture1D.MostDetailedMip = 0; - } - else - { - viewDesc.ViewDimension = D3D11_SRV_DIMENSION_TEXTURE1DARRAY; - viewDesc.Texture1DArray.ArraySize = textureDesc.arraySize; - viewDesc.Texture1DArray.FirstArraySlice = 0; - viewDesc.Texture1DArray.MipLevels = textureDesc.numMipLevels; - viewDesc.Texture1DArray.MostDetailedMip = 0; - } - break; - } - case Resource::Type::Texture2D: - { - if (textureDesc.arraySize <= 0) - { - viewDesc.ViewDimension = D3D11_SRV_DIMENSION_TEXTURE2D; - viewDesc.Texture2D.MipLevels = textureDesc.numMipLevels; - viewDesc.Texture2D.MostDetailedMip = 0; - } - else - { - viewDesc.ViewDimension = D3D11_SRV_DIMENSION_TEXTURE2DARRAY; - viewDesc.Texture2DArray.ArraySize = textureDesc.arraySize; - viewDesc.Texture2DArray.FirstArraySlice = 0; - viewDesc.Texture2DArray.MipLevels = textureDesc.numMipLevels; - viewDesc.Texture2DArray.MostDetailedMip = 0; - } - break; - } - case Resource::Type::TextureCube: - { - if (textureDesc.arraySize <= 0) - { - viewDesc.ViewDimension = D3D11_SRV_DIMENSION_TEXTURECUBE; - viewDesc.TextureCube.MipLevels = textureDesc.numMipLevels; - viewDesc.TextureCube.MostDetailedMip = 0; - } - else - { - viewDesc.ViewDimension = D3D11_SRV_DIMENSION_TEXTURECUBEARRAY; - viewDesc.TextureCubeArray.MipLevels = textureDesc.numMipLevels; - viewDesc.TextureCubeArray.MostDetailedMip = 0; - viewDesc.TextureCubeArray.First2DArrayFace = 0; - viewDesc.TextureCubeArray.NumCubes = textureDesc.arraySize; - } - break; - } - case Resource::Type::Texture3D: - { - viewDesc.ViewDimension = D3D11_SRV_DIMENSION_TEXTURE3D; - viewDesc.Texture3D.MipLevels = textureDesc.numMipLevels; // Old code fixed as one - viewDesc.Texture3D.MostDetailedMip = 0; - break; - } - default: - { - assert(!"Unhandled type"); - return nullptr; - } - } - - SLANG_RETURN_NULL_ON_FAIL(m_device->CreateShaderResourceView(texture->m_resource, &viewDesc, dstDetail.m_srv.writeRef())); - break; - } - case BindingType::Sampler: - { - const BindingState::SamplerDesc& samplerDesc = bindingStateDesc.m_samplerDescs[srcBinding.descIndex]; - - D3D11_SAMPLER_DESC desc = {}; - desc.AddressU = desc.AddressV = desc.AddressW = D3D11_TEXTURE_ADDRESS_WRAP; - - if (samplerDesc.isCompareSampler) - { - desc.ComparisonFunc = D3D11_COMPARISON_LESS_EQUAL; - desc.Filter = D3D11_FILTER_MIN_LINEAR_MAG_MIP_POINT; - desc.MinLOD = desc.MaxLOD = 0.0f; - } - else - { - desc.Filter = D3D11_FILTER_ANISOTROPIC; - desc.MaxAnisotropy = 8; - desc.MinLOD = 0.0f; - desc.MaxLOD = 100.0f; - } - SLANG_RETURN_NULL_ON_FAIL(m_device->CreateSamplerState(&desc, dstDetail.m_samplerState.writeRef())); - break; - } - default: - { - assert(!"Unhandled type"); - return nullptr; - } - } - } - - // Done - return bindingState.detach(); -} - -void D3D11Renderer::_applyBindingState(bool isCompute) -{ - auto context = m_immediateContext.get(); - - const auto& details = m_currentBindings->m_bindingDetails; - const auto& bindings = m_currentBindings->getDesc().m_bindings; - - const int numBindings = int(bindings.Count()); - - for (int i = 0; i < numBindings; ++i) - { - const auto& binding = bindings[i]; - const auto& detail = details[i]; - - const int bindingIndex = binding.registerRange.getSingleIndex(); - - switch (binding.bindingType) - { - case BindingType::Buffer: - { - assert(binding.resource && binding.resource->isBuffer()); - if (binding.resource->canBind(Resource::BindFlag::ConstantBuffer)) - { - ID3D11Buffer* buffer = static_cast(binding.resource.Ptr())->m_buffer; - if (isCompute) - context->CSSetConstantBuffers(bindingIndex, 1, &buffer); - else - { - context->VSSetConstantBuffers(bindingIndex, 1, &buffer); - context->PSSetConstantBuffers(bindingIndex, 1, &buffer); - } - } - else if (detail.m_uav) - { - if (isCompute) - context->CSSetUnorderedAccessViews(bindingIndex, 1, detail.m_uav.readRef(), nullptr); - else - context->OMSetRenderTargetsAndUnorderedAccessViews( - m_currentBindings->getDesc().m_numRenderTargets, - m_renderTargetViews.getBuffer()->readRef(), - m_depthStencilView, - bindingIndex, - 1, - detail.m_uav.readRef(), - nullptr); - } - else - { - if (isCompute) - context->CSSetShaderResources(bindingIndex, 1, detail.m_srv.readRef()); - else - { - context->PSSetShaderResources(bindingIndex, 1, detail.m_srv.readRef()); - context->VSSetShaderResources(bindingIndex, 1, detail.m_srv.readRef()); - } - } - break; - } - case BindingType::Texture: - { - if (detail.m_uav) - { - if (isCompute) - context->CSSetUnorderedAccessViews(bindingIndex, 1, detail.m_uav.readRef(), nullptr); - else - context->OMSetRenderTargetsAndUnorderedAccessViews(D3D11_KEEP_RENDER_TARGETS_AND_DEPTH_STENCIL, - nullptr, nullptr, bindingIndex, 1, detail.m_uav.readRef(), nullptr); - } - else - { - if (isCompute) - context->CSSetShaderResources(bindingIndex, 1, detail.m_srv.readRef()); - else - { - context->PSSetShaderResources(bindingIndex, 1, detail.m_srv.readRef()); - context->VSSetShaderResources(bindingIndex, 1, detail.m_srv.readRef()); - } - } - break; - } - case BindingType::Sampler: - { - if (isCompute) - context->CSSetSamplers(bindingIndex, 1, detail.m_samplerState.readRef()); - else - { - context->PSSetSamplers(bindingIndex, 1, detail.m_samplerState.readRef()); - context->VSSetSamplers(bindingIndex, 1, detail.m_samplerState.readRef()); - } - break; - } - default: - { - assert(!"Not implemented"); - return; - } - } - } -} - -void D3D11Renderer::setBindingState(BindingState* state) -{ - m_currentBindings = static_cast(state); -} -#endif - void D3D11Renderer::_flushGraphicsState() { auto pipelineType = int(PipelineType::Graphics); @@ -2453,22 +2165,37 @@ void D3D11Renderer::DescriptorSetImpl::setResource(UInt range, UInt index, Resou { auto viewImpl = (ResourceViewImpl*)view; auto& rangeInfo = m_layout->m_ranges[range]; + auto flatIndex = rangeInfo.arrayIndex + index; switch (rangeInfo.type) { case D3D11DescriptorSlotType::ShaderResourceView: { - assert(viewImpl->m_type == ResourceViewImpl::Type::SRV); - auto srvImpl = (ShaderResourceViewImpl*)viewImpl; - m_srvs[rangeInfo.arrayIndex + index] = srvImpl->m_srv; + if( viewImpl ) + { + assert(viewImpl->m_type == ResourceViewImpl::Type::SRV); + auto srvImpl = (ShaderResourceViewImpl*)viewImpl; + m_srvs[flatIndex] = srvImpl->m_srv; + } + else + { + m_srvs[flatIndex] = nullptr; + } } break; case D3D11DescriptorSlotType::UnorderedAccessView: { - assert(viewImpl->m_type == ResourceViewImpl::Type::UAV); - auto uavImpl = (UnorderedAccessViewImpl*)viewImpl; - m_uavs[rangeInfo.arrayIndex + index] = uavImpl->m_uav; + if( viewImpl ) + { + assert(viewImpl->m_type == ResourceViewImpl::Type::UAV); + auto uavImpl = (UnorderedAccessViewImpl*)viewImpl; + m_uavs[flatIndex] = uavImpl->m_uav; + } + else + { + m_uavs[flatIndex] = nullptr; + } } break; diff --git a/tools/gfx/d3d12/render-d3d12.cpp b/tools/gfx/d3d12/render-d3d12.cpp index 0f23d8dd4..886754f0e 100644 --- a/tools/gfx/d3d12/render-d3d12.cpp +++ b/tools/gfx/d3d12/render-d3d12.cpp @@ -1082,270 +1082,6 @@ Result D3D12Renderer::captureTextureToSurface(D3D12Resource& resource, Surface& } } -#if 0 -Result D3D12Renderer::calcComputePipelineState(ComPtr& signatureOut, ComPtr& pipelineStateOut) -{ - BindParameters bindParameters; - _calcBindParameters(bindParameters); - - ComPtr rootSignature; - ComPtr pipelineState; - - { - D3D12_ROOT_SIGNATURE_DESC rootSignatureDesc; - rootSignatureDesc.NumParameters = bindParameters.m_paramIndex; - rootSignatureDesc.pParameters = bindParameters.m_parameters; - rootSignatureDesc.NumStaticSamplers = 0; - rootSignatureDesc.pStaticSamplers = nullptr; - rootSignatureDesc.Flags = D3D12_ROOT_SIGNATURE_FLAG_NONE; - - ComPtr signature; - ComPtr error; - SLANG_RETURN_ON_FAIL(m_D3D12SerializeRootSignature(&rootSignatureDesc, D3D_ROOT_SIGNATURE_VERSION_1, signature.writeRef(), error.writeRef())); - SLANG_RETURN_ON_FAIL(m_device->CreateRootSignature(0, signature->GetBufferPointer(), signature->GetBufferSize(), IID_PPV_ARGS(rootSignature.writeRef()))); - } - - { - // Describe and create the compute pipeline state object - D3D12_COMPUTE_PIPELINE_STATE_DESC computeDesc = {}; - computeDesc.pRootSignature = rootSignature; - computeDesc.CS = { m_boundShaderProgram->m_computeShader.getBuffer(), m_boundShaderProgram->m_computeShader.Count() }; - SLANG_RETURN_ON_FAIL(m_device->CreateComputePipelineState(&computeDesc, IID_PPV_ARGS(pipelineState.writeRef()))); - } - - signatureOut.swap(rootSignature); - pipelineStateOut.swap(pipelineState); - - return SLANG_OK; -} -#endif - -#if 0 -D3D12Renderer::RenderState* D3D12Renderer::findRenderState(PipelineType pipelineType) -{ - switch (pipelineType) - { - case PipelineType::Compute: - { - // Check if current state is a match - if (m_currentRenderState) - { - if (m_currentRenderState->m_bindingState == m_boundBindingState && - m_currentRenderState->m_shaderProgram == m_boundShaderProgram) - { - return m_currentRenderState; - } - } - - const int num = int(m_renderStates.Count()); - for (int i = 0; i < num; i++) - { - RenderState* renderState = m_renderStates[i]; - if (renderState->m_bindingState == m_boundBindingState && - renderState->m_shaderProgram == m_boundShaderProgram) - { - return renderState; - } - } - break; - } - case PipelineType::Graphics: - { - if (m_currentRenderState) - { - if (m_currentRenderState->m_bindingState == m_boundBindingState && - m_currentRenderState->m_inputLayout == m_boundInputLayout && - m_currentRenderState->m_shaderProgram == m_boundShaderProgram && - m_currentRenderState->m_primitiveTopologyType == m_primitiveTopologyType) - { - return m_currentRenderState; - } - } - // See if matches one in the list - { - const int num = int(m_renderStates.Count()); - for (int i = 0; i < num; i++) - { - RenderState* renderState = m_renderStates[i]; - if (renderState->m_bindingState == m_boundBindingState && - renderState->m_inputLayout == m_boundInputLayout && - renderState->m_shaderProgram == m_boundShaderProgram && - renderState->m_primitiveTopologyType == m_primitiveTopologyType) - { - // Okay we have a match - return renderState; - } - } - } - break; - } - default: break; - } - return nullptr; -} - -D3D12Renderer::RenderState* D3D12Renderer::calcRenderState() -{ - if (!m_boundShaderProgram) - { - return nullptr; - } - m_currentRenderState = findRenderState(m_boundShaderProgram->m_pipelineType); - if (m_currentRenderState) - { - return m_currentRenderState; - } - - ComPtr rootSignature; - ComPtr pipelineState; - - switch (m_boundShaderProgram->m_pipelineType) - { - case PipelineType::Compute: - { - if (SLANG_FAILED(calcComputePipelineState(rootSignature, pipelineState))) - { - return nullptr; - } - break; - } - case PipelineType::Graphics: - { - if (SLANG_FAILED(calcGraphicsPipelineState(rootSignature, pipelineState))) - { - return nullptr; - } - break; - } - default: return nullptr; - } - - RenderState* renderState = new RenderState; - - renderState->m_primitiveTopologyType = m_primitiveTopologyType; - renderState->m_bindingState = m_boundBindingState; - renderState->m_inputLayout = m_boundInputLayout; - renderState->m_shaderProgram = m_boundShaderProgram; - - renderState->m_rootSignature.swap(rootSignature); - renderState->m_pipelineState.swap(pipelineState); - - m_renderStates.Add(renderState); - - m_currentRenderState = renderState; - - return renderState; -} - -Result D3D12Renderer::_calcBindParameters(BindParameters& params) -{ - int numConstantBuffers = 0; - { - if (m_boundBindingState) - { - const int numBoundConstantBuffers = numConstantBuffers; - - const BindingState::Desc& bindingStateDesc = m_boundBindingState->getDesc(); - - const auto& bindings = bindingStateDesc.m_bindings; - const auto& details = m_boundBindingState->m_bindingDetails; - - const int numBindings = int(bindings.Count()); - - for (int i = 0; i < numBindings; i++) - { - const auto& binding = bindings[i]; - const auto& detail = details[i]; - - const int bindingIndex = binding.registerRange.getSingleIndex(); - - if (binding.bindingType == BindingType::Buffer) - { - assert(binding.resource && binding.resource->isBuffer()); - if (binding.resource->canBind(Resource::BindFlag::ConstantBuffer)) - { - // Make sure it's not overlapping the ones we just statically defined - //assert(binding.m_binding < numBoundConstantBuffers); - - D3D12_ROOT_PARAMETER& param = params.nextParameter(); - param.ParameterType = D3D12_ROOT_PARAMETER_TYPE_CBV; - param.ShaderVisibility = D3D12_SHADER_VISIBILITY_ALL; - - D3D12_ROOT_DESCRIPTOR& descriptor = param.Descriptor; - descriptor.ShaderRegister = bindingIndex; - descriptor.RegisterSpace = 0; - - numConstantBuffers++; - } - } - - if (detail.m_srvIndex >= 0) - { - D3D12_DESCRIPTOR_RANGE& range = params.nextRange(); - - range.RangeType = D3D12_DESCRIPTOR_RANGE_TYPE_SRV; - range.NumDescriptors = 1; - range.BaseShaderRegister = bindingIndex; - range.RegisterSpace = 0; - range.OffsetInDescriptorsFromTableStart = D3D12_DESCRIPTOR_RANGE_OFFSET_APPEND; - - D3D12_ROOT_PARAMETER& param = params.nextParameter(); - - param.ParameterType = D3D12_ROOT_PARAMETER_TYPE_DESCRIPTOR_TABLE; - param.ShaderVisibility = D3D12_SHADER_VISIBILITY_ALL; - - D3D12_ROOT_DESCRIPTOR_TABLE& table = param.DescriptorTable; - table.NumDescriptorRanges = 1; - table.pDescriptorRanges = ⦥ - } - - if (detail.m_uavIndex >= 0) - { - D3D12_DESCRIPTOR_RANGE& range = params.nextRange(); - - range.RangeType = D3D12_DESCRIPTOR_RANGE_TYPE_UAV; - range.NumDescriptors = 1; - range.BaseShaderRegister = bindingIndex; - range.RegisterSpace = 0; - range.OffsetInDescriptorsFromTableStart = D3D12_DESCRIPTOR_RANGE_OFFSET_APPEND; - - D3D12_ROOT_PARAMETER& param = params.nextParameter(); - - param.ParameterType = D3D12_ROOT_PARAMETER_TYPE_DESCRIPTOR_TABLE; - param.ShaderVisibility = D3D12_SHADER_VISIBILITY_ALL; - - D3D12_ROOT_DESCRIPTOR_TABLE& table = param.DescriptorTable; - table.NumDescriptorRanges = 1; - table.pDescriptorRanges = ⦥ - } - } - } - } - - // All the samplers are in one continuous section of the sampler heap - if (m_boundBindingState && m_boundBindingState->m_samplerHeap.getUsedSize() > 0) - { - D3D12_DESCRIPTOR_RANGE& range = params.nextRange(); - - range.RangeType = D3D12_DESCRIPTOR_RANGE_TYPE_SAMPLER; - range.NumDescriptors = m_boundBindingState->m_samplerHeap.getUsedSize(); - range.BaseShaderRegister = 0; - range.RegisterSpace = 0; - range.OffsetInDescriptorsFromTableStart = D3D12_DESCRIPTOR_RANGE_OFFSET_APPEND; - - D3D12_ROOT_PARAMETER& param = params.nextParameter(); - - param.ParameterType = D3D12_ROOT_PARAMETER_TYPE_DESCRIPTOR_TABLE; - param.ShaderVisibility = D3D12_SHADER_VISIBILITY_ALL; - - D3D12_ROOT_DESCRIPTOR_TABLE& table = param.DescriptorTable; - table.NumDescriptorRanges = 1; - table.pDescriptorRanges = ⦥ - } - return SLANG_OK; -} -#endif - Result D3D12Renderer::_bindRenderState(PipelineStateImpl* pipelineStateImpl, ID3D12GraphicsCommandList* commandList, Submitter* submitter) { // TODO: we should only set some of this state as needed... @@ -2952,176 +2688,6 @@ void D3D12Renderer::dispatchCompute(int x, int y, int z) commandList->Dispatch(x, y, z); } -#if 0 -BindingState* D3D12Renderer::createBindingState(const BindingState::Desc& bindingStateDesc) -{ - RefPtr bindingState(new BindingStateImpl(bindingStateDesc)); - - SLANG_RETURN_NULL_ON_FAIL(bindingState->init(m_device)); - - const auto& srcBindings = bindingStateDesc.m_bindings; - const int numBindings = int(srcBindings.Count()); - - auto& dstDetails = bindingState->m_bindingDetails; - dstDetails.SetSize(numBindings); - - for (int i = 0; i < numBindings; ++i) - { - const auto& srcEntry = srcBindings[i]; - auto& dstDetail = dstDetails[i]; - - const int bindingIndex = srcEntry.registerRange.getSingleIndex(); - - switch (srcEntry.bindingType) - { - case BindingType::Buffer: - { - assert(srcEntry.resource && srcEntry.resource->isBuffer()); - BufferResourceImpl* bufferResource = static_cast(srcEntry.resource.Ptr()); - const BufferResource::Desc& desc = bufferResource->getDesc(); - - const size_t bufferSize = bufferDesc.sizeInBytes; - const int elemSize = bufferDesc.elementSize <= 0 ? sizeof(uint32_t) : bufferDesc.elementSize; - - const bool createSrv = false; - - // NOTE! In this arrangement the buffer can either be a ConstantBuffer or a 'StorageBuffer'. - // If it's a storage buffer then it has a 'uav'. - // In neither circumstance is there an associated srv - // This departs a little from dx11 code - in that it will create srv and uav for a storage buffer. - if (bufferDesc.bindFlags & Resource::BindFlag::UnorderedAccess) - { - dstDetail.m_uavIndex = bindingState->m_viewHeap.allocate(); - if (dstDetail.m_uavIndex < 0) - { - return nullptr; - } - - D3D12_UNORDERED_ACCESS_VIEW_DESC uavDesc = {}; - - uavDesc.ViewDimension = D3D12_UAV_DIMENSION_BUFFER; - uavDesc.Format = D3DUtil::getMapFormat(bufferDesc.format); - - uavDesc.Buffer.StructureByteStride = elemSize; - - uavDesc.Buffer.FirstElement = 0; - uavDesc.Buffer.NumElements = (UINT)(bufferSize / elemSize); - uavDesc.Buffer.Flags = D3D12_BUFFER_UAV_FLAG_NONE; - - if (bufferDesc.elementSize == 0 && bufferDesc.format == Format::Unknown) - { - uavDesc.Buffer.Flags |= D3D12_BUFFER_UAV_FLAG_RAW; - uavDesc.Format = DXGI_FORMAT_R32_TYPELESS; - - uavDesc.Buffer.StructureByteStride = 0; - } - else if( bufferDesc.format != Format::Unknown ) - { - uavDesc.Buffer.StructureByteStride = 0; - } - - m_device->CreateUnorderedAccessView(bufferResource->m_resource, nullptr, &uavDesc, bindingState->m_viewHeap.getCpuHandle(dstDetail.m_uavIndex)); - } - if (createSrv && (bufferDesc.bindFlags & (Resource::BindFlag::NonPixelShaderResource | Resource::BindFlag::PixelShaderResource))) - { - dstDetail.m_srvIndex = bindingState->m_viewHeap.allocate(); - if (dstDetail.m_srvIndex < 0) - { - return nullptr; - } - - D3D12_SHADER_RESOURCE_VIEW_DESC srvDesc; - - srvDesc.ViewDimension = D3D12_SRV_DIMENSION_BUFFER; - srvDesc.Format = DXGI_FORMAT_UNKNOWN; - srvDesc.Shader4ComponentMapping = D3D12_DEFAULT_SHADER_4_COMPONENT_MAPPING; - - srvDesc.Buffer.FirstElement = 0; - srvDesc.Buffer.NumElements = (UINT)(bufferSize / elemSize); - srvDesc.Buffer.StructureByteStride = elemSize; - srvDesc.Buffer.Flags = D3D12_BUFFER_SRV_FLAG_NONE; - - if (bufferDesc.elementSize == 0) - { - srvDesc.Format = DXGI_FORMAT_R32_FLOAT; - } - - m_device->CreateShaderResourceView(bufferResource->m_resource, &srvDesc, bindingState->m_viewHeap.getCpuHandle(dstDetail.m_srvIndex)); - } - - break; - } - case BindingType::Texture: - { - assert(srcEntry.resource && srcEntry.resource->isTexture()); - - TextureResourceImpl* textureResource = static_cast(srcEntry.resource.Ptr()); - - dstDetail.m_srvIndex = bindingState->m_viewHeap.allocate(); - if (dstDetail.m_srvIndex < 0) - { - return nullptr; - } - - { - const D3D12_RESOURCE_DESC resourceDesc = textureResource->m_resource.getResource()->GetDesc(); - const DXGI_FORMAT pixelFormat = resourceDesc.Format; - - D3D12_SHADER_RESOURCE_VIEW_DESC srvDesc; - _initSrvDesc(textureResource->getType(), textureResource->getDesc(), resourceDesc, pixelFormat, srvDesc); - - // Create descriptor - m_device->CreateShaderResourceView(textureResource->m_resource, &srvDesc, bindingState->m_viewHeap.getCpuHandle(dstDetail.m_srvIndex)); - } - - break; - } - case BindingType::Sampler: - { - const BindingState::SamplerDesc& samplerDesc = bindingStateDesc.m_samplerDescs[srcEntry.descIndex]; - - const int samplerIndex = bindingIndex; - dstDetail.m_samplerIndex = samplerIndex; - bindingState->m_samplerHeap.placeAt(samplerIndex); - - D3D12_SAMPLER_DESC desc = {}; - desc.AddressU = desc.AddressV = desc.AddressW = D3D12_TEXTURE_ADDRESS_MODE_WRAP; - desc.ComparisonFunc = D3D12_COMPARISON_FUNC_ALWAYS; - - if (samplerDesc.isCompareSampler) - { - desc.ComparisonFunc = D3D12_COMPARISON_FUNC_LESS_EQUAL; - desc.Filter = D3D12_FILTER_MIN_LINEAR_MAG_MIP_POINT; - } - else - { - desc.Filter = D3D12_FILTER_ANISOTROPIC; - desc.MaxAnisotropy = 8; - desc.MinLOD = 0.0f; - desc.MaxLOD = 100.0f; - } - - m_device->CreateSampler(&desc, bindingState->m_samplerHeap.getCpuHandle(samplerIndex)); - - break; - } - case BindingType::CombinedTextureSampler: - { - assert(!"Not implemented"); - return nullptr; - } - } - } - - return bindingState.detach(); -} - -void D3D12Renderer::setBindingState(BindingState* state) -{ - m_boundBindingState = static_cast(state); -} -#endif - void D3D12Renderer::DescriptorSetImpl::setConstantBuffer(UInt range, UInt index, BufferResource* buffer) { auto dxDevice = m_renderer->m_device; @@ -3525,10 +3091,10 @@ Result D3D12Renderer::createDescriptorSetLayout(const DescriptorSetLayout::Desc& // Finally, we will go through and fill in ready-to-go D3D // register range information. { - UInt cbvCounter = 0; - UInt srvCounter = 0; - UInt uavCounter = 0; - UInt samplerCounter = 0; + UInt cbvRegisterCounter = 0; + UInt srvRegisterCounter = 0; + UInt uavRegisterCounter = 0; + UInt samplerRegisterCounter = 0; Int resourceRangeCounter = 0; Int samplerRangeCounter = 0; @@ -3548,7 +3114,6 @@ Result D3D12Renderer::createDescriptorSetLayout(const DescriptorSetLayout::Desc& Int dxRangeIndex = -1; Int dxPairedSamplerRangeIndex = -1; - switch(rangeDesc.type) { default: @@ -3586,7 +3151,12 @@ Result D3D12Renderer::createDescriptorSetLayout(const DescriptorSetLayout::Desc& // so that this range doesn't turn into a descriptor // range in one of the D3D12 descriptor tables. // - UInt bindingIndex = cbvCounter; cbvCounter += bindingCount; + Int dxRegister = rangeDesc.binding; + if( dxRegister < 0 ) + { + dxRegister = cbvRegisterCounter; + } + cbvRegisterCounter = dxRegister + bindingCount; auto rootConstantRangeIndex = descriptorSetLayoutImpl->m_ranges[rr].arrayIndex; auto rootParamIndex = descriptorSetLayoutImpl->m_rootConstantRanges[rootConstantRangeIndex].rootParamIndex; @@ -3598,7 +3168,7 @@ Result D3D12Renderer::createDescriptorSetLayout(const DescriptorSetLayout::Desc& // auto& dxRootParam = descriptorSetLayoutImpl->m_dxRootParameters[rootParamIndex]; dxRootParam.Constants.RegisterSpace = UINT(bindingSpace); - dxRootParam.Constants.ShaderRegister = UINT(bindingIndex); + dxRootParam.Constants.ShaderRegister = UINT(dxRegister); continue; } break; @@ -3607,6 +3177,8 @@ Result D3D12Renderer::createDescriptorSetLayout(const DescriptorSetLayout::Desc& D3D12_DESCRIPTOR_RANGE& dxRange = descriptorSetLayoutImpl->m_dxRanges[dxRangeIndex]; memset(&dxRange, 0, sizeof(dxRange)); + Int dxRegister = rangeDesc.binding; + switch(rangeDesc.type) { default: @@ -3615,11 +3187,15 @@ Result D3D12Renderer::createDescriptorSetLayout(const DescriptorSetLayout::Desc& case DescriptorSlotType::Sampler: { - UInt bindingIndex = samplerCounter; samplerCounter += bindingCount; + if( dxRegister < 0 ) + { + dxRegister = samplerRegisterCounter; + } + samplerRegisterCounter = dxRegister + bindingCount; dxRange.RangeType = D3D12_DESCRIPTOR_RANGE_TYPE_SAMPLER; dxRange.NumDescriptors = UINT(bindingCount); - dxRange.BaseShaderRegister = UINT(bindingIndex); + dxRange.BaseShaderRegister = UINT(dxRegister); dxRange.RegisterSpace = UINT(bindingSpace); dxRange.OffsetInDescriptorsFromTableStart = D3D12_DESCRIPTOR_RANGE_OFFSET_APPEND; } @@ -3628,11 +3204,15 @@ Result D3D12Renderer::createDescriptorSetLayout(const DescriptorSetLayout::Desc& case DescriptorSlotType::SampledImage: case DescriptorSlotType::UniformTexelBuffer: { - UInt bindingIndex = srvCounter; srvCounter += bindingCount; + if( dxRegister < 0 ) + { + dxRegister = srvRegisterCounter; + } + srvRegisterCounter = dxRegister + bindingCount; dxRange.RangeType = D3D12_DESCRIPTOR_RANGE_TYPE_SRV; dxRange.NumDescriptors = UINT(bindingCount); - dxRange.BaseShaderRegister = UINT(bindingIndex); + dxRange.BaseShaderRegister = UINT(dxRegister); dxRange.RegisterSpace = UINT(bindingSpace); dxRange.OffsetInDescriptorsFromTableStart = D3D12_DESCRIPTOR_RANGE_OFFSET_APPEND; } @@ -3642,15 +3222,31 @@ Result D3D12Renderer::createDescriptorSetLayout(const DescriptorSetLayout::Desc& { // The combined texture/sampler case basically just // does the work of both the SRV and sampler cases above. + // + // TODO(tfoley): The current API for passing down an + // explicit register/binding can't handle the requirement + // that we specify *two* registers/bindings for the + // combined image/sampler case. + // + // Realistically, the `Renderer` implementation for + // targes that don't support combined texture/sampler + // bindings should just error out when a client attempts + // to create a descriptor set that uses them (rather than + // the current behavior which adds a lot of complexity + // in the name of trying to make them work). { // Here's the SRV logic: - - UInt bindingIndex = srvCounter; srvCounter += bindingCount; + Int srvRegister = dxRegister; + if( srvRegister < 0 ) + { + srvRegister = srvRegisterCounter; + } + srvRegisterCounter = srvRegister + bindingCount; dxRange.RangeType = D3D12_DESCRIPTOR_RANGE_TYPE_SRV; dxRange.NumDescriptors = UINT(bindingCount); - dxRange.BaseShaderRegister = UINT(bindingIndex); + dxRange.BaseShaderRegister = UINT(srvRegister); dxRange.RegisterSpace = UINT(bindingSpace); dxRange.OffsetInDescriptorsFromTableStart = D3D12_DESCRIPTOR_RANGE_OFFSET_APPEND; } @@ -3660,11 +3256,16 @@ Result D3D12Renderer::createDescriptorSetLayout(const DescriptorSetLayout::Desc& D3D12_DESCRIPTOR_RANGE& dxPairedSamplerRange = descriptorSetLayoutImpl->m_dxRanges[dxPairedSamplerRangeIndex]; memset(&dxPairedSamplerRange, 0, sizeof(dxPairedSamplerRange)); - UInt pairedSamplerBindingIndex = srvCounter; srvCounter += bindingCount; + Int samplerRegister = dxRegister; + if( samplerRegister < 0 ) + { + samplerRegister = samplerRegisterCounter; + } + samplerRegisterCounter = samplerRegister + bindingCount; dxPairedSamplerRange.RangeType = D3D12_DESCRIPTOR_RANGE_TYPE_SAMPLER; dxPairedSamplerRange.NumDescriptors = UINT(bindingCount); - dxPairedSamplerRange.BaseShaderRegister = UINT(pairedSamplerBindingIndex); + dxPairedSamplerRange.BaseShaderRegister = UINT(samplerRegister); dxPairedSamplerRange.RegisterSpace = UINT(bindingSpace); dxPairedSamplerRange.OffsetInDescriptorsFromTableStart = D3D12_DESCRIPTOR_RANGE_OFFSET_APPEND; } @@ -3679,11 +3280,15 @@ Result D3D12Renderer::createDescriptorSetLayout(const DescriptorSetLayout::Desc& case DescriptorSlotType::StorageBuffer: case DescriptorSlotType::DynamicStorageBuffer: { - UInt bindingIndex = uavCounter; uavCounter += bindingCount; + if( dxRegister < 0 ) + { + dxRegister = uavRegisterCounter; + } + uavRegisterCounter = dxRegister + bindingCount; dxRange.RangeType = D3D12_DESCRIPTOR_RANGE_TYPE_UAV; dxRange.NumDescriptors = UINT(bindingCount); - dxRange.BaseShaderRegister = UINT(bindingIndex); + dxRange.BaseShaderRegister = UINT(dxRegister); dxRange.RegisterSpace = UINT(bindingSpace); dxRange.OffsetInDescriptorsFromTableStart = D3D12_DESCRIPTOR_RANGE_OFFSET_APPEND; } @@ -3692,15 +3297,23 @@ Result D3D12Renderer::createDescriptorSetLayout(const DescriptorSetLayout::Desc& case DescriptorSlotType::UniformBuffer: case DescriptorSlotType::DynamicUniformBuffer: { - UInt bindingIndex = cbvCounter; cbvCounter += bindingCount; + if( dxRegister < 0 ) + { + dxRegister = cbvRegisterCounter; + } + cbvRegisterCounter = dxRegister + bindingCount; dxRange.RangeType = D3D12_DESCRIPTOR_RANGE_TYPE_CBV; dxRange.NumDescriptors = UINT(bindingCount); - dxRange.BaseShaderRegister = UINT(bindingIndex); + dxRange.BaseShaderRegister = UINT(dxRegister); dxRange.RegisterSpace = UINT(bindingSpace); dxRange.OffsetInDescriptorsFromTableStart = D3D12_DESCRIPTOR_RANGE_OFFSET_APPEND; } break; + + + + } } } @@ -3722,6 +3335,8 @@ Result D3D12Renderer::createPipelineLayout(const PipelineLayout::Desc& desc, Pip auto descriptorSetCount = desc.descriptorSetCount; + Int spaceCounter = 0; + // We are going to make two passes over the descriptor set layouts // that are being used to build the pipeline layout. In the first // pass we will collect all the descriptor ranges that have been @@ -3740,7 +3355,12 @@ Result D3D12Renderer::createPipelineLayout(const PipelineLayout::Desc& desc, Pip // that comes from multiple spaces (e.g., if it contains an unbounded // array). // - UInt bindingSpace = dd; + Int space = descriptorSetInfo.space; + if( space < 0 ) + { + space = spaceCounter; + } + spaceCounter = space+1; // Copy descriptor range information from the set layout into our // temporary copy (this is required because the same set layout @@ -3754,7 +3374,7 @@ Result D3D12Renderer::createPipelineLayout(const PipelineLayout::Desc& desc, Pip { auto& range = ranges[rangeCount++]; range = setDescriptorRange; - range.RegisterSpace = UINT(bindingSpace); + range.RegisterSpace = UINT(space); // HACK: in order to deal with SM5.0 shaders, `u` registers // in `space0` need to start with a number *after* the number diff --git a/tools/gfx/open-gl/render-gl.cpp b/tools/gfx/open-gl/render-gl.cpp index caf8794c0..bf873212c 100644 --- a/tools/gfx/open-gl/render-gl.cpp +++ b/tools/gfx/open-gl/render-gl.cpp @@ -1151,130 +1151,6 @@ void GLRenderer::dispatchCompute(int x, int y, int z) glDispatchCompute(x, y, z); } -#if 0 -BindingState* GLRenderer::createBindingState(const BindingState::Desc& bindingStateDesc) -{ - RefPtr bindingState(new BindingStateImpl(bindingStateDesc, this)); - - const auto& srcBindings = bindingStateDesc.m_bindings; - const int numBindings = int(srcBindings.Count()); - - auto& dstDetails = bindingState->m_bindingDetails; - dstDetails.SetSize(numBindings); - - for (int i = 0; i < numBindings; ++i) - { - auto& dstDetail = dstDetails[i]; - const auto& srcBinding = srcBindings[i]; - - - switch (srcBinding.bindingType) - { - case BindingType::Texture: - case BindingType::Buffer: - { - break; - } - case BindingType::CombinedTextureSampler: - { - assert(srcBinding.resource && srcBinding.resource->isTexture()); - TextureResourceImpl* texture = static_cast(srcBinding.resource.Ptr()); - const BindingState::SamplerDesc& samplerDesc = bindingStateDesc.m_samplerDescs[srcBinding.descIndex]; - - if (samplerDesc.isCompareSampler) - { - auto target = texture->m_target; - - glTexParameteri(target, GL_TEXTURE_MIN_FILTER, GL_LINEAR); - glTexParameteri(target, GL_TEXTURE_MAG_FILTER, GL_LINEAR); - glTexParameteri(target, GL_TEXTURE_COMPARE_MODE, GL_COMPARE_REF_TO_TEXTURE); - glTexParameteri(target, GL_TEXTURE_COMPARE_FUNC, GL_LEQUAL); - } - break; - } - case BindingType::Sampler: - { - const BindingState::SamplerDesc& samplerDesc = bindingStateDesc.m_samplerDescs[srcBinding.descIndex]; - - GLuint handle; - - glCreateSamplers(1, &handle); - glSamplerParameteri(handle, GL_TEXTURE_WRAP_S, GL_REPEAT); - glSamplerParameteri(handle, GL_TEXTURE_WRAP_T, GL_REPEAT); - glSamplerParameteri(handle, GL_TEXTURE_WRAP_R, GL_REPEAT); - - if (samplerDesc.isCompareSampler) - { - glSamplerParameteri(handle, GL_TEXTURE_MIN_FILTER, GL_LINEAR); - glSamplerParameteri(handle, GL_TEXTURE_MAG_FILTER, GL_LINEAR); - glSamplerParameteri(handle, GL_TEXTURE_COMPARE_MODE, GL_COMPARE_REF_TO_TEXTURE); - glSamplerParameteri(handle, GL_TEXTURE_COMPARE_FUNC, GL_LEQUAL); - } - else - { - glSamplerParameteri(handle, GL_TEXTURE_MIN_FILTER, GL_LINEAR_MIPMAP_LINEAR); - glSamplerParameteri(handle, GL_TEXTURE_MAG_FILTER, GL_LINEAR); - glSamplerParameteri(handle, GL_TEXTURE_MAX_ANISOTROPY_EXT, 8); - } - - dstDetail.m_samplerHandle = handle; - break; - } - } - } - - return bindingState.detach(); -} - -void GLRenderer::setBindingState(BindingState* stateIn) -{ - BindingStateImpl* state = static_cast(stateIn); - - const auto& bindingDesc = state->getDesc(); - - const auto& details = state->m_bindingDetails; - const auto& bindings = bindingDesc.m_bindings; - const int numBindings = int(bindings.Count()); - - for (int i = 0; i < numBindings; ++i) - { - const auto& binding = bindings[i]; - const auto& detail = details[i]; - - switch (binding.bindingType) - { - case BindingType::Buffer: - { - const int bindingIndex = binding.registerRange.getSingleIndex(); - - BufferResourceImpl* buffer = static_cast(binding.resource.Ptr()); - glBindBufferBase(buffer->m_target, bindingIndex, buffer->m_handle); - break; - } - case BindingType::Sampler: - { - for (int index = binding.registerRange.index; index < binding.registerRange.index + binding.registerRange.size; ++index) - { - glBindSampler(index, detail.m_samplerHandle); - } - break; - } - case BindingType::Texture: - case BindingType::CombinedTextureSampler: - { - BufferResourceImpl* buffer = static_cast(binding.resource.Ptr()); - - const int bindingIndex = binding.registerRange.getSingleIndex(); - - glActiveTexture(GL_TEXTURE0 + bindingIndex); - glBindTexture(buffer->m_target, buffer->m_handle); - break; - } - } - } -} -#endif - void GLRenderer::DescriptorSetImpl::setConstantBuffer(UInt range, UInt index, BufferResource* buffer) { auto resourceImpl = (BufferResourceImpl*) buffer; diff --git a/tools/gfx/render.h b/tools/gfx/render.h index 55beb5774..5ad338594 100644 --- a/tools/gfx/render.h +++ b/tools/gfx/render.h @@ -520,6 +520,8 @@ enum class DescriptorSlotType DynamicStorageBuffer, InputAttachment, RootConstant, + InlineUniformBlock, + RayTracingAccelerationStructure, }; class DescriptorSetLayout : public Slang::RefObject @@ -530,6 +532,17 @@ public: DescriptorSlotType type = DescriptorSlotType::Unknown; UInt count = 1; + /// The underlying API-specific binding/register to use for this slot range. + /// + /// A value of `-1` indicates that the implementation should + /// automatically compute the binding/register to use + /// based on the preceeding slot range(s). + /// + /// Some implementations do not have a concept of bindings/regsiters + /// for slot ranges, and will ignore this field. + /// + Int binding = -1; + SlotRangeDesc() {} @@ -555,6 +568,17 @@ public: { DescriptorSetLayout* layout = nullptr; + /// The underlying API-specific space/set number to use for this set. + /// + /// A value of `-1` indicates that the implementation should + /// automatically compute the space/set to use basd on + /// the preceeding set(s) + /// + /// Some implementations do not have a concept of space/set numbers + /// for descriptor sets, and will ignore this field. + /// + Int space = -1; + DescriptorSetDesc() {} diff --git a/tools/gfx/vulkan/render-vk.cpp b/tools/gfx/vulkan/render-vk.cpp index 397d657a4..6f2411f24 100644 --- a/tools/gfx/vulkan/render-vk.cpp +++ b/tools/gfx/vulkan/render-vk.cpp @@ -2270,144 +2270,6 @@ static VkImageViewType _calcImageViewType(TextureResource::Type type, const Text return VK_IMAGE_VIEW_TYPE_MAX_ENUM; } -#if 0 -BindingState* VKRenderer::createBindingState(const BindingState::Desc& bindingStateDesc) -{ - RefPtr bindingState(new BindingStateImpl(bindingStateDesc, &m_api)); - - const auto& srcBindings = bindingStateDesc.m_bindings; - const int numBindings = int(srcBindings.Count()); - - auto& dstDetails = bindingState->m_bindingDetails; - dstDetails.SetSize(numBindings); - - for (int i = 0; i < numBindings; ++i) - { - auto& dstDetail = dstDetails[i]; - const auto& srcBinding = srcBindings[i]; - - switch (srcBinding.bindingType) - { - case BindingType::Buffer: - { - if (!srcBinding.resource || !srcBinding.resource->isBuffer()) - { - assert(!"Needs to have a buffer resource set"); - return nullptr; - } - - BufferResourceImpl* bufferResource = static_cast(srcBinding.resource.Ptr()); - const BufferResource::Desc& bufferResourceDesc = bufferResource->getDesc(); - - if (bufferResourceDesc.bindFlags & Resource::BindFlag::UnorderedAccess) - { - // VkBufferView uav - - VkBufferViewCreateInfo info = { VK_STRUCTURE_TYPE_BUFFER_VIEW_CREATE_INFO }; - - info.format = VK_FORMAT_R32_SFLOAT; - // TODO: - // Not sure how to handle typeless? - if (bufferResourceDesc.elementSize == 0) - { - info.format = VK_FORMAT_R32_SFLOAT; // DXGI_FORMAT_R32_TYPELESS ? - } - - info.buffer = bufferResource->m_buffer.m_buffer; - info.offset = 0; - info.range = bufferResourceDesc.sizeInBytes; - - SLANG_VK_RETURN_NULL_ON_FAIL(m_api.vkCreateBufferView(m_device, &info, nullptr, &dstDetail.m_uav)); - } - - // TODO: Setup views. - // VkImageView srv - - - break; - } - case BindingType::Sampler: - { - VkSamplerCreateInfo samplerInfo = { VK_STRUCTURE_TYPE_SAMPLER_CREATE_INFO }; - - samplerInfo.magFilter = VK_FILTER_LINEAR; - samplerInfo.minFilter = VK_FILTER_LINEAR; - - samplerInfo.addressModeU = VK_SAMPLER_ADDRESS_MODE_REPEAT; - samplerInfo.addressModeV = VK_SAMPLER_ADDRESS_MODE_REPEAT; - samplerInfo.addressModeW = VK_SAMPLER_ADDRESS_MODE_REPEAT; - - samplerInfo.anisotropyEnable = VK_FALSE; - samplerInfo.maxAnisotropy = 1; - - samplerInfo.borderColor = VK_BORDER_COLOR_INT_OPAQUE_BLACK; - samplerInfo.unnormalizedCoordinates = VK_FALSE; - samplerInfo.compareEnable = VK_FALSE; - samplerInfo.compareOp = VK_COMPARE_OP_ALWAYS; - samplerInfo.mipmapMode = VK_SAMPLER_MIPMAP_MODE_LINEAR; - - SLANG_VK_RETURN_NULL_ON_FAIL(m_api.vkCreateSampler(m_device, &samplerInfo, nullptr, &dstDetail.m_sampler)); - - break; - } - case BindingType::Texture: - { - if (!srcBinding.resource || !srcBinding.resource->isTexture()) - { - assert(!"Needs to have a texture resource set"); - return nullptr; - } - - TextureResourceImpl* textureResource = static_cast(srcBinding.resource.Ptr()); - const TextureResource::Desc& texDesc = textureResource->getDesc(); - - VkImageViewType imageViewType = _calcImageViewType(textureResource->getType(), texDesc); - if (imageViewType == VK_IMAGE_VIEW_TYPE_MAX_ENUM) - { - assert(!"Invalid view type"); - return nullptr; - } - const VkFormat format = VulkanUtil::getVkFormat(texDesc.format); - if (format == VK_FORMAT_UNDEFINED) - { - assert(!"Unhandled image format"); - return nullptr; - } - - // Create the image view - - VkImageViewCreateInfo viewInfo = {}; - viewInfo.sType = VK_STRUCTURE_TYPE_IMAGE_VIEW_CREATE_INFO; - viewInfo.image = textureResource->m_image; - viewInfo.viewType = imageViewType; - viewInfo.format = format; - viewInfo.subresourceRange.aspectMask = VK_IMAGE_ASPECT_COLOR_BIT; - viewInfo.subresourceRange.baseMipLevel = 0; - viewInfo.subresourceRange.levelCount = 1; - viewInfo.subresourceRange.baseArrayLayer = 0; - viewInfo.subresourceRange.layerCount = 1; - - viewInfo.components.r = VK_COMPONENT_SWIZZLE_IDENTITY; - viewInfo.components.g = VK_COMPONENT_SWIZZLE_IDENTITY; - viewInfo.components.b = VK_COMPONENT_SWIZZLE_IDENTITY; - viewInfo.components.a = VK_COMPONENT_SWIZZLE_IDENTITY; - - SLANG_VK_RETURN_NULL_ON_FAIL(m_api.vkCreateImageView(m_device, &viewInfo, nullptr, &dstDetail.m_srv)); - - break; - } - case BindingType::CombinedTextureSampler: - { - assert(!"not implemented"); - return nullptr; - } - } - } - - return bindingState.detach();; -} -#endif - static VkDescriptorType translateDescriptorType(DescriptorSlotType type) { switch(type) diff --git a/tools/render-test/cpu-compute-util.cpp b/tools/render-test/cpu-compute-util.cpp index 72e44fb78..7c9103cb3 100644 --- a/tools/render-test/cpu-compute-util.cpp +++ b/tools/render-test/cpu-compute-util.cpp @@ -420,8 +420,9 @@ SlangResult CPUComputeUtil::fillRuntimeHandleInBuffers( Context& context, ISlangSharedLibrary* sharedLib) { + auto request = compilationAndLayout.output.getRequestForReflection(); Slang::ComPtr linkage; - spCompileRequest_getSession(compilationAndLayout.output.request, linkage.writeRef()); + spCompileRequest_getSession(request, linkage.writeRef()); auto& inputLayout = compilationAndLayout.layout; for (auto& entry : inputLayout.entries) { @@ -433,7 +434,7 @@ SlangResult CPUComputeUtil::fillRuntimeHandleInBuffers( case RTTIDataEntryType::RTTIObject: { auto reflection = - slang::ShaderReflection::get(compilationAndLayout.output.request); + slang::ShaderReflection::get(request); auto concreteType = reflection->findTypeByName(rtti.typeName.getBuffer()); ComPtr outName; linkage->getTypeRTTIMangledName(concreteType, outName.writeRef()); @@ -444,7 +445,7 @@ SlangResult CPUComputeUtil::fillRuntimeHandleInBuffers( break; case RTTIDataEntryType::WitnessTable: { - auto reflection = slang::ShaderReflection::get(compilationAndLayout.output.request); + auto reflection = slang::ShaderReflection::get(request); auto concreteType = reflection->findTypeByName(rtti.typeName.getBuffer()); if (!concreteType) return SLANG_FAIL; @@ -503,7 +504,7 @@ SlangResult CPUComputeUtil::fillRuntimeHandleInBuffers( /* static */SlangResult CPUComputeUtil::calcBindings(const ShaderCompilerUtil::OutputAndLayout& compilationAndLayout, Context& outContext) { - auto request = compilationAndLayout.output.request; + auto request = compilationAndLayout.output.getRequestForReflection(); auto reflection = (slang::ShaderReflection*) spGetReflection(request); const auto& sourcePath = compilationAndLayout.sourcePath; @@ -684,7 +685,7 @@ SlangResult CPUComputeUtil::fillRuntimeHandleInBuffers( /* static */SlangResult CPUComputeUtil::calcExecuteInfo(ExecuteStyle style, ISlangSharedLibrary* sharedLib, const uint32_t dispatchSize[3], const ShaderCompilerUtil::OutputAndLayout& compilationAndLayout, Context& context, ExecuteInfo& out) { - auto request = compilationAndLayout.output.request; + auto request = compilationAndLayout.output.getRequestForReflection(); auto reflection = (slang::ShaderReflection*) spGetReflection(request); slang::EntryPointReflection* entryPoint = nullptr; diff --git a/tools/render-test/cuda/cuda-compute-util.cpp b/tools/render-test/cuda/cuda-compute-util.cpp index cc84d4a00..ea7dab163 100644 --- a/tools/render-test/cuda/cuda-compute-util.cpp +++ b/tools/render-test/cuda/cuda-compute-util.cpp @@ -1000,7 +1000,7 @@ static SlangResult _invokeComputeProgram( const uint32_t dispatchSize[3], CUDAComputeUtil::Context& outContext) { - auto reflection = slang::ProgramLayout::get(outputAndLayout.output.request); + auto reflection = slang::ProgramLayout::get(outputAndLayout.output.getRequestForReflection()); auto& bindSet = outContext.m_bindSet; auto& bindRoot = outContext.m_bindRoot; @@ -1403,7 +1403,7 @@ static SlangResult _fillRuntimeHandlesInBuffers( ScopeCUDAModule& cudaModule) { Slang::ComPtr linkage; - spCompileRequest_getSession(compilationAndLayout.output.request, linkage.writeRef()); + spCompileRequest_getSession(compilationAndLayout.output.getRequestForReflection(), linkage.writeRef()); auto& inputLayout = compilationAndLayout.layout; for (auto& entry : inputLayout.entries) { @@ -1415,7 +1415,7 @@ static SlangResult _fillRuntimeHandlesInBuffers( case RTTIDataEntryType::RTTIObject: { auto reflection = - slang::ShaderReflection::get(compilationAndLayout.output.request); + slang::ShaderReflection::get(compilationAndLayout.output.getRequestForReflection()); auto concreteType = reflection->findTypeByName(rtti.typeName.getBuffer()); ComPtr outName; linkage->getTypeRTTIMangledName(concreteType, outName.writeRef()); @@ -1431,7 +1431,7 @@ static SlangResult _fillRuntimeHandlesInBuffers( case RTTIDataEntryType::WitnessTable: { auto reflection = - slang::ShaderReflection::get(compilationAndLayout.output.request); + slang::ShaderReflection::get(compilationAndLayout.output.getRequestForReflection()); auto concreteType = reflection->findTypeByName(rtti.typeName.getBuffer()); if (!concreteType) return SLANG_FAIL; @@ -1527,7 +1527,7 @@ static SlangResult _setUpArguments( const uint32_t dispatchSize[3], CUDAComputeUtil::Context& outContext) { - auto reflection = slang::ProgramLayout::get(outputAndLayout.output.request); + auto reflection = slang::ProgramLayout::get(outputAndLayout.output.getRequestForReflection()); auto& bindSet = outContext.m_bindSet; auto& bindRoot = outContext.m_bindRoot; @@ -1835,7 +1835,7 @@ SlangResult _loadAndInvokeKernel( auto& bindSet = outContext.m_bindSet; auto& bindRoot = outContext.m_bindRoot; - auto request = outputAndLayout.output.request; + auto request = outputAndLayout.output.getRequestForReflection(); auto reflection = (slang::ShaderReflection*) spGetReflection(request); // Load cuda module first so its symbols may be queried and filled into argument buffers. diff --git a/tools/render-test/options.cpp b/tools/render-test/options.cpp index 797439cfa..100c353e0 100644 --- a/tools/render-test/options.cpp +++ b/tools/render-test/options.cpp @@ -250,6 +250,10 @@ static SlangResult _setRendererType(RendererType type, const char* arg, Slang::W outOptions.nvapiExtnSlot = (*argCursor++); } + else if (strcmp(arg, "-shaderobj") == 0) + { + outOptions.useShaderObjects = true; + } else { // Lookup diff --git a/tools/render-test/options.h b/tools/render-test/options.h index ddb903a4a..646cf3a76 100644 --- a/tools/render-test/options.h +++ b/tools/render-test/options.h @@ -3,6 +3,10 @@ #include +#ifndef SLANG_HANDLE_RESULT_FAIL +#define SLANG_HANDLE_RESULT_FAIL(x) assert(!"failure") +#endif + #include "../../slang-com-helper.h" #include "../../source/core/slang-writer.h" @@ -63,6 +67,8 @@ struct Options bool useDXIL = false; bool onlyStartup = false; + bool useShaderObjects = false; + bool performanceProfile = false; bool dontAddDefaultEntryPoints = false; diff --git a/tools/render-test/render-test-main.cpp b/tools/render-test/render-test-main.cpp index f6f3e4ce6..197ee2b46 100644 --- a/tools/render-test/render-test-main.cpp +++ b/tools/render-test/render-test-main.cpp @@ -67,60 +67,1865 @@ static void _outputProfileTime(uint64_t startTicks, uint64_t endTicks) out.print("profile-time=%g\n", time); } +class ProgramVars; + +struct ShaderOutputPlan +{ + struct Item + { + Index inputLayoutEntryIndex; + RefPtr resource; + }; + + List items; +}; + class RenderTestApp : public WindowListener { - public: +public: + // WindowListener + virtual Result update(Window* window) SLANG_OVERRIDE; + + // 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( + SlangSession* session, + Renderer* renderer, + const Options& options, + const ShaderCompilerUtil::Input& input) = 0; + void runCompute(); + void renderFrame(); + void finalize(); + + virtual void applyBinding(PipelineType pipelineType) = 0; + virtual void setProjectionMatrix() = 0; + virtual Result writeBindingOutput(BindRoot* bindRoot, const char* fileName) = 0; + + Result writeScreen(const char* filename); + +protected: + /// Called in initialize + Result _initializeShaders( + SlangSession* session, + Renderer* renderer, + Options::ShaderProgramType shaderType, + const ShaderCompilerUtil::Input& input); + + uint64_t m_startTicks; + + // variables for state to be used for rendering... + uintptr_t m_constantBufferSize; + + RefPtr m_renderer; + + RefPtr m_inputLayout; + RefPtr m_vertexBuffer; + RefPtr m_shaderProgram; + RefPtr m_pipelineState; + + ShaderCompilerUtil::OutputAndLayout m_compilationOutput; + + ShaderInputLayout m_shaderInputLayout; ///< The binding layout + + Options m_options; +}; + +class LegacyRenderTestApp : public RenderTestApp +{ +public: + virtual void applyBinding(PipelineType pipelineType) SLANG_OVERRIDE; + virtual void setProjectionMatrix() SLANG_OVERRIDE; + virtual Result initialize( + SlangSession* session, + Renderer* renderer, + const Options& options, + const ShaderCompilerUtil::Input& input) SLANG_OVERRIDE; + + BindingStateImpl* getBindingState() const { return m_bindingState; } + + virtual Result writeBindingOutput(BindRoot* bindRoot, const char* fileName) override; + +protected: + uintptr_t m_constantBufferSize; + RefPtr m_constantBuffer; + RefPtr m_bindingState; + int m_numAddedConstantBuffers; ///< Constant buffers can be added to the binding directly. Will be added at the end. +}; + +class ShaderObjectRenderTestApp : public RenderTestApp +{ +public: + virtual void applyBinding(PipelineType pipelineType) SLANG_OVERRIDE; + virtual void setProjectionMatrix() SLANG_OVERRIDE; + virtual Result initialize( + SlangSession* session, + Renderer* renderer, + const Options& options, + const ShaderCompilerUtil::Input& input) SLANG_OVERRIDE; + virtual Result writeBindingOutput(BindRoot* bindRoot, const char* fileName) override; + +protected: + RefPtr m_programVars; + ShaderOutputPlan m_outputPlan; +}; + +struct ShaderOffset +{ + SlangInt uniformOffset = 0; + SlangInt bindingRangeIndex = 0; + SlangInt bindingArrayIndex = 0; +}; + +class ShaderObjectLayout : public RefObject +{ +public: + struct BindingRangeInfo + { + slang::BindingType bindingType; + Index count; + Index baseIndex; + Index descriptorSetIndex; + Index rangeIndexInDescriptorSet; +// Index subObjectRangeIndex = -1; + }; + + struct SubObjectRangeInfo + { + RefPtr layout; +// Index baseIndex; +// Index count; + Index bindingRangeIndex; + }; + + struct DescriptorSetInfo : public RefObject + { + RefPtr layout; + Slang::Int space = -1; + }; + + struct Builder + { + public: + Builder(Renderer* renderer) + : m_renderer(renderer) + {} + + List m_bindingRanges; + List m_subObjectRanges; + + Index m_resourceViewCount = 0; + Index m_samplerCount = 0; + Index m_combinedTextureSamplerCount = 0; + Index m_subObjectCount = 0; + Index m_varyingInputCount = 0; + Index m_varyingOutputCount = 0; + + struct DescriptorSetBuildInfo : public RefObject + { + List slotRangeDescs; + Index space; + }; + List> m_descriptorSetBuildInfos; + Dictionary m_mapSpaceToDescriptorSetIndex; + + Index findOrAddDescriptorSet(Index space) + { + Index index; + if(m_mapSpaceToDescriptorSetIndex.TryGetValue(space, index)) + return index; + + RefPtr info = new DescriptorSetBuildInfo(); + info->space = space; + + index = m_descriptorSetBuildInfos.getCount(); + m_descriptorSetBuildInfos.add(info); + + m_mapSpaceToDescriptorSetIndex.Add(space, index); + return index; + } + + static DescriptorSlotType _mapDescriptorType(slang::BindingType slangBindingType) + { + switch(slangBindingType) + { + default: return DescriptorSlotType::Unknown; + + #define CASE(FROM, TO) \ + case slang::BindingType::FROM: return DescriptorSlotType::TO + + CASE(Sampler, Sampler); + CASE(CombinedTextureSampler, CombinedImageSampler); + CASE(Texture, SampledImage); + CASE(MutableTexture, StorageImage); + CASE(TypedBuffer, UniformTexelBuffer); + CASE(MutableTypedBuffer, StorageTexelBuffer); + CASE(RawBuffer, UniformBuffer); + CASE(MutableRawBuffer, StorageBuffer); + CASE(InputRenderTarget, InputAttachment); + CASE(InlineUniformData, InlineUniformBlock); + CASE(RayTracingAccelerationStructure, RayTracingAccelerationStructure); + CASE(ConstantBuffer, UniformBuffer); + CASE(PushConstant, RootConstant); + + #undef CASE + } + } + + slang::TypeLayoutReflection* unwrapParameterGroups(slang::TypeLayoutReflection* typeLayout) + { + for(;;) + { + if(!typeLayout->getType()) + { + if(auto elementTypeLayout = typeLayout->getElementTypeLayout()) + typeLayout = elementTypeLayout; + } + + switch(typeLayout->getKind()) + { + default: + return typeLayout; + + case slang::TypeReflection::Kind::ConstantBuffer: + case slang::TypeReflection::Kind::ParameterBlock: + typeLayout = typeLayout->getElementTypeLayout(); + continue; + } + } + } + + void _addDescriptorSets(slang::TypeLayoutReflection* typeLayout, slang::VariableLayoutReflection* varLayout = nullptr) + { + SlangInt descriptorSetCount = typeLayout->getDescriptorSetCount(); + for(SlangInt s = 0; s < descriptorSetCount; ++s) + { + auto descriptorSetIndex = findOrAddDescriptorSet(typeLayout->getDescriptorSetSpaceOffset(s)); + auto descriptorSetInfo = m_descriptorSetBuildInfos[descriptorSetIndex]; + + SlangInt descriptorRangeCount = typeLayout->getDescriptorSetDescriptorRangeCount(s); + for(SlangInt r = 0; r < descriptorRangeCount; ++r) + { + auto slangBindingType = typeLayout->getDescriptorSetDescriptorRangeType(s, r); + auto gfxDescriptorType = _mapDescriptorType(slangBindingType); + + DescriptorSetLayout::SlotRangeDesc descriptorRangeDesc; + descriptorRangeDesc.binding = typeLayout->getDescriptorSetDescriptorRangeIndexOffset(s, r); + descriptorRangeDesc.count = typeLayout->getDescriptorSetDescriptorRangeDescriptorCount(s, r); + descriptorRangeDesc.type = gfxDescriptorType; + + if(varLayout) + { + auto category = typeLayout->getDescriptorSetDescriptorRangeCategory(s, r); + descriptorRangeDesc.binding += varLayout->getOffset(category); + } + + descriptorSetInfo->slotRangeDescs.add(descriptorRangeDesc); + } + } + } + + Result setElementTypeLayout(slang::TypeLayoutReflection* typeLayout) + { + typeLayout = unwrapParameterGroups(typeLayout); + + m_elementTypeLayout = typeLayout; + + // First we will use the Slang layout information to allocate + // the descriptor set layout(s) required to store values + // of the given type. + // + _addDescriptorSets(typeLayout); + + // Next we will compute the binding ranges that are used to store + // the logical contents of the object in memory. These will relate + // to the descriptor ranges in the various sets, but not always + // in a one-to-one fashion. + + SlangInt bindingRangeCount = typeLayout->getBindingRangeCount(); + for(SlangInt r = 0; r < bindingRangeCount; ++r) + { + slang::BindingType slangBindingType = typeLayout->getBindingRangeType(r); + SlangInt count = typeLayout->getBindingRangeBindingCount(r); + slang::TypeLayoutReflection* slangLeafTypeLayout = typeLayout->getBindingRangeLeafTypeLayout(r); + + SlangInt descriptorSetIndex = typeLayout->getBindingRangeDescriptorSetIndex(r); + SlangInt rangeIndexInDescriptorSet = typeLayout->getBindingRangeFirstDescriptorRangeIndex(r); + + Index baseIndex = 0; + switch(slangBindingType) + { + case slang::BindingType::ConstantBuffer: + case slang::BindingType::ParameterBlock: + case slang::BindingType::ExistentialValue: + baseIndex = m_subObjectCount; + m_subObjectCount += count; + break; + + case slang::BindingType::Sampler: + baseIndex = m_samplerCount; + m_samplerCount += count; + break; + + case slang::BindingType::CombinedTextureSampler: + baseIndex = m_combinedTextureSamplerCount; + m_combinedTextureSamplerCount += count; + break; + + case slang::BindingType::VaryingInput: + baseIndex = m_varyingInputCount; + m_varyingInputCount += count; + break; + + case slang::BindingType::VaryingOutput: + baseIndex = m_varyingOutputCount; + m_varyingOutputCount += count; + break; + + default: + baseIndex = m_resourceViewCount; + m_resourceViewCount += count; + break; + } + + BindingRangeInfo bindingRangeInfo; + bindingRangeInfo.bindingType = slangBindingType; + bindingRangeInfo.count = count; +// bindingRangeInfo.descriptorSetIndex = descriptorSetIndex; +// bindingRangeInfo.rangeIndexInDescriptorSet = slotRangeIndex; +// bindingRangeInfo.subObjectRangeIndex = subObjectRangeIndex; + bindingRangeInfo.baseIndex = baseIndex; + bindingRangeInfo.descriptorSetIndex = descriptorSetIndex; + bindingRangeInfo.rangeIndexInDescriptorSet = rangeIndexInDescriptorSet; + + m_bindingRanges.add(bindingRangeInfo); + +#if 0 + SlangInt binding = typeLayout->getBindingRangeIndexOffset(r); + SlangInt space = typeLayout->getBindingRangeSpaceOffset(r); + SlangInt subObjectRangeIndex = typeLayout->getBindingRangeSubObjectRangeIndex(r); + + DescriptorSetLayout::SlotRangeDesc slotRange; + slotRange.type = _mapDescriptorType(slangBindingType); + slotRange.count = count; + slotRange.binding = binding; + + Index descriptorSetIndex = findOrAddDescriptorSet(space); + RefPtr descriptorSetInfo = m_descriptorSetInfos[descriptorSetIndex]; + + Index slotRangeIndex = descriptorSetInfo->slotRanges.getCount(); + descriptorSetInfo->slotRanges.add(slotRange); +#endif + } + + SlangInt subObjectRangeCount = typeLayout->getSubObjectRangeCount(); + for(SlangInt r = 0; r < subObjectRangeCount; ++r) + { + SlangInt bindingRangeIndex = typeLayout->getSubObjectRangeBindingRangeIndex(r); + auto slangBindingType = typeLayout->getBindingRangeType(bindingRangeIndex); + slang::TypeLayoutReflection* slangLeafTypeLayout = typeLayout->getBindingRangeLeafTypeLayout(bindingRangeIndex); + + // A sub-object range can either represent a sub-object of a known + // type, like a `ConstantBuffer` or `ParameterBlock` + // (in which case we can pre-compute a layout to use, based on + // the type `Foo`) *or* it can represent a sub-object of some + // existential type (e.g., `IBar`) in which case we cannot + // know the appropraite type/layout of sub-object to allocate. + // + RefPtr subObjectLayout; + if(slangBindingType != slang::BindingType::ExistentialValue) + { + ShaderObjectLayout::createForElementType(m_renderer, slangLeafTypeLayout->getElementTypeLayout(), subObjectLayout.writeRef()); + } + + SubObjectRangeInfo subObjectRange; + subObjectRange.bindingRangeIndex = bindingRangeIndex; + subObjectRange.layout = subObjectLayout; + + m_subObjectRanges.add(subObjectRange); + } + +#if 0 + SlangInt subObjectRangeCount = typeLayout->getSubObjectRangeCount(); + for(SlangInt r = 0; r < subObjectRangeCount; ++r) + { + + // TODO: Still need a way to map the binding ranges for + // the sub-object over so that they can be used to + // set/get the sub-object as needed. + } +#endif + return SLANG_OK; + } + + SlangResult build(ShaderObjectLayout** outLayout) + { + RefPtr layout = new ShaderObjectLayout(); + SLANG_RETURN_ON_FAIL(layout->_init(this)); + + *outLayout = layout.detach(); + return SLANG_OK; + } + + Renderer* m_renderer = nullptr; + slang::TypeLayoutReflection* m_elementTypeLayout = nullptr; + }; + + static Result createForElementType(Renderer* renderer, slang::TypeLayoutReflection* elementType, ShaderObjectLayout** outLayout) + { + Builder builder(renderer); + builder.setElementTypeLayout(elementType); + return builder.build(outLayout); + } + + List> const& getDescriptorSets() { return m_descriptorSets; } + + List const& getBindingRanges() { return m_bindingRanges; } + + Index getBindingRangeCount() { return m_bindingRanges.getCount(); } + + BindingRangeInfo const& getBindingRange(Index index) { return m_bindingRanges[index]; } + + slang::TypeLayoutReflection* getElementTypeLayout() + { + return m_elementTypeLayout; + } + + Index getResourceViewCount() { return m_resourceViewCount; } + Index getSamplerCount() { return m_samplerCount; } + Index getCombinedTextureSamplerCount() { return m_combinedTextureSamplerCount; } + Index getSubObjectCount() { return m_subObjectCount; } + + SubObjectRangeInfo const& getSubObjectRange(Index index) { return m_subObjectRanges[index]; } + List const& getSubObjectRanges() { return m_subObjectRanges; } + + Renderer* getRenderer() + { + return m_renderer; + } + +protected: + Result _init(Builder const* builder) + { + auto renderer = builder->m_renderer; + m_renderer = renderer; + + m_elementTypeLayout = builder->m_elementTypeLayout; + m_bindingRanges = builder->m_bindingRanges; + + for(auto descriptorSetBuildInfo : builder->m_descriptorSetBuildInfos) + { + auto& slotRangeDescs = descriptorSetBuildInfo->slotRangeDescs; + DescriptorSetLayout::Desc desc; + desc.slotRangeCount = slotRangeDescs.getCount(); + desc.slotRanges = slotRangeDescs.getBuffer(); + + RefPtr descriptorSetLayout; + SLANG_RETURN_ON_FAIL(m_renderer->createDescriptorSetLayout(desc, descriptorSetLayout.writeRef())); + + RefPtr descriptorSetInfo = new DescriptorSetInfo(); + descriptorSetInfo->layout = descriptorSetLayout; + descriptorSetInfo->space = descriptorSetBuildInfo->space; + + m_descriptorSets.add(descriptorSetInfo); + } + + m_resourceViewCount = builder->m_resourceViewCount; + m_samplerCount = builder->m_samplerCount; + m_combinedTextureSamplerCount = builder->m_combinedTextureSamplerCount; + m_subObjectCount = builder->m_subObjectCount; + + m_subObjectRanges = builder->m_subObjectRanges; + + return SLANG_OK; + } + + Renderer* m_renderer; + List> m_descriptorSets; + List m_bindingRanges; + slang::TypeLayoutReflection* m_elementTypeLayout; + Index m_resourceViewCount = 0; + Index m_samplerCount = 0; + Index m_combinedTextureSamplerCount = 0; + Index m_subObjectCount = 0; + List m_subObjectRanges; +}; + +class EntryPointLayout : public ShaderObjectLayout +{ + typedef ShaderObjectLayout Super; +public: + + struct VaryingInputInfo + { + }; + + struct VaryingOutputInfo + { + }; + + struct Builder : Super::Builder + { + Builder(Renderer* renderer) + : Super::Builder(renderer) + {} + + Result build(EntryPointLayout** outLayout) + { + RefPtr layout = new EntryPointLayout(); + SLANG_RETURN_ON_FAIL(layout->_init(this)); + + *outLayout = layout.detach(); + return SLANG_OK; + } + + void _addEntryPointParam(slang::VariableLayoutReflection* entryPointParam) + { + auto slangStage = entryPointParam->getStage(); + auto typeLayout = entryPointParam->getTypeLayout(); + + SlangInt bindingRangeCount = typeLayout->getBindingRangeCount(); + for(SlangInt r = 0; r < bindingRangeCount; ++r) + { + slang::BindingType slangBindingType = typeLayout->getBindingRangeType(r); + SlangInt count = typeLayout->getBindingRangeBindingCount(r); + + switch(slangBindingType) + { + default: + break; + + case slang::BindingType::VaryingInput: + { + VaryingInputInfo info; + + m_varyingInputs.add(info); + } + break; + + case slang::BindingType::VaryingOutput: + { + VaryingOutputInfo info; + m_varyingOutputs.add(info); + } + break; + } + } + } + + void addEntryPointParams(slang::EntryPointLayout* entryPointLayout) + { + m_slangEntryPointLayout = entryPointLayout; + + setElementTypeLayout(entryPointLayout->getTypeLayout()); + + m_stage = translateStage(entryPointLayout->getStage()); + _addEntryPointParam(entryPointLayout->getVarLayout()); + _addEntryPointParam(entryPointLayout->getResultVarLayout()); + } + + slang::EntryPointLayout* m_slangEntryPointLayout = nullptr; + + gfx::StageType m_stage; + List m_varyingInputs; + List m_varyingOutputs; + }; + + Result _init(Builder const* builder) + { + auto renderer = builder->m_renderer; + + SLANG_RETURN_ON_FAIL(Super::_init(builder)); + + m_slangEntryPointLayout = builder->m_slangEntryPointLayout; + m_stage = builder->m_stage; + m_varyingInputs = builder->m_varyingInputs; + m_varyingOutputs = builder->m_varyingOutputs; + + return SLANG_OK; + } + + List const& getVaryingInputs() { return m_varyingInputs; } + List const& getVaryingOutputs() { return m_varyingOutputs; } + + gfx::StageType getStage() const { return m_stage; } + + slang::EntryPointLayout* getSlangLayout() const { return m_slangEntryPointLayout; }; + + slang::EntryPointLayout* m_slangEntryPointLayout; + gfx::StageType m_stage; + List m_varyingInputs; + List m_varyingOutputs; +}; + +class ProgramLayout : public ShaderObjectLayout +{ + typedef ShaderObjectLayout Super; +public: + + struct EntryPointInfo + { + RefPtr layout; + Index rangeOffset; + }; + + + struct Builder : Super::Builder + { + Builder(Renderer* renderer) + : Super::Builder(renderer) + {} + + Result build(ProgramLayout** outLayout) + { + RefPtr layout = new ProgramLayout(); + SLANG_RETURN_ON_FAIL(layout->_init(this)); + + *outLayout = layout.detach(); + return SLANG_OK; + } + + void addGlobalParams(slang::VariableLayoutReflection* globalsLayout) + { + setElementTypeLayout(globalsLayout->getTypeLayout()); + } + + void addEntryPoint(EntryPointLayout* entryPointLayout) + { + EntryPointInfo info; + info.layout = entryPointLayout; + + if(m_descriptorSetBuildInfos.getCount()) + { + info.rangeOffset = m_descriptorSetBuildInfos[0]->slotRangeDescs.getCount(); + } + + auto slangEntryPointLayout = entryPointLayout->getSlangLayout(); + _addDescriptorSets(slangEntryPointLayout->getTypeLayout(), slangEntryPointLayout->getVarLayout()); + + m_entryPoints.add(info); + } + + List m_entryPoints; + }; + + Slang::Int getRenderTargetCount() + { + return m_renderTargetCount; + } + + PipelineLayout* getPipelineLayout() { return m_pipelineLayout; } + + Index findEntryPointIndex(gfx::StageType stage) + { + auto entryPointCount = m_entryPoints.getCount(); + for(Index i = 0; i < entryPointCount; ++i) + { + auto entryPoint = m_entryPoints[i]; + if(entryPoint.layout->getStage() == stage) + return i; + } + return -1; + } + + EntryPointInfo const& getEntryPoint(Index index) + { + return m_entryPoints[index]; + } + + List const& getEntryPoints() const { return m_entryPoints; } + +protected: + Result _init(Builder const* builder) + { + auto renderer = builder->m_renderer; + + SLANG_RETURN_ON_FAIL(Super::_init(builder)); + + m_entryPoints = builder->m_entryPoints; + + List pipelineDescriptorSets; + _addDescriptorSetsRec(this, pipelineDescriptorSets); + +#if 0 + _createInputLayout(builder); +#endif + + auto fragmentEntryPointIndex = findEntryPointIndex(gfx::StageType::Fragment); + if(fragmentEntryPointIndex != -1) + { + auto fragmentEntryPoint = getEntryPoint(fragmentEntryPointIndex); + m_renderTargetCount = fragmentEntryPoint.layout->getVaryingOutputs().getCount(); + } + + PipelineLayout::Desc pipelineLayoutDesc; + pipelineLayoutDesc.renderTargetCount = m_renderTargetCount; + pipelineLayoutDesc.descriptorSetCount = pipelineDescriptorSets.getCount(); + pipelineLayoutDesc.descriptorSets = pipelineDescriptorSets.getBuffer(); + + SLANG_RETURN_ON_FAIL(renderer->createPipelineLayout(pipelineLayoutDesc, m_pipelineLayout.writeRef())); + + return SLANG_OK; + } + + static void _addDescriptorSetsRec(ShaderObjectLayout* layout, List& ioPipelineDescriptorSets) + { + for(auto descriptorSetInfo : layout->getDescriptorSets()) + { + PipelineLayout::DescriptorSetDesc pipelineDescriptorSet; + pipelineDescriptorSet.layout = descriptorSetInfo->layout; + pipelineDescriptorSet.space = descriptorSetInfo->space; + + ioPipelineDescriptorSets.add(pipelineDescriptorSet); + } + + // TODO: next we need to recurse into the "sub-objects" of `layout` and + // add their descriptor sets as well. + } + +#if 0 + Result _createInputLayout(Builder const* builder) + { + auto renderer = builder->m_renderer; + + List const& inputElements = builder->getInputElements(); + SLANG_RETURN_ON_FAIL(renderer->createInputLayout(inputElements.getBuffer(), inputElements.getCount(), m_inputLayout.writeRef())); + + return SLANG_OK; + } +#endif + + List m_entryPoints; + gfx::UInt m_renderTargetCount = 0; + + RefPtr m_pipelineLayout; +}; + +class ShaderObject : public RefObject +{ +public: + static Result create(Renderer* renderer, ShaderObjectLayout* layout, ShaderObject** outShaderObject) + { + RefPtr object = new ShaderObject(); + SLANG_RETURN_ON_FAIL(object->init(renderer, layout)); + + *outShaderObject = object.detach(); + return SLANG_OK; + } + + Renderer* getRenderer() + { + return m_layout->getRenderer(); + } + + ShaderObjectLayout* getLayout() + { + return m_layout; + } + + slang::TypeLayoutReflection* getElementTypeLayout() + { + return m_layout->getElementTypeLayout(); + } + + SlangResult setData(ShaderOffset const& offset, void const* data, size_t size) + { + Renderer* renderer = getRenderer(); + + char* dest = (char*) renderer->map(m_buffer, MapFlavor::HostWrite); + memcpy(dest + offset.uniformOffset, data, size); + renderer->unmap(m_buffer); + + return SLANG_OK; + } + + SlangResult setObject(ShaderOffset const& offset, ShaderObject* object) + { + if(offset.bindingRangeIndex < 0) return SLANG_E_INVALID_ARG; + if(offset.bindingRangeIndex >= m_layout->getBindingRangeCount()) return SLANG_E_INVALID_ARG; + auto& bindingRange = m_layout->getBindingRange(offset.bindingRangeIndex); + + // TODO: Is this reasonable to store the base index directly in the binding range? + m_objects[bindingRange.baseIndex + offset.bindingArrayIndex] = object; + +// auto& subObjectRange = m_layout->getSubObjectRange(bindingRange.subObjectRangeIndex); +// m_objects[subObjectRange.baseIndex + offset.bindingArrayIndex] = object; + +#if 0 + + SLANG_ASSERT(bindingRange.descriptorSetIndex >= 0); + SLANG_ASSERT(bindingRange.descriptorSetIndex < m_descriptorSets.getCount()); + auto& descriptorSet = m_descriptorSets[bindingRange.descriptorSetIndex]; + + descriptorSet->setConstantBuffer(bindingRange.rangeIndexInDescriptorSet, offset.bindingArrayIndex, buffer); + return SLANG_OK; +#else + return SLANG_E_NOT_IMPLEMENTED; +#endif + } + + SlangResult getObject(ShaderOffset const& offset, ShaderObject** outObject) + { + SLANG_ASSERT(outObject); + if(offset.bindingRangeIndex < 0) return SLANG_E_INVALID_ARG; + if(offset.bindingRangeIndex >= m_layout->getBindingRangeCount()) return SLANG_E_INVALID_ARG; + auto& bindingRange = m_layout->getBindingRange(offset.bindingRangeIndex); + + *outObject = m_objects[bindingRange.baseIndex + offset.bindingArrayIndex]; + +// auto& subObjectRange = m_layout->getSubObjectRange(bindingRange.subObjectRangeIndex); +// *outObject = m_objects[subObjectRange.baseIndex + offset.bindingArrayIndex]; + + return SLANG_OK; + +#if 0 + SLANG_ASSERT(bindingRange.descriptorSetIndex >= 0); + SLANG_ASSERT(bindingRange.descriptorSetIndex < m_descriptorSets.getCount()); + auto& descriptorSet = m_descriptorSets[bindingRange.descriptorSetIndex]; + + descriptorSet->setConstantBuffer(bindingRange.rangeIndexInDescriptorSet, offset.bindingArrayIndex, buffer); + return SLANG_OK; +#endif + } + + ShaderObject* getObject(ShaderOffset const& offset) + { + ShaderObject* object = nullptr; + SLANG_RETURN_NULL_ON_FAIL(getObject(offset, &object)); + return object; + } + + SlangResult setResource(ShaderOffset const& offset, ResourceView* resourceView) + { + if(offset.bindingRangeIndex < 0) return SLANG_E_INVALID_ARG; + if(offset.bindingRangeIndex >= m_layout->getBindingRangeCount()) return SLANG_E_INVALID_ARG; + auto& bindingRange = m_layout->getBindingRange(offset.bindingRangeIndex); + + m_resourceViews[bindingRange.baseIndex + offset.bindingArrayIndex] = resourceView; + return SLANG_OK; + } + + SlangResult setSampler(ShaderOffset const& offset, SamplerState* sampler) + { + if(offset.bindingRangeIndex < 0) return SLANG_E_INVALID_ARG; + if(offset.bindingRangeIndex >= m_layout->getBindingRangeCount()) return SLANG_E_INVALID_ARG; + auto& bindingRange = m_layout->getBindingRange(offset.bindingRangeIndex); + + m_samplers[bindingRange.baseIndex + offset.bindingArrayIndex] = sampler; + return SLANG_OK; + } + + SlangResult setCombinedTextureSampler(ShaderOffset const& offset, ResourceView* textureView, SamplerState* sampler) + { + if(offset.bindingRangeIndex < 0) return SLANG_E_INVALID_ARG; + if(offset.bindingRangeIndex >= m_layout->getBindingRangeCount()) return SLANG_E_INVALID_ARG; + auto& bindingRange = m_layout->getBindingRange(offset.bindingRangeIndex); + + auto& slot = m_combinedTextureSamplers[bindingRange.baseIndex + offset.bindingArrayIndex]; + slot.textureView = textureView; + slot.sampler = sampler; + return SLANG_OK; + } + +protected: + friend class ProgramVars; + + Result init(Renderer* renderer, ShaderObjectLayout* layout) + { + m_layout = layout; + + // If the layout tells us that there is any uniform data, + // then we need to allocate a constant buffer to hold that data. + // + // TODO: Do we need to allocate a shadow copy for use from + // the CPU? + // + // TODO: When/where do we bind this constant buffer into + // a descriptor set for later use? + // + size_t uniformSize = layout->getElementTypeLayout()->getSize(); + if(uniformSize) + { + BufferResource::Desc bufferDesc; + bufferDesc.init(uniformSize); + bufferDesc.cpuAccessFlags |= Resource::AccessFlag::Write; + SLANG_RETURN_ON_FAIL(renderer->createBufferResource(Resource::Usage::ConstantBuffer, bufferDesc, nullptr, m_buffer.writeRef())); + } + +#if 0 + // If the layout tells us there are any descriptor sets to + // allocate, then we do so now. + // + for(auto descriptorSetInfo : layout->getDescriptorSets()) + { + RefPtr descriptorSet; + SLANG_RETURN_ON_FAIL(renderer->createDescriptorSet(descriptorSetInfo->layout, descriptorSet.writeRef())); + m_descriptorSets.add(descriptorSet); + } +#endif + + m_resourceViews.setCount(layout->getResourceViewCount()); + m_samplers.setCount(layout->getSamplerCount()); + m_combinedTextureSamplers.setCount(layout->getCombinedTextureSamplerCount()); + + // If the layout specifies that we have any sub-objects, then + // we need to size the array to account for them. + // + Index subObjectCount = layout->getSubObjectCount(); + m_objects.setCount(subObjectCount); + + for(auto subObjectRangeInfo : layout->getSubObjectRanges()) + { + RefPtr subObjectLayout = subObjectRangeInfo.layout; + + // In the case where the sub-object range represents an + // existential-type leaf field (e.g., an `IBar`), we + // cannot pre-allocate the objet(s) to go into that + // range, since we can't possibly know what to allocate + // at this point. + // + if(!subObjectLayout) + continue; + // + // Otherwise, we will allocate a sub-object to fill + // in each entry in this range, based on the layout + // information we already have. + + auto& bindingRangeInfo = layout->getBindingRange(subObjectRangeInfo.bindingRangeIndex); + for(Index i = 0; i < bindingRangeInfo.count; ++i) + { + RefPtr subObject; + SLANG_RETURN_ON_FAIL(ShaderObject::create(renderer, subObjectLayout, subObject.writeRef())); + m_objects[bindingRangeInfo.baseIndex + i] = subObject; + } + } + + return SLANG_OK; + } + + Result apply(Renderer* renderer, PipelineType pipelineType, PipelineLayout* pipelineLayout, Index& ioRootIndex) + { + ShaderObjectLayout* layout = m_layout; + + // Create the descritpor sets required by the layout... + // + List> descriptorSets; + for(auto descriptorSetInfo : layout->getDescriptorSets()) + { + RefPtr descriptorSet; + SLANG_RETURN_ON_FAIL(renderer->createDescriptorSet(descriptorSetInfo->layout, descriptorSet.writeRef())); + descriptorSets.add(descriptorSet); + } + + SLANG_RETURN_ON_FAIL(_bindIntoDescriptorSets(descriptorSets.getBuffer())); + + for(auto descriptorSet : descriptorSets) + { + renderer->setDescriptorSet(pipelineType, pipelineLayout, ioRootIndex++, descriptorSet); + } + + return SLANG_OK; + } + + Result _bindIntoDescriptorSet(DescriptorSet* descriptorSet, Index baseRangeIndex, Index subObjectRangeArrayIndex) + { + ShaderObjectLayout* layout = m_layout; + + if(m_buffer) + { + descriptorSet->setConstantBuffer(baseRangeIndex, subObjectRangeArrayIndex, m_buffer); + baseRangeIndex++; + } + + for(auto bindingRangeInfo : layout->getBindingRanges()) + { + switch(bindingRangeInfo.bindingType) + { + case slang::BindingType::VaryingInput: + case slang::BindingType::VaryingOutput: + continue; + + default: + break; + } + + SLANG_ASSERT(bindingRangeInfo.descriptorSetIndex == 0); + + auto count = bindingRangeInfo.count; + auto baseIndex = bindingRangeInfo.baseIndex; + + auto descriptorRangeIndex = baseRangeIndex + bindingRangeInfo.rangeIndexInDescriptorSet; + auto descriptorArrayBaseIndex = subObjectRangeArrayIndex * count; + + switch(bindingRangeInfo.bindingType) + { + case slang::BindingType::ConstantBuffer: + case slang::BindingType::ParameterBlock: + break; + + case slang::BindingType::ExistentialValue: + // + // TODO: If the existential value is one that "fits" into the storage available, + // then we should write its data directly into that area. Otherwise, we need + // to bind its content as "pending" data which will come after any other data + // beloning to the same set (that is, it's starting descriptorRangeIndex will + // need to be one after the number of ranges accounted for in the original type) + // + break; + + case slang::BindingType::CombinedTextureSampler: + for(Index i = 0; i < count; ++i) + { + auto& slot = m_combinedTextureSamplers[baseIndex + i]; + descriptorSet->setCombinedTextureSampler(descriptorRangeIndex, descriptorArrayBaseIndex + i, slot.textureView, slot.sampler); + } + break; + + case slang::BindingType::Sampler: + for(Index i = 0; i < count; ++i) + { + descriptorSet->setSampler(descriptorRangeIndex, descriptorArrayBaseIndex + i, m_samplers[baseIndex + i]); + } + break; + + default: + for(Index i = 0; i < count; ++i) + { + descriptorSet->setResource(descriptorRangeIndex, descriptorArrayBaseIndex + i, m_resourceViews[baseIndex + i]); + } + break; + } + } + + return SLANG_OK; + } + + public: + virtual Result _bindIntoDescriptorSets(RefPtr* descriptorSets) + { + ShaderObjectLayout* layout = m_layout; + + if(m_buffer) + { + // TODO: look up binding infor for default constant buffer... + descriptorSets[0]->setConstantBuffer(0, 0, m_buffer); + } + + // Fill in the descriptor sets based on binding ranges + // + for(auto bindingRangeInfo : layout->getBindingRanges()) + { + DescriptorSet* descriptorSet = descriptorSets[bindingRangeInfo.descriptorSetIndex]; + auto rangeIndex = bindingRangeInfo.rangeIndexInDescriptorSet; + auto baseIndex = bindingRangeInfo.baseIndex; + auto count = bindingRangeInfo.count; + switch(bindingRangeInfo.bindingType) + { + case slang::BindingType::ConstantBuffer: + case slang::BindingType::ParameterBlock: + for(Index i = 0; i < count; ++i) + { + ShaderObject* subObject = m_objects[baseIndex + i]; + + subObject->_bindIntoDescriptorSet(descriptorSet, rangeIndex, i); + } + break; + + case slang::BindingType::CombinedTextureSampler: + for(Index i = 0; i < count; ++i) + { + auto& slot = m_combinedTextureSamplers[baseIndex + i]; + descriptorSet->setCombinedTextureSampler(rangeIndex, i, slot.textureView, slot.sampler); + } + break; + + case slang::BindingType::Sampler: + for(Index i = 0; i < count; ++i) + { + descriptorSet->setSampler(rangeIndex, i, m_samplers[baseIndex + i]); + } + break; + + case slang::BindingType::VaryingInput: + case slang::BindingType::VaryingOutput: + break; + + case slang::BindingType::ExistentialValue: + // Here we are binding as if existential value is the same + // as a constant buffer or parameter block, which will lead + // to incorrect results... + for(Index i = 0; i < count; ++i) + { + ShaderObject* subObject = m_objects[baseIndex + i]; + + subObject->_bindIntoDescriptorSet(descriptorSet, rangeIndex, i); + } + break; + + default: + for(Index i = 0; i < count; ++i) + { + descriptorSet->setResource(rangeIndex, i, m_resourceViews[baseIndex + i]); + } + break; + } + } + return SLANG_OK; + } + + + RefPtr m_layout = nullptr; + RefPtr m_buffer; + + List> m_resourceViews; + + List> m_samplers; + + struct CombinedTextureSamplerSlot + { + RefPtr textureView; + RefPtr sampler; + }; + List m_combinedTextureSamplers; + +// List> m_descriptorSets; + List> m_objects; +}; + +class EntryPointVars : public ShaderObject +{ + typedef ShaderObject Super; + +public: + static Result create(Renderer* renderer, EntryPointLayout* layout, EntryPointVars** outShaderObject) + { + RefPtr object = new EntryPointVars(); + SLANG_RETURN_ON_FAIL(object->init(renderer, layout)); + + *outShaderObject = object.detach(); + return SLANG_OK; + } + + EntryPointLayout* getLayout() + { + return static_cast(m_layout.Ptr()); + } + +protected: + Result init(Renderer* renderer, EntryPointLayout* layout) + { + SLANG_RETURN_ON_FAIL(Super::init(renderer, layout)); + return SLANG_OK; + } +}; + +class ProgramVars : public ShaderObject +{ + typedef ShaderObject Super; + +public: + static Result create(Renderer* renderer, ProgramLayout* layout, ProgramVars** outShaderObject) + { + RefPtr object = new ProgramVars(); + SLANG_RETURN_ON_FAIL(object->init(renderer, layout)); + + *outShaderObject = object.detach(); + return SLANG_OK; + } + + ProgramLayout* getLayout() + { + return static_cast(m_layout.Ptr()); + } + + void apply(Renderer* renderer, PipelineType pipelineType) + { + auto pipelineLayout = getLayout()->getPipelineLayout(); + + Index rootIndex = 0; + ShaderObject::apply(renderer, pipelineType, pipelineLayout, rootIndex); + +#if 0 + + Index descriptorSetCount = m_descriptorSets.getCount(); + for(Index descriptorSetIndex = 0; descriptorSetIndex < descriptorSetCount; ++descriptorSetIndex) + { + renderer->setDescriptorSet( + pipelineType, + pipelineLayout, + descriptorSetIndex, + m_descriptorSets[descriptorSetIndex]); + } +#endif + + // TODO: We also need to bind any descriptor sets that are + // part of sub-objects of this object. + } + + List> const& getEntryPoints() const { return m_entryPoints; } + +protected: + + virtual Result _bindIntoDescriptorSets(RefPtr* descriptorSets) + { + SLANG_RETURN_ON_FAIL(Super::_bindIntoDescriptorSets(descriptorSets)); + + auto entryPointCount = m_entryPoints.getCount(); + for( Index i = 0; i < entryPointCount; ++i ) + { + auto entryPoint = m_entryPoints[i]; + auto& entryPointInfo = getLayout()->getEntryPoint(i); + + SLANG_RETURN_ON_FAIL(entryPoint->_bindIntoDescriptorSet(descriptorSets[0], entryPointInfo.rangeOffset, 0)); + } + + return SLANG_OK; + } + + + Result init(Renderer* renderer, ProgramLayout* layout) + { + SLANG_RETURN_ON_FAIL(Super::init(renderer, layout)); + + for(auto entryPointInfo : layout->getEntryPoints()) + { + RefPtr entryPoint; + SLANG_RETURN_ON_FAIL(EntryPointVars::create(renderer, entryPointInfo.layout, entryPoint.writeRef())); + m_entryPoints.add(entryPoint); + } + + return SLANG_OK; + } + + List> m_entryPoints; +}; + + /// Represents a "pointer" to the storage for a shader parameter of a (dynamically) known type. + /// + /// A `ShaderCursor` serves as a pointer-like type for things stored inside a `ShaderObject`. + /// + /// A cursor that points to the entire content of a shader object can be formed as `ShaderCursor(someObject)`. + /// A cursor pointing to a structure field or array element can be formed from another cursor + /// using `getField` or `getElement` respectively. + /// + /// Given a cursor pointing to a value of some "primitive" type, we can set or get the value + /// using operations like `setResource`, `getResource`, etc. + /// + /// Because type information for shader parameters is being reflected dynamically, all type + /// checking for shader cursors occurs at runtime, and errors may occur when attempting to + /// set a parameter using a value of an inappropriate type. As much as possible, `ShaderCursor` + /// attempts to protect against these cases and return an error `Result` or an invalid + /// cursor, rather than allowing operations to proceed with incorrect types. + /// +struct ShaderCursor +{ + ShaderObject* m_baseObject = nullptr; + slang::TypeLayoutReflection* m_typeLayout = nullptr; + ShaderOffset m_offset; + + /// Get the type (layout) of the value being pointed at by the cursor + slang::TypeLayoutReflection* getTypeLayout() const + { + return m_typeLayout; + } + + /// Is this cursor valid (that is, does it seem to point to an actual location)? + /// + /// This check is equivalent to checking whether a pointer is null, so it is + /// a very weak sense of "valid." In particular, it is possible to form a + /// `ShaderCursor` for which `isValid()` is true, but attempting to get or + /// set the value would be an error (like dereferencing a garbage pointer). + /// + bool isValid() const + { + return m_baseObject != nullptr; + } + + Result getDereferenced(ShaderCursor& outCursor) const + { + switch(m_typeLayout->getKind()) + { + default: + return SLANG_E_INVALID_ARG; + + case slang::TypeReflection::Kind::ConstantBuffer: + case slang::TypeReflection::Kind::ParameterBlock: + { + ShaderObject* subObject = m_baseObject->getObject(m_offset); + outCursor = ShaderCursor(subObject); + return SLANG_OK; + } + + } + } + + ShaderCursor getDereferenced() + { + ShaderCursor result; + getDereferenced(result); + return result; + } + + /// Form a cursor pointing to the field with the given `name` within the value this cursor points at. + /// + /// If the operation succeeds, then the field cursor is written to `outCursor`. + Result getField(UnownedStringSlice const& name, ShaderCursor& outCursor) + { + // If this cursor is invalid, then can't possible fetch a field. + // + if(!isValid()) return SLANG_E_INVALID_ARG; + + // If the cursor is valid, we want to consider the type of data + // it is referencing. + // + switch(m_typeLayout->getKind()) + { + // The easy/expected case is when the value has a structure type. + // + case slang::TypeReflection::Kind::Struct: + { + // We start by looking up the index of a field matching `name`. + // + // If there is no such field, we have an error. + // + SlangInt fieldIndex = m_typeLayout->findFieldIndexByName(name.begin(), name.end()); + if(fieldIndex == -1) + return SLANG_E_INVALID_ARG; + + // Once we know the index of the field being referenced, + // we create a cursor to point at the field, based on + // the offset information already in this cursor, plus + // offsets derived from the field's layout. + // + slang::VariableLayoutReflection* fieldLayout = m_typeLayout->getFieldByIndex((unsigned int) fieldIndex); + ShaderCursor fieldCursor; + + // The field cursorwill point into the same parent object. + // + fieldCursor.m_baseObject = m_baseObject; + + // The type being pointed to is the tyep of the field. + // + fieldCursor.m_typeLayout = fieldLayout->getTypeLayout(); + + // The byte offset is the current offset plus the relative offset of the field. + // The offset in binding ranges is computed similarly. + // + fieldCursor.m_offset.uniformOffset = m_offset.uniformOffset + fieldLayout->getOffset(); + fieldCursor.m_offset.bindingRangeIndex = m_offset.bindingRangeIndex + m_typeLayout->getFieldBindingRangeOffset(fieldIndex); + + // The index of the field within any binding ranges will be the same + // as the index computed for the parent structure. + // + // Note: this case would arise for an array of structures with texture-type + // fields. Suppose we have: + // + // struct S { Texture2D t; Texture2D u; } + // S g[4]; + // + // In this scenario, `g` holds two binding ranges: + // + // * Range #0 comprises 4 textures, representing `g[...].t` + // * Range #1 comprises 4 textures, representing `g[...].u` + // + // A cursor for `g[2]` would have a `bindingRangeIndex` of zero but + // a `bindingArrayIndex` of 2, iindicating that we could end up + // referencing either range, but no matter what we know the index + // is 2. Thus when we form a cursor for `g[2].u` we want to + // apply the binding range offset to get a `bindingRangeIndex` of + // 1, while the `bindingArrayIndex` is unmodified. + // + // The result is that `g[2].u` is stored in range #1 at array index 2. + // + fieldCursor.m_offset.bindingArrayIndex = m_offset.bindingArrayIndex; + + outCursor = fieldCursor; + return SLANG_OK; + } + break; + + // In some cases the user might be trying to acess a field by name + // from a cursor that references a constant buffer or parameter block, + // and in these cases we want the access to Just Work. + // + case slang::TypeReflection::Kind::ConstantBuffer: + case slang::TypeReflection::Kind::ParameterBlock: + { + // We basically need to "dereference" the current cursor + // to go from a pointer to a constant buffer to a pointer + // to the *contents* of the constant buffer. + // + ShaderCursor d = getDereferenced(); + return d.getField(name, outCursor); + } + break; + } + + return SLANG_E_INVALID_ARG; + } + + ShaderCursor getField(UnownedStringSlice const& name) + { + ShaderCursor cursor; + getField(name, cursor); + return cursor; + } + + ShaderCursor getField(String const& name) + { + return getField(name.getUnownedSlice()); + } + + ShaderCursor getElement(Index index) + { + // TODO: need to auto-dereference various buffer types... + + if(m_typeLayout->getKind() == slang::TypeReflection::Kind::Array) + { + ShaderCursor elementCursor; + elementCursor.m_baseObject = m_baseObject; + elementCursor.m_typeLayout = m_typeLayout->getElementTypeLayout(); + elementCursor.m_offset.uniformOffset = m_offset.uniformOffset + index * m_typeLayout->getElementStride(SLANG_PARAMETER_CATEGORY_UNIFORM); + elementCursor.m_offset.bindingRangeIndex = m_offset.bindingRangeIndex; + elementCursor.m_offset.bindingArrayIndex = m_offset.bindingArrayIndex*m_typeLayout->getElementCount() + index; + return elementCursor; + } + + return ShaderCursor(); + } + + static int _peek(UnownedStringSlice const& slice) + { + const char* b = slice.begin(); + const char* e = slice.end(); + if(b == e) return -1; + return *b; + } + + static int _get(UnownedStringSlice& slice) + { + const char* b = slice.begin(); + const char* e = slice.end(); + if(b == e) return -1; + auto result = *b++; + slice = UnownedStringSlice(b, e); + return result; + } + + static Result followPath(UnownedStringSlice const& path, ShaderCursor& ioCursor) + { + ShaderCursor cursor = ioCursor; + + enum + { + ALLOW_NAME = 0x1, + ALLOW_SUBSCRIPT = 0x2, + ALLOW_DOT = 0x4, + }; + int state = ALLOW_NAME | ALLOW_SUBSCRIPT; + + UnownedStringSlice rest = path; + for(;;) + { + int c = _peek(rest); + + if(c == -1) + break; + else if( c == '.' ) + { + if(!(state & ALLOW_DOT)) + return SLANG_E_INVALID_ARG; + + _get(rest); + state = ALLOW_NAME; + continue; + } + else if( c == '[' ) + { + if(!(state & ALLOW_SUBSCRIPT)) + return SLANG_E_INVALID_ARG; + + _get(rest); + Index index = 0; + while(_peek(rest) != ']') + { + int d = _get(rest); + if(d >= '0' && d <= '9') + { + index = index*10 + (d - '0'); + } + else + { + return SLANG_E_INVALID_ARG; + } + } + + if(_peek(rest) != ']') + return SLANG_E_INVALID_ARG; + _get(rest); - // WindowListener - virtual Result update(Window* window) SLANG_OVERRIDE; + cursor = cursor.getElement(index); + state = ALLOW_DOT | ALLOW_SUBSCRIPT; + continue; + } + else + { + const char* nameBegin = rest.begin(); + for(;;) + { + switch(_peek(rest)) + { + default: + _get(rest); + continue; + + case -1: + case '.': + case '[': + break; + } + break; + } + char const* nameEnd = rest.begin(); + UnownedStringSlice name(nameBegin, nameEnd); + cursor = cursor.getField(name); + state = ALLOW_DOT | ALLOW_SUBSCRIPT; + continue; + } + } - // At initialization time, we are going to load and compile our Slang shader - // code, and then create the API objects we need for rendering. - Result initialize(SlangSession* session, Renderer* renderer, const Options& options, const ShaderCompilerUtil::Input& input); - void runCompute(); - void renderFrame(); - void finalize(); + ioCursor = cursor; + return SLANG_OK; + } - BindingStateImpl* getBindingState() const { return m_bindingState; } + static Result followPath(String const& path, ShaderCursor& ioCursor) + { + return followPath(path.getUnownedSlice(), ioCursor); + } - Result writeBindingOutput(BindRoot* bindRoot, const char* fileName); + ShaderCursor getPath(UnownedStringSlice const& path) + { + ShaderCursor result(*this); + followPath(path, result); + return result; + } - Result writeScreen(const char* filename); - protected: - /// Called in initialize - Result _initializeShaders(SlangSession* session, Renderer* renderer, Options::ShaderProgramType shaderType, const ShaderCompilerUtil::Input& input); + ShaderCursor getPath(String const& path) + { + ShaderCursor result(*this); + followPath(path, result); + return result; + } - uint64_t m_startTicks; + ShaderCursor() + {} - // variables for state to be used for rendering... - uintptr_t m_constantBufferSize; + ShaderCursor(ShaderObject* object) + : m_baseObject(object) + , m_typeLayout(object->getElementTypeLayout()) + {} - RefPtr m_renderer; + SlangResult setData(void const* data, size_t size) + { + return m_baseObject->setData(m_offset, data, size); + } - RefPtr m_constantBuffer; - RefPtr m_inputLayout; - RefPtr m_vertexBuffer; - RefPtr m_shaderProgram; - RefPtr m_pipelineState; - RefPtr m_bindingState; + SlangResult setObject(ShaderObject* object) + { + return m_baseObject->setObject(m_offset, object); + } - ShaderCompilerUtil::OutputAndLayout m_compilationOutput; + SlangResult setResource(ResourceView* resourceView) + { + return m_baseObject->setResource(m_offset, resourceView); + } - ShaderInputLayout m_shaderInputLayout; ///< The binding layout - int m_numAddedConstantBuffers; ///< Constant buffers can be added to the binding directly. Will be added at the end. + SlangResult setSampler(SamplerState* sampler) + { + return m_baseObject->setSampler(m_offset, sampler); + } - Options m_options; + SlangResult setCombinedTextureSampler(ResourceView* textureView, SamplerState* sampler) + { + return m_baseObject->setCombinedTextureSampler(m_offset, textureView, sampler); + } }; -SlangResult RenderTestApp::initialize(SlangSession* session, Renderer* renderer, const Options& options, const ShaderCompilerUtil::Input& input) +SlangResult _assignVarsFromLayout( + Renderer* renderer, + ProgramVars* shaderObject, + ShaderInputLayout const& layout, + ShaderOutputPlan& ioOutputPlan, + slang::ProgramLayout* slangReflection) +{ + ShaderCursor rootCursor = ShaderCursor(shaderObject); + + const int textureBindFlags = Resource::BindFlag::NonPixelShaderResource | Resource::BindFlag::PixelShaderResource; + + Index entryCount = layout.entries.getCount(); + for(Index entryIndex = 0; entryIndex < entryCount; ++entryIndex) + { + auto& entry = layout.entries[entryIndex]; + if(entry.name.getLength() == 0) + { + StdWriters::getError().print("error: entries in `ShaderInputLayout` must include a name\n"); + return SLANG_E_INVALID_ARG; + } + + auto entryCursor = rootCursor.getPath(entry.name); + + if(!entryCursor.isValid()) + { + for(auto entryPoint : shaderObject->getEntryPoints()) + { + entryCursor = ShaderCursor(entryPoint).getPath(entry.name); + if(entryCursor.isValid()) + break; + } + } + + + if(!entryCursor.isValid()) + { + StdWriters::getError().print("error: could not find shader parameter matching '%s'\n", entry.name.begin()); + return SLANG_E_INVALID_ARG; + } + + RefPtr resource; + switch(entry.type) + { + case ShaderInputType::Uniform: + { + const size_t bufferSize = entry.bufferData.getCount() * sizeof(uint32_t); + + ShaderCursor dataCursor = entryCursor; + switch(dataCursor.getTypeLayout()->getKind()) + { + case slang::TypeReflection::Kind::ConstantBuffer: + case slang::TypeReflection::Kind::ParameterBlock: + dataCursor = dataCursor.getDereferenced(); + break; + + default: + break; + + } + + dataCursor.setData(entry.bufferData.getBuffer(), bufferSize); + } + break; + + case ShaderInputType::Buffer: + { + const InputBufferDesc& srcBuffer = entry.bufferDesc; + const size_t bufferSize = entry.bufferData.getCount() * sizeof(uint32_t); + + 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: + { + + RefPtr bufferResource; + SLANG_RETURN_ON_FAIL(ShaderRendererUtil::createBufferResource(entry.bufferDesc, entry.isOutput, bufferSize, entry.bufferData.getBuffer(), renderer, bufferResource)); + resource = bufferResource; + + ResourceView::Desc viewDesc; + viewDesc.type = ResourceView::Type::UnorderedAccess; + viewDesc.format = srcBuffer.format; + auto bufferView = renderer->createBufferView( + bufferResource, + viewDesc); + entryCursor.setResource(bufferView); + } + break; + } + +#if 0 + switch(srcBuffer.type) + { + case InputBufferType::ConstantBuffer: + descriptorSet->setConstantBuffer(rangeIndex, 0, bufferResource); + break; + + case InputBufferType::StorageBuffer: + { + ResourceView::Desc viewDesc; + viewDesc.type = ResourceView::Type::UnorderedAccess; + viewDesc.format = srcBuffer.format; + auto bufferView = renderer->createBufferView( + bufferResource, + viewDesc); + descriptorSet->setResource(rangeIndex, 0, bufferView); + } + break; + } + + if(srcEntry.isOutput) + { + BindingStateImpl::OutputBinding binding; + binding.entryIndex = i; + binding.resource = bufferResource; + outputBindings.add(binding); + } +#endif + } + break; + + case ShaderInputType::CombinedTextureSampler: + { + RefPtr texture; + SLANG_RETURN_ON_FAIL(ShaderRendererUtil::generateTextureResource(entry.textureDesc, textureBindFlags, renderer, texture)); + resource = texture; + + auto sampler = _createSamplerState(renderer, entry.samplerDesc); + + ResourceView::Desc viewDesc; + viewDesc.type = ResourceView::Type::ShaderResource; + auto textureView = renderer->createTextureView( + texture, + viewDesc); + + entryCursor.setCombinedTextureSampler(textureView, sampler); + +#if 0 + descriptorSet->setCombinedTextureSampler(rangeIndex, 0, textureView, sampler); + + if(srcEntry.isOutput) + { + BindingStateImpl::OutputBinding binding; + binding.entryIndex = i; + binding.resource = texture; + outputBindings.add(binding); + } +#endif + } + break; + + case ShaderInputType::Texture: + { + RefPtr texture; + SLANG_RETURN_ON_FAIL(ShaderRendererUtil::generateTextureResource(entry.textureDesc, textureBindFlags, renderer, texture)); + resource = texture; + + // TODO: support UAV textures... + + ResourceView::Desc viewDesc; + viewDesc.type = ResourceView::Type::ShaderResource; + auto textureView = renderer->createTextureView( + texture, + viewDesc); + + if (!textureView) + { + return SLANG_FAIL; + } + + entryCursor.setResource(textureView); + +#if 0 + descriptorSet->setResource(rangeIndex, 0, textureView); + + if(srcEntry.isOutput) + { + BindingStateImpl::OutputBinding binding; + binding.entryIndex = i; + binding.resource = texture; + outputBindings.add(binding); + } +#endif + } + break; + + case ShaderInputType::Sampler: + { + auto sampler = _createSamplerState(renderer, entry.samplerDesc); + + entryCursor.setSampler(sampler); +#if 0 + descriptorSet->setSampler(rangeIndex, 0, sampler); +#endif + } + break; + + case ShaderInputType::Object: + { + auto typeName = entry.objectDesc.typeName; + auto slangType = slangReflection->findTypeByName(typeName.getBuffer()); + auto slangTypeLayout = slangReflection->getTypeLayout(slangType); + + RefPtr shaderObjectLayout; + SLANG_RETURN_ON_FAIL(ShaderObjectLayout::createForElementType(renderer, slangTypeLayout, shaderObjectLayout.writeRef())); + + RefPtr shaderObject; + SLANG_RETURN_ON_FAIL(ShaderObject::create(renderer, shaderObjectLayout, shaderObject.writeRef())); + + entryCursor.setObject(shaderObject); + } + break; + + default: + assert(!"Unhandled type"); + return SLANG_FAIL; + } + + if(entry.isOutput) + { + ShaderOutputPlan::Item item; + item.inputLayoutEntryIndex = entryIndex; + item.resource = resource; + ioOutputPlan.items.add(item); + } + + } + return SLANG_OK; +} + +void LegacyRenderTestApp::applyBinding(PipelineType pipelineType) +{ + m_bindingState->apply(m_renderer.Ptr(), pipelineType); +} + +void ShaderObjectRenderTestApp::applyBinding(PipelineType pipelineType) +{ + m_programVars->apply(m_renderer.Ptr(), pipelineType); +} + +SlangResult LegacyRenderTestApp::initialize( + SlangSession* session, + Renderer* renderer, + const Options& options, + const ShaderCompilerUtil::Input& input) { m_options = options; SLANG_RETURN_ON_FAIL(_initializeShaders(session, renderer, options.shaderType, input)); m_numAddedConstantBuffers = 0; - m_renderer = renderer; + m_renderer = renderer; // TODO(tfoley): use each API's reflection interface to query the constant-buffer size needed m_constantBufferSize = 16 * sizeof(float); @@ -129,16 +1934,17 @@ SlangResult RenderTestApp::initialize(SlangSession* session, Renderer* renderer, constantBufferDesc.init(m_constantBufferSize); constantBufferDesc.cpuAccessFlags = Resource::AccessFlag::Write; - m_constantBuffer = renderer->createBufferResource(Resource::Usage::ConstantBuffer, constantBufferDesc); + m_constantBuffer = + renderer->createBufferResource(Resource::Usage::ConstantBuffer, constantBufferDesc); if (!m_constantBuffer) return SLANG_FAIL; //! Hack -> if doing a graphics test, add an extra binding for our dynamic constant buffer // - // TODO: Should probably be more sophisticated than this - with 'dynamic' constant buffer/s binding always being specified - // in the test file + // TODO: Should probably be more sophisticated than this - with 'dynamic' constant buffer/s + // binding always being specified in the test file RefPtr addedConstantBuffer; - switch(m_options.shaderType) + switch (m_options.shaderType) { default: break; @@ -151,7 +1957,8 @@ SlangResult RenderTestApp::initialize(SlangSession* session, Renderer* renderer, } BindingStateImpl* bindingState = nullptr; - SLANG_RETURN_ON_FAIL(ShaderRendererUtil::createBindingState(m_shaderInputLayout, m_renderer, addedConstantBuffer, &bindingState)); + SLANG_RETURN_ON_FAIL(ShaderRendererUtil::createBindingState( + m_shaderInputLayout, m_renderer, addedConstantBuffer, &bindingState)); m_bindingState = bindingState; // Do other initialization that doesn't depend on the source language. @@ -159,24 +1966,25 @@ SlangResult RenderTestApp::initialize(SlangSession* session, Renderer* renderer, // Input Assembler (IA) const InputElementDesc inputElements[] = { - { "A", 0, Format::RGB_Float32, offsetof(Vertex, position) }, - { "A", 1, Format::RGB_Float32, offsetof(Vertex, color) }, - { "A", 2, Format::RG_Float32, offsetof(Vertex, uv) }, + {"A", 0, Format::RGB_Float32, offsetof(Vertex, position)}, + {"A", 1, Format::RGB_Float32, offsetof(Vertex, color)}, + {"A", 2, Format::RG_Float32, offsetof(Vertex, uv)}, }; m_inputLayout = renderer->createInputLayout(inputElements, SLANG_COUNT_OF(inputElements)); - if(!m_inputLayout) + if (!m_inputLayout) return SLANG_FAIL; BufferResource::Desc vertexBufferDesc; vertexBufferDesc.init(kVertexCount * sizeof(Vertex)); - m_vertexBuffer = renderer->createBufferResource(Resource::Usage::VertexBuffer, vertexBufferDesc, kVertexData); - if(!m_vertexBuffer) + m_vertexBuffer = renderer->createBufferResource( + Resource::Usage::VertexBuffer, vertexBufferDesc, kVertexData); + if (!m_vertexBuffer) return SLANG_FAIL; { - switch(m_options.shaderType) + switch (m_options.shaderType) { default: assert(!"unexpected test shader type"); @@ -211,6 +2019,134 @@ SlangResult RenderTestApp::initialize(SlangSession* session, Renderer* renderer, return m_pipelineState ? SLANG_OK : SLANG_FAIL; } +SlangResult ShaderObjectRenderTestApp::initialize( + SlangSession* session, + Renderer* renderer, + const Options& options, + const ShaderCompilerUtil::Input& input) +{ + m_options = options; + + // We begin by compiling the shader file and entry points that specified via the options. + // + SLANG_RETURN_ON_FAIL(ShaderCompilerUtil::compileWithLayout(session, options, input, m_compilationOutput)); + m_shaderInputLayout = m_compilationOutput.layout; + + // Once the shaders have been compiled we load them via the underlying API. + // + SLANG_RETURN_ON_FAIL(renderer->createProgram(m_compilationOutput.output.desc, m_shaderProgram.writeRef())); + + // If we are doing a non-pass-through compilation, then we will rely on + // Slang's reflection API to tell us what the parameters of the program are. + // + RefPtr programLayout = nullptr; + + // Okay, we will use Slang reflection to determine what the parameters of the shader are. + // + auto slangReflection = (slang::ProgramLayout*) spGetReflection(m_compilationOutput.output.getRequestForReflection()); + { + ProgramLayout::Builder builder(renderer); + builder.addGlobalParams(slangReflection->getGlobalParamsVarLayout()); + + // TODO: Also need to reflect entry points here. + + SlangInt entryPointCount = slangReflection->getEntryPointCount(); + for(SlangInt e = 0; e < entryPointCount; ++e) + { + auto slangEntryPoint = slangReflection->getEntryPointByIndex(e); + + EntryPointLayout::Builder entryPointBuilder(renderer); + entryPointBuilder.addEntryPointParams(slangEntryPoint); + + RefPtr entryPointLayout; + SLANG_RETURN_ON_FAIL(entryPointBuilder.build(entryPointLayout.writeRef())); + + builder.addEntryPoint(entryPointLayout); + } + + SLANG_RETURN_ON_FAIL(builder.build(programLayout.writeRef())); + } + + // The shape of the parameters will determine the pipeline layout that we use. + // + RefPtr pipelineLayout = programLayout->getPipelineLayout(); + + // Once we have determined the layout of all the parameters we need to bind, + // we will create a shader object to use for storing and binding those parameters. + // + RefPtr programVars; + SLANG_RETURN_ON_FAIL(ProgramVars::create(renderer, programLayout, programVars.writeRef())); + m_programVars = programVars; + + // Now we need to assign from the input parameter data that was parsed into + // the program vars we allocated. + // + SLANG_RETURN_ON_FAIL(_assignVarsFromLayout(renderer, programVars, m_compilationOutput.layout, m_outputPlan, slangReflection)); + + m_renderer = renderer; + + // TODO(tfoley): use each API's reflection interface to query the constant-buffer size needed +// m_constantBufferSize = 16 * sizeof(float); + + { + switch(m_options.shaderType) + { + default: + assert(!"unexpected test shader type"); + return SLANG_FAIL; + + case Options::ShaderProgramType::Compute: + { + ComputePipelineStateDesc desc; + desc.pipelineLayout = pipelineLayout; + desc.program = m_shaderProgram; + + m_pipelineState = renderer->createComputePipelineState(desc); + } + break; + + case Options::ShaderProgramType::Graphics: + case Options::ShaderProgramType::GraphicsCompute: + { + // TODO: We should conceivably be able to match up the "available" vertex + // attributes, as defined by the vertex stream(s) on the model being + // renderer, with the "required" vertex attributes as defiend on the + // shader. + // + // For now we just create a fixed input layout for all graphics tests + // since at present they all draw the same single triangle with a + // fixed/known set of attributes. + // + const InputElementDesc inputElements[] = { + { "A", 0, Format::RGB_Float32, offsetof(Vertex, position) }, + { "A", 1, Format::RGB_Float32, offsetof(Vertex, color) }, + { "A", 2, Format::RG_Float32, offsetof(Vertex, uv) }, + }; + + RefPtr inputLayout; + SLANG_RETURN_ON_FAIL(renderer->createInputLayout(inputElements, SLANG_COUNT_OF(inputElements), inputLayout.writeRef())); + + BufferResource::Desc vertexBufferDesc; + vertexBufferDesc.init(kVertexCount * sizeof(Vertex)); + + SLANG_RETURN_ON_FAIL(renderer->createBufferResource(Resource::Usage::VertexBuffer, vertexBufferDesc, kVertexData, m_vertexBuffer.writeRef())); + + GraphicsPipelineStateDesc desc; + desc.pipelineLayout = pipelineLayout; + desc.program = m_shaderProgram; + desc.inputLayout = inputLayout; + desc.renderTargetCount = programLayout->getRenderTargetCount(); + + m_pipelineState = renderer->createGraphicsPipelineState(desc); + } + break; + } + } + + // If success must have a pipeline state + return m_pipelineState ? SLANG_OK : SLANG_FAIL; +} + Result RenderTestApp::_initializeShaders(SlangSession* session, Renderer* renderer, Options::ShaderProgramType shaderType, const ShaderCompilerUtil::Input& input) { SLANG_RETURN_ON_FAIL(ShaderCompilerUtil::compileWithLayout(session, m_options, input, m_compilationOutput)); @@ -219,16 +2155,36 @@ Result RenderTestApp::_initializeShaders(SlangSession* session, Renderer* render return m_shaderProgram ? SLANG_OK : SLANG_FAIL; } -void RenderTestApp::renderFrame() + +void LegacyRenderTestApp::setProjectionMatrix() { auto mappedData = m_renderer->map(m_constantBuffer, MapFlavor::WriteDiscard); - if(mappedData) + if (mappedData) { - const ProjectionStyle projectionStyle = RendererUtil::getProjectionStyle(m_renderer->getRendererType()); + const ProjectionStyle projectionStyle = + RendererUtil::getProjectionStyle(m_renderer->getRendererType()); RendererUtil::getIdentityProjection(projectionStyle, (float*)mappedData); - m_renderer->unmap(m_constantBuffer); + m_renderer->unmap(m_constantBuffer); } +} + +void ShaderObjectRenderTestApp::setProjectionMatrix() +{ + const ProjectionStyle projectionStyle = + RendererUtil::getProjectionStyle(m_renderer->getRendererType()); + + float projectionMatrix[16]; + RendererUtil::getIdentityProjection(projectionStyle, projectionMatrix); + ShaderCursor(m_programVars) + .getField("Uniforms") + .getDereferenced() + .setData(projectionMatrix, sizeof(projectionMatrix)); +} + +void RenderTestApp::renderFrame() +{ + setProjectionMatrix(); auto pipelineType = PipelineType::Graphics; @@ -237,7 +2193,7 @@ void RenderTestApp::renderFrame() m_renderer->setPrimitiveTopology(PrimitiveTopology::TriangleList); m_renderer->setVertexBuffer(0, m_vertexBuffer, sizeof(Vertex)); - m_bindingState->apply(m_renderer, pipelineType); + applyBinding(pipelineType); m_renderer->draw(3); } @@ -246,7 +2202,7 @@ void RenderTestApp::runCompute() { auto pipelineType = PipelineType::Compute; m_renderer->setPipelineState(pipelineType, m_pipelineState); - m_bindingState->apply(m_renderer, pipelineType); + applyBinding(pipelineType); m_startTicks = ProcessUtil::getClockTick(); @@ -257,7 +2213,7 @@ void RenderTestApp::finalize() { } -Result RenderTestApp::writeBindingOutput(BindRoot* bindRoot, const char* fileName) +Result LegacyRenderTestApp::writeBindingOutput(BindRoot* bindRoot, const char* fileName) { // Submit the work m_renderer->submitGpuWork(); @@ -304,6 +2260,51 @@ Result RenderTestApp::writeBindingOutput(BindRoot* bindRoot, const char* fileNam return SLANG_OK; } +Result ShaderObjectRenderTestApp::writeBindingOutput(BindRoot* bindRoot, const char* fileName) +{ + // Submit the work + m_renderer->submitGpuWork(); + // Wait until everything is complete + m_renderer->waitForGpu(); + + FILE * f = fopen(fileName, "wb"); + if (!f) + { + return SLANG_FAIL; + } + FileWriter writer(f, WriterFlags(0)); + + for(auto outputItem : m_outputPlan.items) + { + auto& inputEntry = m_shaderInputLayout.entries[outputItem.inputLayoutEntryIndex]; + assert(inputEntry.isOutput); + + auto resource = outputItem.resource; + if (resource && resource->isBuffer()) + { + BufferResource* bufferResource = static_cast(resource.Ptr()); + const size_t bufferSize = bufferResource->getDesc().sizeInBytes; + + unsigned int* ptr = (unsigned int*)m_renderer->map(bufferResource, MapFlavor::HostRead); + if (!ptr) + { + return SLANG_FAIL; + } + + const SlangResult res = ShaderInputLayout::writeBinding(bindRoot, inputEntry, ptr, bufferSize, &writer); + + m_renderer->unmap(bufferResource); + + SLANG_RETURN_ON_FAIL(res); + } + else + { + printf("invalid output type at %d.\n", int(outputItem.inputLayoutEntryIndex)); + } + } + return SLANG_OK; +} + Result RenderTestApp::writeScreen(const char* filename) { @@ -338,6 +2339,7 @@ Result RenderTestApp::update(Window* window) if (m_options.performanceProfile) { +#if 0 // It might not be enough on some APIs to 'waitForGpu' to mean the computation has completed. Let's lock an output // buffer to be sure if (m_bindingState->outputBindings.getCount() > 0) @@ -360,6 +2362,7 @@ Result RenderTestApp::update(Window* window) m_renderer->unmap(bufferResource); } } +#endif // Note we don't do the same with screen rendering -> as that will do a lot of work, which may swamp any computation // so can only really profile compute shaders at the moment @@ -373,7 +2376,7 @@ Result RenderTestApp::update(Window* window) { if (m_options.shaderType == Options::ShaderProgramType::Compute || m_options.shaderType == Options::ShaderProgramType::GraphicsCompute) { - auto request = m_compilationOutput.output.request; + auto request = m_compilationOutput.output.getRequestForReflection(); auto slangReflection = (slang::ShaderReflection*) spGetReflection(request); BindSet bindSet; @@ -629,7 +2632,7 @@ static SlangResult _innerMain(Slang::StdWriters* stdWriters, SlangSession* sessi { // Get the shared library -> it contains the executable code, we need to keep around if we recompile ComPtr sharedLibrary; - SLANG_RETURN_ON_FAIL(spGetEntryPointHostCallable(compilationAndLayout.output.request, 0, 0, sharedLibrary.writeRef())); + SLANG_RETURN_ON_FAIL(spGetEntryPointHostCallable(compilationAndLayout.output.getRequestForKernels(), 0, 0, sharedLibrary.writeRef())); // This is a hack to work around, reflection when compiling straight C/C++ code. In that case the code is just passed // straight through to the C++ compiler so no reflection. In these tests though we should have conditional code @@ -780,7 +2783,11 @@ static SlangResult _innerMain(Slang::StdWriters* stdWriters, SlangSession* sessi } { - RefPtr app(new RenderTestApp); + RefPtr app; + if (options.useShaderObjects) + app = new ShaderObjectRenderTestApp(); + else + app = new LegacyRenderTestApp(); SLANG_RETURN_ON_FAIL(app->initialize(session, renderer, options, input)); window->show(); return window->runLoop(app); diff --git a/tools/render-test/shader-input-layout.cpp b/tools/render-test/shader-input-layout.cpp index 6c6f67c86..5f43b254d 100644 --- a/tools/render-test/shader-input-layout.cpp +++ b/tools/render-test/shader-input-layout.cpp @@ -140,11 +140,19 @@ namespace renderer_test 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; @@ -226,6 +234,10 @@ namespace renderer_test numRenderTargets = parser.ReadInt(); continue; } + else + { + throw TextFormatException(String("Invalid input syntax at line ") + String(parser.NextToken().Position.Line)); + } parser.ReadToken(); // parse options if (parser.LookAhead("(")) @@ -510,6 +522,11 @@ namespace renderer_test parser.Read("="); entry.textureDesc.mipMapCount = int(parser.ReadInt()); } + else if( word == "type" ) + { + parser.Read("="); + entry.objectDesc.typeName = parser.ReadWord(); + } if (parser.LookAhead(",")) parser.Read(","); diff --git a/tools/render-test/shader-input-layout.h b/tools/render-test/shader-input-layout.h index 97796d7f6..437da820b 100644 --- a/tools/render-test/shader-input-layout.h +++ b/tools/render-test/shader-input-layout.h @@ -17,7 +17,8 @@ using namespace gfx; enum class ShaderInputType { - Buffer, Texture, Sampler, CombinedTextureSampler, Array + Buffer, Texture, Sampler, CombinedTextureSampler, Array, Uniform, + Object, }; enum class InputTextureContent @@ -81,6 +82,11 @@ struct BindlessHandleDataEntry Slang::String name; }; +struct InputObjectDesc +{ + Slang::String typeName; +}; + class ShaderInputLayoutEntry { public: @@ -92,6 +98,7 @@ public: 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. diff --git a/tools/render-test/shader-renderer-util.cpp b/tools/render-test/shader-renderer-util.cpp index b2911ffd5..5e77cf1fd 100644 --- a/tools/render-test/shader-renderer-util.cpp +++ b/tools/render-test/shader-renderer-util.cpp @@ -149,7 +149,7 @@ static SamplerState::Desc _calcSamplerDesc(const InputSamplerDesc& srcDesc) return dstDesc; } -static RefPtr _createSamplerState( +RefPtr _createSamplerState( Renderer* renderer, const InputSamplerDesc& srcDesc) { diff --git a/tools/render-test/shader-renderer-util.h b/tools/render-test/shader-renderer-util.h index 872f21768..76f3b6d87 100644 --- a/tools/render-test/shader-renderer-util.h +++ b/tools/render-test/shader-renderer-util.h @@ -47,6 +47,10 @@ struct BindingStateImpl : public Slang::RefObject int m_numRenderTargets = 1; }; +RefPtr _createSamplerState( + Renderer* renderer, + const InputSamplerDesc& srcDesc); + /// Utility class containing functions that construct items on the renderer using the ShaderInputLayout representation struct ShaderRendererUtil { diff --git a/tools/render-test/slang-support.cpp b/tools/render-test/slang-support.cpp index 78c730cab..baa745b14 100644 --- a/tools/render-test/slang-support.cpp +++ b/tools/render-test/slang-support.cpp @@ -20,7 +20,7 @@ static const char fragmentEntryPointName[] = "fragmentMain"; static const char computeEntryPointName[] = "computeMain"; static const char rtEntryPointName[] = "raygenMain"; -static gfx::StageType _translateStage(SlangStage slangStage) +gfx::StageType translateStage(SlangStage slangStage) { switch(slangStage) { @@ -50,12 +50,12 @@ static gfx::StageType _translateStage(SlangStage slangStage) } } -/* static */ SlangResult ShaderCompilerUtil::compileProgram(SlangSession* session, const Options& options, const Input& input, const ShaderCompileRequest& request, Output& out) +/* static */ SlangResult ShaderCompilerUtil::_compileProgramImpl(SlangSession* session, const Options& options, const Input& input, const ShaderCompileRequest& request, Output& out) { out.reset(); SlangCompileRequest* slangRequest = spCreateCompileRequest(session); - out.request = slangRequest; + out.m_requestForKernels = slangRequest; out.session = session; // Parse all the extra args @@ -215,7 +215,7 @@ static gfx::StageType _translateStage(SlangStage slangStage) size_t codeSize = 0; char const* code = (char const*) spGetEntryPointCode(slangRequest, int(ee), &codeSize); - auto gfxStage = _translateStage(actualEntryPoint.slangStage); + auto gfxStage = translateStage(actualEntryPoint.slangStage); ShaderProgram::KernelDesc kernelDesc; kernelDesc.stage = gfxStage; @@ -231,6 +231,58 @@ static gfx::StageType _translateStage(SlangStage slangStage) return SLANG_OK; } +/* static */ SlangResult ShaderCompilerUtil::compileProgram(SlangSession* session, const Options& options, const Input& input, const ShaderCompileRequest& request, Output& out) +{ + if( input.passThrough == SLANG_PASS_THROUGH_NONE ) + { + return _compileProgramImpl(session, options, input, request, out); + } + else + { + bool canUseSlangForPrecompile = false; + switch (input.passThrough) + { + case SLANG_PASS_THROUGH_DXC: + case SLANG_PASS_THROUGH_FXC: + canUseSlangForPrecompile = true; + break; + default: + break; + } + // If we are doing a HLSL pass-through compilation, then we can't rely + // on the downstream compiler for the reflection information that + // will drive all of our parameter binding. As such, we will first + // compile with Slang to get reflection information, and then + // compile in another pass using the desired downstream compiler + // so that we can get the refleciton information we need. + // + Output slangOutput; + if (canUseSlangForPrecompile) + { + ShaderCompilerUtil::Input slangInput = input; + slangInput.sourceLanguage = SLANG_SOURCE_LANGUAGE_SLANG; + slangInput.passThrough = SLANG_PASS_THROUGH_NONE; + // TODO: we want to pass along a flag to skip codegen... + + + SLANG_RETURN_ON_FAIL(_compileProgramImpl(session, options, slangInput, request, slangOutput)); + } + + // Now we have what we need to be able to do the downstream compile better. + // + // TODO: We should be able to use the output from the Slang compilation + // to fill in the actual entry points to be used for this compilation, + // so that discovery of entry points via `[shader(...)]` attributes will work. + // + SLANG_RETURN_ON_FAIL(_compileProgramImpl(session, options, input, request, out)); + + out.m_extraRequestForReflection = slangOutput.getRequestForReflection(); + slangOutput.m_requestForKernels = nullptr; + + return SLANG_OK; + } +} + /* static */SlangResult ShaderCompilerUtil::readSource(const String& inSourcePath, List& outSourceText) { // Read in the source code diff --git a/tools/render-test/slang-support.h b/tools/render-test/slang-support.h index 5e38c8c69..a0a7ef276 100644 --- a/tools/render-test/slang-support.h +++ b/tools/render-test/slang-support.h @@ -10,6 +10,8 @@ namespace renderer_test { +gfx::StageType translateStage(SlangStage slangStage); + struct ShaderCompilerUtil { struct Input @@ -42,19 +44,21 @@ struct ShaderCompilerUtil } kernelDescs.clear(); - if (request && session) + if (m_requestForKernels && session) + { + spDestroyCompileRequest(m_requestForKernels); + } + if (m_extraRequestForReflection && session) { - spDestroyCompileRequest(request); + spDestroyCompileRequest(m_extraRequestForReflection); } session = nullptr; - request = nullptr; + m_requestForKernels = nullptr; + m_extraRequestForReflection = nullptr; } ~Output() { - if (request && session) - { - spDestroyCompileRequest(request); - } + reset(); } Slang::Index findKernelDescIndex(gfx::StageType stage) const @@ -71,7 +75,16 @@ struct ShaderCompilerUtil List kernelDescs; ShaderProgram::Desc desc; - SlangCompileRequest* request = nullptr; + + /// Compile request that owns the lifetime of compiled kernel code. + SlangCompileRequest* m_requestForKernels = nullptr; + + /// Compile request that owns the lifetime of reflection information. + SlangCompileRequest* m_extraRequestForReflection = nullptr; + + SlangCompileRequest* getRequestForKernels() const { return m_requestForKernels; } + SlangCompileRequest* getRequestForReflection() const { return m_extraRequestForReflection ? m_extraRequestForReflection : m_requestForKernels; } + SlangSession* session = nullptr; }; @@ -87,6 +100,7 @@ struct ShaderCompilerUtil static SlangResult readSource(const Slang::String& inSourcePath, List& outSourceText); + static SlangResult _compileProgramImpl(SlangSession* session, const Options& options, const Input& input, const ShaderCompileRequest& request, Output& out); static SlangResult compileProgram(SlangSession* session, const Options& options, const Input& input, const ShaderCompileRequest& request, Output& out); }; -- cgit v1.2.3