diff options
| author | Tim Foley <tfoleyNV@users.noreply.github.com> | 2018-08-03 08:39:28 -0700 |
|---|---|---|
| committer | GitHub <noreply@github.com> | 2018-08-03 08:39:28 -0700 |
| commit | 68d705f6c805c9b4d31b386e065762e6db13ad18 (patch) | |
| tree | 97ffc0f24358101222d1bc62ac0c50affc55af12 /tools/gfx/render-d3d11.cpp | |
| parent | 5ea746a571ced32a8975eb3a238c562b3d487149 (diff) | |
Major overhaul of Renderer abstraction, to support a new example (#624)
The original goal here was to bring up a second example program: `model-viewer`.
While the existing `hello-world` example is enough to get somebody up to speed with the basics of the Slang API (as a drop-in replacement for `D3DCompile` or similar), it doesn't really show any of the big-picture stuff that Slang is meant to enable.
There wasn't any use of D3D12/Vulkan descriptor tables/sets, and there wasn't any use of interfaces, generics, or `ParameterBlock`s in the shader code.
The `model-viewer` example addresses these issues. Its shader code involves generics, interfaces, and multiple `ParameterBlock`s, and the host-side code demonstrates a few key things for working with Slang:
* There is an application-level abstraction for parameter blocks, that combines the graphics-API descriptor set object with Slang type information
* There is a shader cache layer used to look up an appropriate variant of a rendering effect by using parameter block types to "plug in" global type variables
* There is a clear separation between the phases of compilation: a first phase that does semantic checking and enables reflection-based allocation of graphics API objects, followed by one or more code generation passes for specialized kernels.
This example is certainly not perfect, and it will need to be revamped more going forward. In particular:
* The output picture is ugly as sin. We need a plan for how to get this to load better content, perhaps even popping up an error message to note that the required input data isn't present in the basic repository.
* The shader code is too simplistic. There isn't any real material variety, and the `IMaterial` abstraction is completely wrong.
* The use of parameter blocks is facile because there are no resource parameters right now. Fixing that will likely expose issues around interfacing with Slang's reflection API.
* The whole example exposes the issue that Slang's current APIs aren't really designed for the benefit of two-phase compilation (since our many client application has been stuck on one-phase compilation).
* Global type parameters are actually a Bad Idea that we only did for compatibility with existing codebases. We should not be showing them off in an example of the Right Way to use Slang, but the language support for type parameters on entry points is still not complete.
Of course, the majority of the changes here are *not* inside the example applications, and instead involve a major overhaul of the `Renderer` abstraction that is used for both tests and examples. The main thrust of the change is to make the abstraction layer be closer to the D3D12/Vulkan model than to a D3D11-style model. This is important for the `model-viewer` example, since it aspires to show how Slang can be incorporated into a renderer that targets a modern API. The most important bit is actually the use of descriptor sets and "pipeline layouts" a la Vulkan, since without these Slang's `ParameterBlock` abstraction won't make a lot of sense.
Implementation of the abstraction for the various APIs has very much been on an as-needed basis. The current implementation is just enough for the two examples to work, plus enough to get all the tests to pass in both debug and release builds on Windows.
A big missing feature in the API abstraction right now is memory lifetime management. The code had been trending toward something D3D11-like where a constant buffer could be mapped per-frame with the implementation doing behind-the-scenes allocation for targets like D3D12/Vulkan. I'd like to shift more toward a model of just exposing "transient" allocations that are only valid for one frame, because these are more representation of how an efficient renderer for next-generation APIs will work. That transition isn't actually complete, though, so there are problems with the existing examples where `hello-world` is actually scribbling into memory that the GPU might still be using, while `model-viewer` is doing full-on heavy-weight allocations on a per-frame basis with no real concern for the performance implications.
All together, there are a lot of things here that need more work, but this branch has been way too long-lived already, and so I'd like to get this checked in as long as all the tests pass.
Diffstat (limited to 'tools/gfx/render-d3d11.cpp')
| -rw-r--r-- | tools/gfx/render-d3d11.cpp | 2112 |
1 files changed, 2112 insertions, 0 deletions
diff --git a/tools/gfx/render-d3d11.cpp b/tools/gfx/render-d3d11.cpp new file mode 100644 index 000000000..57c0672bd --- /dev/null +++ b/tools/gfx/render-d3d11.cpp @@ -0,0 +1,2112 @@ +// render-d3d11.cpp + +#define _CRT_SECURE_NO_WARNINGS + +#include "render-d3d11.h" + +//WORKING: #include "options.h" +#include "render.h" +#include "d3d-util.h" + +#include "surface.h" + +// In order to use the Slang API, we need to include its header + +//#include <slang.h> + +#include "../../slang-com-ptr.h" + +// We will be rendering with Direct3D 11, so we need to include +// the Windows and D3D11 headers + +#define WIN32_LEAN_AND_MEAN +#define NOMINMAX +#include <Windows.h> +#undef WIN32_LEAN_AND_MEAN +#undef NOMINMAX + +#include <d3d11_2.h> +#include <d3dcompiler.h> + +// We will use the C standard library just for printing error messages. +#include <stdio.h> + +#ifdef _MSC_VER +#include <stddef.h> +#if (_MSC_VER < 1900) +#define snprintf sprintf_s +#endif +#endif +// +using namespace Slang; + +namespace gfx { + +class D3D11Renderer : public Renderer +{ +public: + enum + { + kMaxUAVs = 64, + kMaxRTVs = 8, + }; + + // Renderer implementation + virtual SlangResult initialize(const Desc& desc, void* inWindowHandle) override; + virtual void setClearColor(const float color[4]) override; + virtual void clearFrame() override; + virtual void presentFrame() override; + TextureResource::Desc getSwapChainTextureDesc() override; + + Result createTextureResource(Resource::Usage initialUsage, const TextureResource::Desc& desc, const TextureResource::Data* initData, TextureResource** outResource) override; + Result createBufferResource(Resource::Usage initialUsage, const BufferResource::Desc& desc, const void* initData, BufferResource** outResource) override; + Result createSamplerState(SamplerState::Desc const& desc, SamplerState** outSampler) override; + + Result createTextureView(TextureResource* texture, ResourceView::Desc const& desc, ResourceView** outView) override; + Result createBufferView(BufferResource* buffer, ResourceView::Desc const& desc, ResourceView** outView) override; + + Result createInputLayout(const InputElementDesc* inputElements, UInt inputElementCount, InputLayout** outLayout) override; + + Result createDescriptorSetLayout(const DescriptorSetLayout::Desc& desc, DescriptorSetLayout** outLayout) override; + Result createPipelineLayout(const PipelineLayout::Desc& desc, PipelineLayout** outLayout) override; + Result createDescriptorSet(DescriptorSetLayout* layout, DescriptorSet** outDescriptorSet) override; + + Result createProgram(const ShaderProgram::Desc& desc, ShaderProgram** outProgram) override; + Result createGraphicsPipelineState(const GraphicsPipelineStateDesc& desc, PipelineState** outState) override; + Result createComputePipelineState(const ComputePipelineStateDesc& desc, PipelineState** outState) override; + + virtual SlangResult captureScreenSurface(Surface& surfaceOut) override; + + virtual void* map(BufferResource* buffer, MapFlavor flavor) override; + virtual void unmap(BufferResource* buffer) override; + virtual void setPrimitiveTopology(PrimitiveTopology topology) override; + + virtual void setDescriptorSet(PipelineType pipelineType, PipelineLayout* layout, UInt index, DescriptorSet* descriptorSet) override; + + virtual void setVertexBuffers(UInt startSlot, UInt slotCount, BufferResource*const* buffers, const UInt* strides, const UInt* offsets) override; + virtual void setIndexBuffer(BufferResource* buffer, Format indexFormat, UInt offset) override; + virtual void setDepthStencilTarget(ResourceView* depthStencilView) override; + virtual void setPipelineState(PipelineType pipelineType, PipelineState* state) override; + virtual void draw(UInt vertexCount, UInt startVertex) override; + virtual void drawIndexed(UInt indexCount, UInt startIndex, UInt baseVertex) override; + virtual void dispatchCompute(int x, int y, int z) override; + virtual void submitGpuWork() override {} + virtual void waitForGpu() override {} + virtual RendererType getRendererType() const override { return RendererType::DirectX11; } + + protected: + +#if 0 + struct BindingDetail + { + ComPtr<ID3D11ShaderResourceView> m_srv; + ComPtr<ID3D11UnorderedAccessView> m_uav; + ComPtr<ID3D11SamplerState> m_samplerState; + }; + + class BindingStateImpl: public BindingState + { + public: + typedef BindingState Parent; + + /// Ctor + BindingStateImpl(const Desc& desc): + Parent(desc) + {} + + List<BindingDetail> m_bindingDetails; + }; +#endif + + enum class D3D11DescriptorSlotType + { + ConstantBuffer, + ShaderResourceView, + UnorderedAccessView, + Sampler, + + CombinedTextureSampler, + + CountOf, + }; + + class DescriptorSetLayoutImpl : public DescriptorSetLayout + { + public: + struct RangeInfo + { + D3D11DescriptorSlotType type; + UInt arrayIndex; + UInt pairedSamplerArrayIndex; + }; + List<RangeInfo> m_ranges; + + UInt m_counts[int(D3D11DescriptorSlotType::CountOf)]; + }; + + class PipelineLayoutImpl : public PipelineLayout + { + public: + struct DescriptorSetInfo + { + RefPtr<DescriptorSetLayoutImpl> layout; + UInt baseIndices[int(D3D11DescriptorSlotType::CountOf)]; + }; + + List<DescriptorSetInfo> m_descriptorSets; + UINT m_uavCount; + }; + + class DescriptorSetImpl : public DescriptorSet + { + public: + virtual void setConstantBuffer(UInt range, UInt index, BufferResource* buffer) override; + virtual void setResource(UInt range, UInt index, ResourceView* view) override; + virtual void setSampler(UInt range, UInt index, SamplerState* sampler) override; + virtual void setCombinedTextureSampler( + UInt range, + UInt index, + ResourceView* textureView, + SamplerState* sampler) override; + + RefPtr<DescriptorSetLayoutImpl> m_layout; + + List<ComPtr<ID3D11Buffer>> m_cbs; + List<ComPtr<ID3D11ShaderResourceView>> m_srvs; + List<ComPtr<ID3D11UnorderedAccessView>> m_uavs; + List<ComPtr<ID3D11SamplerState>> m_samplers; + }; + + class ShaderProgramImpl: public ShaderProgram + { + public: + ComPtr<ID3D11VertexShader> m_vertexShader; + ComPtr<ID3D11PixelShader> m_pixelShader; + ComPtr<ID3D11ComputeShader> m_computeShader; + }; + + class BufferResourceImpl: public BufferResource + { + public: + typedef BufferResource Parent; + + BufferResourceImpl(const Desc& desc, Usage initialUsage): + Parent(desc), + m_initialUsage(initialUsage) + { + } + + MapFlavor m_mapFlavor; + Usage m_initialUsage; + ComPtr<ID3D11Buffer> m_buffer; + ComPtr<ID3D11Buffer> m_staging; + }; + class TextureResourceImpl : public TextureResource + { + public: + typedef TextureResource Parent; + + TextureResourceImpl(const Desc& desc, Usage initialUsage) : + Parent(desc), + m_initialUsage(initialUsage) + { + } + Usage m_initialUsage; + ComPtr<ID3D11Resource> m_resource; + + }; + + class SamplerStateImpl : public SamplerState + { + public: + ComPtr<ID3D11SamplerState> m_sampler; + }; + + + class ResourceViewImpl : public ResourceView + { + public: + enum class Type + { + SRV, + UAV, + DSV, + RTV, + }; + Type m_type; + }; + + class ShaderResourceViewImpl : public ResourceViewImpl + { + public: + ComPtr<ID3D11ShaderResourceView> m_srv; + }; + + class UnorderedAccessViewImpl : public ResourceViewImpl + { + public: + ComPtr<ID3D11UnorderedAccessView> m_uav; + }; + + class DepthStencilViewImpl : public ResourceViewImpl + { + public: + ComPtr<ID3D11DepthStencilView> m_dsv; + }; + + class RenderTargetViewImpl : public ResourceViewImpl + { + public: + ComPtr<ID3D11RenderTargetView> m_rtv; + }; + + class InputLayoutImpl: public InputLayout + { + public: + ComPtr<ID3D11InputLayout> m_layout; + }; + + class PipelineStateImpl : public PipelineState + { + public: + RefPtr<ShaderProgramImpl> m_program; + RefPtr<PipelineLayoutImpl> m_pipelineLayout; + }; + + + class GraphicsPipelineStateImpl : public PipelineStateImpl + { + public: + UINT m_rtvCount; + + RefPtr<InputLayoutImpl> m_inputLayout; + ComPtr<ID3D11DepthStencilState> m_depthStencilState; + ComPtr<ID3D11RasterizerState> m_rasterizerState; + + UINT m_stencilRef; + }; + + class ComputePipelineStateImpl : public PipelineStateImpl + { + public: + }; + + /// Capture a texture to a file + static HRESULT captureTextureToSurface(ID3D11Device* device, ID3D11DeviceContext* context, ID3D11Texture2D* texture, Surface& surfaceOut); + + void _flushGraphicsState(); + void _flushComputeState(); + + ComPtr<IDXGISwapChain> m_swapChain; + ComPtr<ID3D11Device> m_device; + ComPtr<ID3D11DeviceContext> m_immediateContext; + ComPtr<ID3D11Texture2D> m_backBufferTexture; + + RefPtr<TextureResourceImpl> m_primaryRenderTargetTexture; + RefPtr<RenderTargetViewImpl> m_primaryRenderTargetView; + +// List<ComPtr<ID3D11RenderTargetView> > m_renderTargetViews; +// List<ComPtr<ID3D11Texture2D> > m_renderTargetTextures; + + bool m_renderTargetBindingsDirty = false; + + RefPtr<GraphicsPipelineStateImpl> m_currentGraphicsState; + RefPtr<ComputePipelineStateImpl> m_currentComputeState; + + ComPtr<ID3D11RenderTargetView> m_rtvBindings[kMaxRTVs]; + ComPtr<ID3D11DepthStencilView> m_dsvBinding; + ComPtr<ID3D11UnorderedAccessView> m_uavBindings[int(PipelineType::CountOf)][kMaxUAVs]; + bool m_targetBindingsDirty[int(PipelineType::CountOf)]; + + Desc m_desc; + + float m_clearColor[4] = { 0, 0, 0, 0 }; +}; + +Renderer* createD3D11Renderer() +{ + return new D3D11Renderer(); +} + +/* static */HRESULT D3D11Renderer::captureTextureToSurface(ID3D11Device* device, ID3D11DeviceContext* context, ID3D11Texture2D* texture, Surface& surfaceOut) +{ + if (!context) return E_INVALIDARG; + if (!texture) return E_INVALIDARG; + + D3D11_TEXTURE2D_DESC textureDesc; + texture->GetDesc(&textureDesc); + + // Don't bother supporting MSAA for right now + if (textureDesc.SampleDesc.Count > 1) + { + fprintf(stderr, "ERROR: cannot capture multi-sample texture\n"); + return E_INVALIDARG; + } + + HRESULT hr = S_OK; + ComPtr<ID3D11Texture2D> stagingTexture; + + if (textureDesc.Usage == D3D11_USAGE_STAGING && (textureDesc.CPUAccessFlags & D3D11_CPU_ACCESS_READ)) + { + stagingTexture = texture; + } + else + { + // Modify the descriptor to give us a staging texture + textureDesc.BindFlags = 0; + textureDesc.MiscFlags &= ~D3D11_RESOURCE_MISC_TEXTURECUBE; + textureDesc.CPUAccessFlags = D3D11_CPU_ACCESS_READ; + textureDesc.Usage = D3D11_USAGE_STAGING; + + hr = device->CreateTexture2D(&textureDesc, 0, stagingTexture.writeRef()); + if (FAILED(hr)) + { + fprintf(stderr, "ERROR: failed to create staging texture\n"); + return hr; + } + + context->CopyResource(stagingTexture, texture); + } + + // Now just read back texels from the staging textures + { + D3D11_MAPPED_SUBRESOURCE mappedResource; + SLANG_RETURN_ON_FAIL(context->Map(stagingTexture, 0, D3D11_MAP_READ, 0, &mappedResource)); + + Result res = surfaceOut.set(textureDesc.Width, textureDesc.Height, Format::RGBA_Unorm_UInt8, mappedResource.RowPitch, mappedResource.pData, SurfaceAllocator::getMallocAllocator()); + + // Make sure to unmap + context->Unmap(stagingTexture, 0); + return res; + } +} + +// !!!!!!!!!!!!!!!!!!!!!!!!!!!! Renderer interface !!!!!!!!!!!!!!!!!!!!!!!!!! + +SlangResult D3D11Renderer::initialize(const Desc& desc, void* inWindowHandle) +{ + auto windowHandle = (HWND)inWindowHandle; + m_desc = desc; + + // Rather than statically link against D3D, we load it dynamically. + HMODULE d3dModule = LoadLibraryA("d3d11.dll"); + if (!d3dModule) + { + fprintf(stderr, "error: failed load 'd3d11.dll'\n"); + return SLANG_FAIL; + } + + PFN_D3D11_CREATE_DEVICE_AND_SWAP_CHAIN D3D11CreateDeviceAndSwapChain_ = + (PFN_D3D11_CREATE_DEVICE_AND_SWAP_CHAIN)GetProcAddress(d3dModule, "D3D11CreateDeviceAndSwapChain"); + if (!D3D11CreateDeviceAndSwapChain_) + { + fprintf(stderr, + "error: failed load symbol 'D3D11CreateDeviceAndSwapChain'\n"); + return SLANG_FAIL; + } + + UINT deviceFlags = 0; + +#ifdef _DEBUG + // We will enable the D3D debug more for debug builds. + // + // TODO: we should probably provide a command-line option + // to override this kind of default rather than leave it + // up to each back-end to specify. + deviceFlags |= D3D11_CREATE_DEVICE_DEBUG; +#endif + + // Our swap chain uses RGBA8 with sRGB, with double buffering. + DXGI_SWAP_CHAIN_DESC swapChainDesc = { 0 }; + swapChainDesc.BufferUsage = DXGI_USAGE_RENDER_TARGET_OUTPUT; + + // Note(tfoley): Disabling sRGB for DX back buffer for now, so that we + // can get consistent output with OpenGL, where setting up sRGB will + // probably be more involved. + // swapChainDesc.BufferDesc.Format = DXGI_FORMAT_R8G8B8A8_UNORM_SRGB; + swapChainDesc.BufferDesc.Format = DXGI_FORMAT_R8G8B8A8_UNORM; + + swapChainDesc.SampleDesc.Count = 1; + swapChainDesc.SampleDesc.Quality = 0; + swapChainDesc.BufferCount = 2; + swapChainDesc.OutputWindow = windowHandle; + swapChainDesc.Windowed = TRUE; + swapChainDesc.SwapEffect = DXGI_SWAP_EFFECT_DISCARD; + swapChainDesc.Flags = 0; + + // We will ask for the highest feature level that can be supported. + const D3D_FEATURE_LEVEL featureLevels[] = { + D3D_FEATURE_LEVEL_11_1, + D3D_FEATURE_LEVEL_11_0, + D3D_FEATURE_LEVEL_10_1, + D3D_FEATURE_LEVEL_10_0, + D3D_FEATURE_LEVEL_9_3, + D3D_FEATURE_LEVEL_9_2, + D3D_FEATURE_LEVEL_9_1, + }; + D3D_FEATURE_LEVEL featureLevel = D3D_FEATURE_LEVEL_9_1; + const int totalNumFeatureLevels = SLANG_COUNT_OF(featureLevels); + + // On a machine that does not have an up-to-date version of D3D installed, + // the `D3D11CreateDeviceAndSwapChain` call will fail with `E_INVALIDARG` + // if you ask for featuer level 11_1. The workaround is to call + // `D3D11CreateDeviceAndSwapChain` up to twice: the first time with 11_1 + // at the start of the list of requested feature levels, and the second + // time without it. + + for (int ii = 0; ii < 2; ++ii) + { + const HRESULT hr = D3D11CreateDeviceAndSwapChain_( + nullptr, // adapter (use default) + D3D_DRIVER_TYPE_REFERENCE, +// D3D_DRIVER_TYPE_HARDWARE, + nullptr, // software + deviceFlags, + &featureLevels[ii], + totalNumFeatureLevels - ii, + D3D11_SDK_VERSION, + &swapChainDesc, + m_swapChain.writeRef(), + m_device.writeRef(), + &featureLevel, + m_immediateContext.writeRef()); + + // Failures with `E_INVALIDARG` might be due to feature level 11_1 + // not being supported. + if (hr == E_INVALIDARG) + { + continue; + } + + // Other failures are real, though. + SLANG_RETURN_ON_FAIL(hr); + // We must have a swap chain + break; + } + + // TODO: Add support for debugging to help detect leaks: + // + // ComPtr<ID3D11Debug> gDebug; + // m_device->QueryInterface(IID_PPV_ARGS(gDebug.writeRef())); + // + + // After we've created the swap chain, we can request a pointer to the + // back buffer as a D3D11 texture, and create a render-target view from it. + + static const IID kIID_ID3D11Texture2D = { + 0x6f15aaf2, 0xd208, 0x4e89, 0x9a, 0xb4, 0x48, + 0x95, 0x35, 0xd3, 0x4f, 0x9c }; + + SLANG_RETURN_ON_FAIL(m_swapChain->GetBuffer(0, kIID_ID3D11Texture2D, (void**)m_backBufferTexture.writeRef())); + +// for (int i = 0; i < 8; i++) + { + ComPtr<ID3D11Texture2D> texture; + D3D11_TEXTURE2D_DESC textureDesc; + m_backBufferTexture->GetDesc(&textureDesc); + SLANG_RETURN_ON_FAIL(m_device->CreateTexture2D(&textureDesc, nullptr, texture.writeRef())); + + ComPtr<ID3D11RenderTargetView> rtv; + D3D11_RENDER_TARGET_VIEW_DESC rtvDesc; + rtvDesc.Format = DXGI_FORMAT_R8G8B8A8_UNORM; + rtvDesc.Texture2D.MipSlice = 0; + rtvDesc.ViewDimension = D3D11_RTV_DIMENSION_TEXTURE2D; + SLANG_RETURN_ON_FAIL(m_device->CreateRenderTargetView(texture, &rtvDesc, rtv.writeRef())); + + TextureResource::Desc resourceDesc; + resourceDesc.init2D(Resource::Type::Texture2D, Format::RGBA_Unorm_UInt8, textureDesc.Width, textureDesc.Height, 1); + + RefPtr<TextureResource> primaryRenderTargetTexture; + SLANG_RETURN_ON_FAIL(createTextureResource(Resource::Usage::RenderTarget, resourceDesc, nullptr, primaryRenderTargetTexture.writeRef())); + + ResourceView::Desc viewDesc; + viewDesc.format = resourceDesc.format; + viewDesc.type = ResourceView::Type::RenderTarget; + RefPtr<ResourceView> primaryRenderTargetView; + SLANG_RETURN_ON_FAIL(createTextureView(primaryRenderTargetTexture, viewDesc, primaryRenderTargetView.writeRef())); + + m_primaryRenderTargetTexture = (TextureResourceImpl*) primaryRenderTargetTexture.Ptr(); + m_primaryRenderTargetView = (RenderTargetViewImpl*) primaryRenderTargetView.Ptr(); + } + +// m_immediateContext->OMSetRenderTargets(1, m_primaryRenderTargetView->m_rtv.readRef(), nullptr); + m_rtvBindings[0] = m_primaryRenderTargetView->m_rtv; + m_targetBindingsDirty[int(PipelineType::Graphics)] = true; + + // Similarly, we are going to set up a viewport once, and then never + // switch, since this is a simple test app. + D3D11_VIEWPORT viewport; + viewport.TopLeftX = 0; + viewport.TopLeftY = 0; + viewport.Width = (float)desc.width; + viewport.Height = (float)desc.height; + viewport.MaxDepth = 1; // TODO(tfoley): use reversed depth + viewport.MinDepth = 0; + m_immediateContext->RSSetViewports(1, &viewport); + + return SLANG_OK; +} + +void D3D11Renderer::setClearColor(const float color[4]) +{ + memcpy(m_clearColor, color, sizeof(m_clearColor)); +} + +void D3D11Renderer::clearFrame() +{ + m_immediateContext->ClearRenderTargetView(m_primaryRenderTargetView->m_rtv, m_clearColor); + + if(m_dsvBinding) + { + m_immediateContext->ClearDepthStencilView(m_dsvBinding, D3D11_CLEAR_DEPTH | D3D11_CLEAR_STENCIL, 1.0f, 0); + } +} + +void D3D11Renderer::presentFrame() +{ + m_immediateContext->CopyResource(m_backBufferTexture, m_primaryRenderTargetTexture->m_resource); + m_swapChain->Present(0, 0); +} + +TextureResource::Desc D3D11Renderer::getSwapChainTextureDesc() +{ + D3D11_TEXTURE2D_DESC dxDesc; + ((ID3D11Texture2D*)m_primaryRenderTargetTexture->m_resource.get())->GetDesc(&dxDesc); + + TextureResource::Desc desc; + desc.init2D(Resource::Type::Texture2D, Format::Unknown, dxDesc.Width, dxDesc.Height, 1); + + return desc; +} + +SlangResult D3D11Renderer::captureScreenSurface(Surface& surfaceOut) +{ + return captureTextureToSurface(m_device, m_immediateContext, (ID3D11Texture2D*) m_primaryRenderTargetTexture->m_resource.get(), surfaceOut); +} + +static D3D11_BIND_FLAG _calcResourceFlag(Resource::BindFlag::Enum bindFlag) +{ + typedef Resource::BindFlag BindFlag; + switch (bindFlag) + { + case BindFlag::VertexBuffer: return D3D11_BIND_VERTEX_BUFFER; + case BindFlag::IndexBuffer: return D3D11_BIND_INDEX_BUFFER; + case BindFlag::ConstantBuffer: return D3D11_BIND_CONSTANT_BUFFER; + case BindFlag::StreamOutput: return D3D11_BIND_STREAM_OUTPUT; + case BindFlag::RenderTarget: return D3D11_BIND_RENDER_TARGET; + case BindFlag::DepthStencil: return D3D11_BIND_DEPTH_STENCIL; + case BindFlag::UnorderedAccess: return D3D11_BIND_UNORDERED_ACCESS; + case BindFlag::PixelShaderResource: return D3D11_BIND_SHADER_RESOURCE; + case BindFlag::NonPixelShaderResource: return D3D11_BIND_SHADER_RESOURCE; + default: return D3D11_BIND_FLAG(0); + } +} + +static int _calcResourceBindFlags(int bindFlags) +{ + int dstFlags = 0; + while (bindFlags) + { + int lsb = bindFlags & -bindFlags; + + dstFlags |= _calcResourceFlag(Resource::BindFlag::Enum(lsb)); + bindFlags &= ~lsb; + } + return dstFlags; +} + +static int _calcResourceAccessFlags(int accessFlags) +{ + switch (accessFlags) + { + case 0: return 0; + case Resource::AccessFlag::Read: return D3D11_CPU_ACCESS_READ; + case Resource::AccessFlag::Write: return D3D11_CPU_ACCESS_WRITE; + case Resource::AccessFlag::Read | + Resource::AccessFlag::Write: return D3D11_CPU_ACCESS_READ | D3D11_CPU_ACCESS_WRITE; + default: assert(!"Invalid flags"); return 0; + } +} + +Result D3D11Renderer::createTextureResource(Resource::Usage initialUsage, const TextureResource::Desc& descIn, const TextureResource::Data* initData, TextureResource** outResource) +{ + TextureResource::Desc srcDesc(descIn); + srcDesc.setDefaults(initialUsage); + + const int effectiveArraySize = srcDesc.calcEffectiveArraySize(); + + if(initData) + { + assert(initData->numSubResources == srcDesc.numMipLevels * effectiveArraySize * srcDesc.size.depth); + } + + const DXGI_FORMAT format = D3DUtil::getMapFormat(srcDesc.format); + if (format == DXGI_FORMAT_UNKNOWN) + { + return SLANG_FAIL; + } + + const int bindFlags = _calcResourceBindFlags(srcDesc.bindFlags); + + // Set up the initialize data + List<D3D11_SUBRESOURCE_DATA> subRes; + D3D11_SUBRESOURCE_DATA* subResourcesPtr = nullptr; + if(initData) + { + subRes.SetSize(srcDesc.numMipLevels * effectiveArraySize); + { + int subResourceIndex = 0; + for (int i = 0; i < effectiveArraySize; i++) + { + for (int j = 0; j < srcDesc.numMipLevels; j++) + { + const int mipHeight = TextureResource::calcMipSize(srcDesc.size.height, j); + + D3D11_SUBRESOURCE_DATA& data = subRes[subResourceIndex]; + + data.pSysMem = initData->subResources[subResourceIndex]; + + data.SysMemPitch = UINT(initData->mipRowStrides[j]); + data.SysMemSlicePitch = UINT(initData->mipRowStrides[j] * mipHeight); + + subResourceIndex++; + } + } + } + subResourcesPtr = subRes.Buffer(); + } + + const int accessFlags = _calcResourceAccessFlags(srcDesc.cpuAccessFlags); + + RefPtr<TextureResourceImpl> texture(new TextureResourceImpl(srcDesc, initialUsage)); + + switch (srcDesc.type) + { + case Resource::Type::Texture1D: + { + D3D11_TEXTURE1D_DESC desc = { 0 }; + desc.BindFlags = bindFlags; + desc.CPUAccessFlags = accessFlags; + desc.Format = format; + desc.MiscFlags = 0; + desc.MipLevels = srcDesc.numMipLevels; + desc.ArraySize = effectiveArraySize; + desc.Width = srcDesc.size.width; + desc.Usage = D3D11_USAGE_DEFAULT; + + ComPtr<ID3D11Texture1D> texture1D; + SLANG_RETURN_ON_FAIL(m_device->CreateTexture1D(&desc, subResourcesPtr, texture1D.writeRef())); + + texture->m_resource = texture1D; + break; + } + case Resource::Type::TextureCube: + case Resource::Type::Texture2D: + { + D3D11_TEXTURE2D_DESC desc = { 0 }; + desc.BindFlags = bindFlags; + desc.CPUAccessFlags = accessFlags; + desc.Format = format; + desc.MiscFlags = 0; + desc.MipLevels = srcDesc.numMipLevels; + desc.ArraySize = effectiveArraySize; + + desc.Width = srcDesc.size.width; + desc.Height = srcDesc.size.height; + desc.Usage = D3D11_USAGE_DEFAULT; + desc.SampleDesc.Count = srcDesc.sampleDesc.numSamples; + desc.SampleDesc.Quality = srcDesc.sampleDesc.quality; + + if (srcDesc.type == Resource::Type::TextureCube) + { + desc.MiscFlags |= D3D11_RESOURCE_MISC_TEXTURECUBE; + } + + ComPtr<ID3D11Texture2D> texture2D; + SLANG_RETURN_ON_FAIL(m_device->CreateTexture2D(&desc, subResourcesPtr, texture2D.writeRef())); + + texture->m_resource = texture2D; + break; + } + case Resource::Type::Texture3D: + { + D3D11_TEXTURE3D_DESC desc = { 0 }; + desc.BindFlags = bindFlags; + desc.CPUAccessFlags = accessFlags; + desc.Format = format; + desc.MiscFlags = 0; + desc.MipLevels = srcDesc.numMipLevels; + desc.Width = srcDesc.size.width; + desc.Height = srcDesc.size.height; + desc.Depth = srcDesc.size.depth; + desc.Usage = D3D11_USAGE_DEFAULT; + + ComPtr<ID3D11Texture3D> texture3D; + SLANG_RETURN_ON_FAIL(m_device->CreateTexture3D(&desc, subResourcesPtr, texture3D.writeRef())); + + texture->m_resource = texture3D; + break; + } + default: + return SLANG_FAIL; + } + + *outResource = texture.detach(); + return SLANG_OK; +} + +Result D3D11Renderer::createBufferResource(Resource::Usage initialUsage, const BufferResource::Desc& descIn, const void* initData, BufferResource** outResource) +{ + BufferResource::Desc srcDesc(descIn); + srcDesc.setDefaults(initialUsage); + + // Make aligned to 256 bytes... not sure why, but if you remove this the tests do fail. + const size_t alignedSizeInBytes = D3DUtil::calcAligned(srcDesc.sizeInBytes, 256); + + // Hack to make the initialization never read from out of bounds memory, by copying into a buffer + List<uint8_t> initDataBuffer; + if (initData && alignedSizeInBytes > srcDesc.sizeInBytes) + { + initDataBuffer.SetSize(alignedSizeInBytes); + ::memcpy(initDataBuffer.Buffer(), initData, srcDesc.sizeInBytes); + initData = initDataBuffer.Buffer(); + } + + D3D11_BUFFER_DESC bufferDesc = { 0 }; + bufferDesc.ByteWidth = UINT(alignedSizeInBytes); + bufferDesc.BindFlags = _calcResourceBindFlags(srcDesc.bindFlags); + // For read we'll need to do some staging + bufferDesc.CPUAccessFlags = _calcResourceAccessFlags(descIn.cpuAccessFlags & Resource::AccessFlag::Write); + bufferDesc.Usage = D3D11_USAGE_DEFAULT; + + // If written by CPU, make it dynamic + if (descIn.cpuAccessFlags & Resource::AccessFlag::Write) + { + bufferDesc.Usage = D3D11_USAGE_DYNAMIC; + } + + switch (initialUsage) + { + case Resource::Usage::ConstantBuffer: + { + // We'll just assume ConstantBuffers are dynamic for now + bufferDesc.Usage = D3D11_USAGE_DYNAMIC; + break; + } + default: break; + } + + if (bufferDesc.BindFlags & (D3D11_BIND_UNORDERED_ACCESS | D3D11_BIND_SHADER_RESOURCE)) + { + //desc.BindFlags = D3D11_BIND_UNORDERED_ACCESS | D3D11_BIND_SHADER_RESOURCE; + if (srcDesc.elementSize != 0) + { + bufferDesc.StructureByteStride = srcDesc.elementSize; + bufferDesc.MiscFlags = D3D11_RESOURCE_MISC_BUFFER_STRUCTURED; + } + else + { + bufferDesc.MiscFlags = D3D11_RESOURCE_MISC_BUFFER_ALLOW_RAW_VIEWS; + } + } + + D3D11_SUBRESOURCE_DATA subResourceData = { 0 }; + subResourceData.pSysMem = initData; + + RefPtr<BufferResourceImpl> buffer(new BufferResourceImpl(srcDesc, initialUsage)); + + SLANG_RETURN_ON_FAIL(m_device->CreateBuffer(&bufferDesc, initData ? &subResourceData : nullptr, buffer->m_buffer.writeRef())); + + if (srcDesc.cpuAccessFlags & Resource::AccessFlag::Read) + { + D3D11_BUFFER_DESC bufDesc = {}; + bufDesc.BindFlags = 0; + bufDesc.ByteWidth = (UINT)alignedSizeInBytes; + bufDesc.CPUAccessFlags = D3D11_CPU_ACCESS_READ; + bufDesc.Usage = D3D11_USAGE_STAGING; + + SLANG_RETURN_ON_FAIL(m_device->CreateBuffer(&bufDesc, nullptr, buffer->m_staging.writeRef())); + } + + *outResource = buffer.detach(); + return SLANG_OK; +} + +D3D11_FILTER_TYPE translateFilterMode(TextureFilteringMode mode) +{ + switch (mode) + { + default: + return D3D11_FILTER_TYPE(0); + +#define CASE(SRC, DST) \ + case TextureFilteringMode::SRC: return D3D11_FILTER_TYPE_##DST + + CASE(Point, POINT); + CASE(Linear, LINEAR); + +#undef CASE + } +} + +D3D11_FILTER_REDUCTION_TYPE translateFilterReduction(TextureReductionOp op) +{ + switch (op) + { + default: + return D3D11_FILTER_REDUCTION_TYPE(0); + +#define CASE(SRC, DST) \ + case TextureReductionOp::SRC: return D3D11_FILTER_REDUCTION_TYPE_##DST + + CASE(Average, STANDARD); + CASE(Comparison, COMPARISON); + CASE(Minimum, MINIMUM); + CASE(Maximum, MAXIMUM); + +#undef CASE + } +} + +D3D11_TEXTURE_ADDRESS_MODE translateAddressingMode(TextureAddressingMode mode) +{ + switch (mode) + { + default: + return D3D11_TEXTURE_ADDRESS_MODE(0); + +#define CASE(SRC, DST) \ + case TextureAddressingMode::SRC: return D3D11_TEXTURE_ADDRESS_##DST + + CASE(Wrap, WRAP); + CASE(ClampToEdge, CLAMP); + CASE(ClampToBorder, BORDER); + CASE(MirrorRepeat, MIRROR); + CASE(MirrorOnce, MIRROR_ONCE); + +#undef CASE + } +} + +static D3D11_COMPARISON_FUNC translateComparisonFunc(ComparisonFunc func) +{ + switch (func) + { + default: + // TODO: need to report failures + return D3D11_COMPARISON_ALWAYS; + +#define CASE(FROM, TO) \ + case ComparisonFunc::FROM: return D3D11_COMPARISON_##TO + + CASE(Never, NEVER); + CASE(Less, LESS); + CASE(Equal, EQUAL); + CASE(LessEqual, LESS_EQUAL); + CASE(Greater, GREATER); + CASE(NotEqual, NOT_EQUAL); + CASE(GreaterEqual, GREATER_EQUAL); + CASE(Always, ALWAYS); +#undef CASE + } +} + +Result D3D11Renderer::createSamplerState(SamplerState::Desc const& desc, SamplerState** outSampler) +{ + D3D11_FILTER_REDUCTION_TYPE dxReduction = translateFilterReduction(desc.reductionOp); + D3D11_FILTER dxFilter; + if (desc.maxAnisotropy > 1) + { + dxFilter = D3D11_ENCODE_ANISOTROPIC_FILTER(dxReduction); + } + else + { + D3D11_FILTER_TYPE dxMin = translateFilterMode(desc.minFilter); + D3D11_FILTER_TYPE dxMag = translateFilterMode(desc.magFilter); + D3D11_FILTER_TYPE dxMip = translateFilterMode(desc.mipFilter); + + dxFilter = D3D11_ENCODE_BASIC_FILTER(dxMin, dxMag, dxMip, dxReduction); + } + + D3D11_SAMPLER_DESC dxDesc = {}; + dxDesc.Filter = dxFilter; + dxDesc.AddressU = translateAddressingMode(desc.addressU); + dxDesc.AddressV = translateAddressingMode(desc.addressV); + dxDesc.AddressW = translateAddressingMode(desc.addressW); + dxDesc.MipLODBias = desc.mipLODBias; + dxDesc.MaxAnisotropy = desc.maxAnisotropy; + dxDesc.ComparisonFunc = translateComparisonFunc(desc.comparisonFunc); + for (int ii = 0; ii < 4; ++ii) + dxDesc.BorderColor[ii] = desc.borderColor[ii]; + dxDesc.MinLOD = desc.minLOD; + dxDesc.MaxLOD = desc.maxLOD; + + ComPtr<ID3D11SamplerState> sampler; + SLANG_RETURN_ON_FAIL(m_device->CreateSamplerState( + &dxDesc, + sampler.writeRef())); + + RefPtr<SamplerStateImpl> samplerImpl = new SamplerStateImpl(); + samplerImpl->m_sampler = sampler; + *outSampler = samplerImpl.detach(); + return SLANG_OK; +} + +Result D3D11Renderer::createTextureView(TextureResource* texture, ResourceView::Desc const& desc, ResourceView** outView) +{ + auto resourceImpl = (TextureResourceImpl*) texture; + + switch (desc.type) + { + default: + return SLANG_FAIL; + + case ResourceView::Type::RenderTarget: + { + ComPtr<ID3D11RenderTargetView> rtv; + SLANG_RETURN_ON_FAIL(m_device->CreateRenderTargetView(resourceImpl->m_resource, nullptr, rtv.writeRef())); + + RefPtr<RenderTargetViewImpl> viewImpl = new RenderTargetViewImpl(); + viewImpl->m_type = ResourceViewImpl::Type::RTV; + viewImpl->m_rtv = rtv; + *outView = viewImpl.detach(); + return SLANG_OK; + } + break; + + case ResourceView::Type::DepthStencil: + { + ComPtr<ID3D11DepthStencilView> dsv; + SLANG_RETURN_ON_FAIL(m_device->CreateDepthStencilView(resourceImpl->m_resource, nullptr, dsv.writeRef())); + + RefPtr<DepthStencilViewImpl> viewImpl = new DepthStencilViewImpl(); + viewImpl->m_type = ResourceViewImpl::Type::DSV; + viewImpl->m_dsv = dsv; + *outView = viewImpl.detach(); + return SLANG_OK; + } + break; + + case ResourceView::Type::UnorderedAccess: + { + ComPtr<ID3D11UnorderedAccessView> uav; + SLANG_RETURN_ON_FAIL(m_device->CreateUnorderedAccessView(resourceImpl->m_resource, nullptr, uav.writeRef())); + + RefPtr<UnorderedAccessViewImpl> viewImpl = new UnorderedAccessViewImpl(); + viewImpl->m_type = ResourceViewImpl::Type::UAV; + viewImpl->m_uav = uav; + *outView = viewImpl.detach(); + return SLANG_OK; + } + break; + + case ResourceView::Type::ShaderResource: + { + ComPtr<ID3D11ShaderResourceView> srv; + SLANG_RETURN_ON_FAIL(m_device->CreateShaderResourceView(resourceImpl->m_resource, nullptr, srv.writeRef())); + + RefPtr<ShaderResourceViewImpl> viewImpl = new ShaderResourceViewImpl(); + viewImpl->m_type = ResourceViewImpl::Type::SRV; + viewImpl->m_srv = srv; + *outView = viewImpl.detach(); + return SLANG_OK; + } + break; + } +} + +Result D3D11Renderer::createBufferView(BufferResource* buffer, ResourceView::Desc const& desc, ResourceView** outView) +{ + auto resourceImpl = (BufferResourceImpl*) buffer; + auto resourceDesc = resourceImpl->getDesc(); + + switch (desc.type) + { + default: + return SLANG_FAIL; + + case ResourceView::Type::UnorderedAccess: + { + D3D11_UNORDERED_ACCESS_VIEW_DESC uavDesc = {}; + uavDesc.ViewDimension = D3D11_UAV_DIMENSION_BUFFER; + uavDesc.Format = D3DUtil::getMapFormat(desc.format); + uavDesc.Buffer.FirstElement = 0; + uavDesc.Buffer.NumElements = resourceDesc.sizeInBytes; + + if(resourceDesc.elementSize) + { + uavDesc.Buffer.NumElements = resourceDesc.sizeInBytes / resourceDesc.elementSize; + } + else if(desc.format == Format::Unknown) + { + uavDesc.Buffer.Flags |= D3D11_BUFFER_UAV_FLAG_RAW; + uavDesc.Format = DXGI_FORMAT_R32_TYPELESS; + } + + ComPtr<ID3D11UnorderedAccessView> uav; + SLANG_RETURN_ON_FAIL(m_device->CreateUnorderedAccessView(resourceImpl->m_buffer, &uavDesc, uav.writeRef())); + + RefPtr<UnorderedAccessViewImpl> viewImpl = new UnorderedAccessViewImpl(); + viewImpl->m_type = ResourceViewImpl::Type::UAV; + viewImpl->m_uav = uav; + *outView = viewImpl.detach(); + return SLANG_OK; + } + break; + + case ResourceView::Type::ShaderResource: + { + D3D11_SHADER_RESOURCE_VIEW_DESC srvDesc = {}; + srvDesc.ViewDimension = D3D11_SRV_DIMENSION_BUFFER; + srvDesc.Format = D3DUtil::getMapFormat(desc.format); + srvDesc.Buffer.ElementOffset = 0; + srvDesc.Buffer.ElementWidth = 1; + srvDesc.Buffer.FirstElement = 0; + srvDesc.Buffer.NumElements = resourceDesc.sizeInBytes; + + if(resourceDesc.elementSize) + { + srvDesc.Buffer.ElementWidth = resourceDesc.elementSize; + srvDesc.Buffer.NumElements = resourceDesc.sizeInBytes / resourceDesc.elementSize; + } + + ComPtr<ID3D11ShaderResourceView> srv; + SLANG_RETURN_ON_FAIL(m_device->CreateShaderResourceView(resourceImpl->m_buffer, &srvDesc, srv.writeRef())); + + RefPtr<ShaderResourceViewImpl> viewImpl = new ShaderResourceViewImpl(); + viewImpl->m_type = ResourceViewImpl::Type::SRV; + viewImpl->m_srv = srv; + *outView = viewImpl.detach(); + return SLANG_OK; + } + break; + } +} + +Result D3D11Renderer::createInputLayout(const InputElementDesc* inputElementsIn, UInt inputElementCount, InputLayout** outLayout) +{ + D3D11_INPUT_ELEMENT_DESC inputElements[16] = {}; + + char hlslBuffer[1024]; + char* hlslCursor = &hlslBuffer[0]; + + hlslCursor += sprintf(hlslCursor, "float4 main(\n"); + + for (UInt ii = 0; ii < inputElementCount; ++ii) + { + inputElements[ii].SemanticName = inputElementsIn[ii].semanticName; + inputElements[ii].SemanticIndex = (UINT)inputElementsIn[ii].semanticIndex; + inputElements[ii].Format = D3DUtil::getMapFormat(inputElementsIn[ii].format); + inputElements[ii].InputSlot = 0; + inputElements[ii].AlignedByteOffset = (UINT)inputElementsIn[ii].offset; + inputElements[ii].InputSlotClass = D3D11_INPUT_PER_VERTEX_DATA; + inputElements[ii].InstanceDataStepRate = 0; + + if (ii != 0) + { + hlslCursor += sprintf(hlslCursor, ",\n"); + } + + char const* typeName = "Unknown"; + switch (inputElementsIn[ii].format) + { + case Format::RGBA_Float32: + typeName = "float4"; + break; + case Format::RGB_Float32: + typeName = "float3"; + break; + case Format::RG_Float32: + typeName = "float2"; + break; + case Format::R_Float32: + typeName = "float"; + break; + default: + return SLANG_FAIL; + } + + hlslCursor += sprintf(hlslCursor, "%s a%d : %s%d", + typeName, + (int)ii, + inputElementsIn[ii].semanticName, + (int)inputElementsIn[ii].semanticIndex); + } + + hlslCursor += sprintf(hlslCursor, "\n) : SV_Position { return 0; }"); + + ComPtr<ID3DBlob> vertexShaderBlob; + SLANG_RETURN_ON_FAIL(D3DUtil::compileHLSLShader("inputLayout", hlslBuffer, "main", "vs_5_0", vertexShaderBlob)); + + ComPtr<ID3D11InputLayout> inputLayout; + SLANG_RETURN_ON_FAIL(m_device->CreateInputLayout(&inputElements[0], (UINT)inputElementCount, vertexShaderBlob->GetBufferPointer(), vertexShaderBlob->GetBufferSize(), + inputLayout.writeRef())); + + RefPtr<InputLayoutImpl> impl = new InputLayoutImpl; + impl->m_layout.swap(inputLayout); + + *outLayout = impl.detach(); + return SLANG_OK; +} + +void* D3D11Renderer::map(BufferResource* bufferIn, MapFlavor flavor) +{ + BufferResourceImpl* bufferResource = static_cast<BufferResourceImpl*>(bufferIn); + + D3D11_MAP mapType; + ID3D11Buffer* buffer = bufferResource->m_buffer; + + switch (flavor) + { + case MapFlavor::WriteDiscard: + mapType = D3D11_MAP_WRITE_DISCARD; + break; + case MapFlavor::HostWrite: + mapType = D3D11_MAP_WRITE; + break; + case MapFlavor::HostRead: + mapType = D3D11_MAP_READ; + + buffer = bufferResource->m_staging; + if (!buffer) + { + return nullptr; + } + + // Okay copy the data over + m_immediateContext->CopyResource(buffer, bufferResource->m_buffer); + + break; + default: + return nullptr; + } + + // We update our constant buffer per-frame, just for the purposes + // of the example, but we don't actually load different data + // per-frame (we always use an identity projection). + D3D11_MAPPED_SUBRESOURCE mappedSub; + SLANG_RETURN_NULL_ON_FAIL(m_immediateContext->Map(buffer, 0, mapType, 0, &mappedSub)); + + bufferResource->m_mapFlavor = flavor; + + return mappedSub.pData; +} + +void D3D11Renderer::unmap(BufferResource* bufferIn) +{ + BufferResourceImpl* bufferResource = static_cast<BufferResourceImpl*>(bufferIn); + ID3D11Buffer* buffer = (bufferResource->m_mapFlavor == MapFlavor::HostRead) ? bufferResource->m_staging : bufferResource->m_buffer; + m_immediateContext->Unmap(buffer, 0); +} + +#if 0 +void D3D11Renderer::setInputLayout(InputLayout* inputLayoutIn) +{ + auto inputLayout = static_cast<InputLayoutImpl*>(inputLayoutIn); + m_immediateContext->IASetInputLayout(inputLayout->m_layout); +} +#endif + +void D3D11Renderer::setPrimitiveTopology(PrimitiveTopology topology) +{ + m_immediateContext->IASetPrimitiveTopology(D3DUtil::getPrimitiveTopology(topology)); +} + +void D3D11Renderer::setVertexBuffers(UInt startSlot, UInt slotCount, BufferResource*const* buffersIn, const UInt* stridesIn, const UInt* offsetsIn) +{ + static const int kMaxVertexBuffers = 16; + assert(slotCount <= kMaxVertexBuffers); + + UINT vertexStrides[kMaxVertexBuffers]; + UINT vertexOffsets[kMaxVertexBuffers]; + ID3D11Buffer* dxBuffers[kMaxVertexBuffers]; + + auto buffers = (BufferResourceImpl*const*)buffersIn; + + for (UInt ii = 0; ii < slotCount; ++ii) + { + vertexStrides[ii] = (UINT)stridesIn[ii]; + vertexOffsets[ii] = (UINT)offsetsIn[ii]; + dxBuffers[ii] = buffers[ii]->m_buffer; + } + + m_immediateContext->IASetVertexBuffers((UINT)startSlot, (UINT)slotCount, dxBuffers, &vertexStrides[0], &vertexOffsets[0]); +} + +void D3D11Renderer::setIndexBuffer(BufferResource* buffer, Format indexFormat, UInt offset) +{ + DXGI_FORMAT dxFormat = D3DUtil::getMapFormat(indexFormat); + m_immediateContext->IASetIndexBuffer(((BufferResourceImpl*)buffer)->m_buffer, dxFormat, offset); +} + +void D3D11Renderer::setDepthStencilTarget(ResourceView* depthStencilView) +{ + m_dsvBinding = ((DepthStencilViewImpl*) depthStencilView)->m_dsv; + m_targetBindingsDirty[int(PipelineType::Graphics)] = true; +} + +void D3D11Renderer::setPipelineState(PipelineType pipelineType, PipelineState* state) +{ + switch(pipelineType) + { + default: + break; + + case PipelineType::Graphics: + { + auto stateImpl = (GraphicsPipelineStateImpl*) state; + auto programImpl = stateImpl->m_program; + + // TODO: We could conceivably do some lightweight state + // differencing here (e.g., check if `programImpl` is the + // same as the program that is currently bound). + // + // It isn't clear how much that would pay off given that + // the D3D11 runtime seems to do its own state diffing. + + // IA + + m_immediateContext->IASetInputLayout(stateImpl->m_inputLayout->m_layout); + + // VS + + m_immediateContext->VSSetShader(programImpl->m_vertexShader, nullptr, 0); + + // HS + + // DS + + // GS + + // RS + + m_immediateContext->RSSetState(stateImpl->m_rasterizerState); + + // PS + + m_immediateContext->PSSetShader(programImpl->m_pixelShader, nullptr, 0); + + // OM + + m_immediateContext->OMSetDepthStencilState(stateImpl->m_depthStencilState, stateImpl->m_stencilRef); + + m_currentGraphicsState = stateImpl; + } + break; + + case PipelineType::Compute: + { + auto stateImpl = (ComputePipelineStateImpl*) state; + auto programImpl = stateImpl->m_program; + + // CS + + m_immediateContext->CSSetShader(programImpl->m_computeShader, nullptr, 0); + + m_currentComputeState = stateImpl; + } + break; + } + + /// ... +} + +void D3D11Renderer::draw(UInt vertexCount, UInt startVertex) +{ + _flushGraphicsState(); + m_immediateContext->Draw((UINT)vertexCount, (UINT)startVertex); +} + +void D3D11Renderer::drawIndexed(UInt indexCount, UInt startIndex, UInt baseVertex) +{ + _flushGraphicsState(); + m_immediateContext->DrawIndexed((UINT)indexCount, (UINT)startIndex, (UInt)baseVertex); +} + +Result D3D11Renderer::createProgram(const ShaderProgram::Desc& desc, ShaderProgram** outProgram) +{ + if (desc.pipelineType == PipelineType::Compute) + { + auto computeKernel = desc.findKernel(StageType::Compute); + + ComPtr<ID3D11ComputeShader> computeShader; + SLANG_RETURN_ON_FAIL(m_device->CreateComputeShader(computeKernel->codeBegin, computeKernel->getCodeSize(), nullptr, computeShader.writeRef())); + + RefPtr<ShaderProgramImpl> shaderProgram = new ShaderProgramImpl(); + shaderProgram->m_computeShader.swap(computeShader); + + *outProgram = shaderProgram.detach(); + return SLANG_OK; + } + else + { + auto vertexKernel = desc.findKernel(StageType::Vertex); + auto fragmentKernel = desc.findKernel(StageType::Fragment); + + ComPtr<ID3D11VertexShader> vertexShader; + ComPtr<ID3D11PixelShader> pixelShader; + + SLANG_RETURN_ON_FAIL(m_device->CreateVertexShader(vertexKernel->codeBegin, vertexKernel->getCodeSize(), nullptr, vertexShader.writeRef())); + SLANG_RETURN_ON_FAIL(m_device->CreatePixelShader(fragmentKernel->codeBegin, fragmentKernel->getCodeSize(), nullptr, pixelShader.writeRef())); + + RefPtr<ShaderProgramImpl> shaderProgram = new ShaderProgramImpl(); + shaderProgram->m_vertexShader.swap(vertexShader); + shaderProgram->m_pixelShader.swap(pixelShader); + + *outProgram = shaderProgram.detach(); + return SLANG_OK; + } +} + +static D3D11_STENCIL_OP translateStencilOp(StencilOp op) +{ + switch(op) + { + default: + // TODO: need to report failures + return D3D11_STENCIL_OP_KEEP; + +#define CASE(FROM, TO) \ + case StencilOp::FROM: return D3D11_STENCIL_OP_##TO + + CASE(Keep, KEEP); + CASE(Zero, ZERO); + CASE(Replace, REPLACE); + CASE(IncrementSaturate, INCR_SAT); + CASE(DecrementSaturate, DECR_SAT); + CASE(Invert, INVERT); + CASE(IncrementWrap, INCR); + CASE(DecrementWrap, DECR); +#undef CASE + + } +} + +static D3D11_FILL_MODE translateFillMode(FillMode mode) +{ + switch(mode) + { + default: + // TODO: need to report failures + return D3D11_FILL_SOLID; + + case FillMode::Solid: return D3D11_FILL_SOLID; + case FillMode::Wireframe: return D3D11_FILL_WIREFRAME; + } +} + +static D3D11_CULL_MODE translateCullMode(CullMode mode) +{ + switch(mode) + { + default: + // TODO: need to report failures + return D3D11_CULL_NONE; + + case CullMode::None: return D3D11_CULL_NONE; + case CullMode::Back: return D3D11_CULL_BACK; + case CullMode::Front: return D3D11_CULL_FRONT; + } +} + +Result D3D11Renderer::createGraphicsPipelineState(const GraphicsPipelineStateDesc& desc, PipelineState** outState) +{ + auto programImpl = (ShaderProgramImpl*) desc.program; + + ComPtr<ID3D11DepthStencilState> depthStencilState; + { + D3D11_DEPTH_STENCIL_DESC dsDesc; + dsDesc.DepthEnable = desc.depthStencil.depthTestEnable; + dsDesc.DepthWriteMask = desc.depthStencil.depthWriteEnable ? D3D11_DEPTH_WRITE_MASK_ALL : D3D11_DEPTH_WRITE_MASK_ZERO; + dsDesc.DepthFunc = translateComparisonFunc(desc.depthStencil.depthFunc); + dsDesc.StencilEnable = desc.depthStencil.stencilEnable; + dsDesc.StencilReadMask = desc.depthStencil.stencilReadMask; + dsDesc.StencilWriteMask = desc.depthStencil.stencilWriteMask; + + #define FACE(DST, SRC) \ + dsDesc.DST.StencilFailOp = translateStencilOp( desc.depthStencil.SRC.stencilFailOp); \ + dsDesc.DST.StencilDepthFailOp = translateStencilOp( desc.depthStencil.SRC.stencilDepthFailOp); \ + dsDesc.DST.StencilPassOp = translateStencilOp( desc.depthStencil.SRC.stencilPassOp); \ + dsDesc.DST.StencilFunc = translateComparisonFunc(desc.depthStencil.SRC.stencilFunc); \ + /* end */ + + FACE(FrontFace, frontFace); + FACE(BackFace, backFace); + + SLANG_RETURN_ON_FAIL(m_device->CreateDepthStencilState( + &dsDesc, + depthStencilState.writeRef())); + } + + ComPtr<ID3D11RasterizerState> rasterizerState; + { + D3D11_RASTERIZER_DESC rsDesc; + rsDesc.FillMode = translateFillMode(desc.rasterizer.fillMode); + rsDesc.CullMode = translateCullMode(desc.rasterizer.cullMode); + rsDesc.FrontCounterClockwise = desc.rasterizer.frontFace == FrontFaceMode::Clockwise; + rsDesc.DepthBias = desc.rasterizer.depthBias; + rsDesc.DepthBiasClamp = desc.rasterizer.depthBiasClamp; + rsDesc.SlopeScaledDepthBias = desc.rasterizer.slopeScaledDepthBias; + rsDesc.DepthClipEnable = desc.rasterizer.depthClipEnable; + rsDesc.ScissorEnable = desc.rasterizer.scissorEnable; + rsDesc.MultisampleEnable = desc.rasterizer.multisampleEnable; + rsDesc.AntialiasedLineEnable = desc.rasterizer.antialiasedLineEnable; + + SLANG_RETURN_ON_FAIL(m_device->CreateRasterizerState( + &rsDesc, + rasterizerState.writeRef())); + + } + + RefPtr<GraphicsPipelineStateImpl> state = new GraphicsPipelineStateImpl(); + state->m_program = programImpl; + state->m_stencilRef = desc.depthStencil.stencilRef; + state->m_depthStencilState = depthStencilState; + state->m_rasterizerState = rasterizerState; + state->m_pipelineLayout = (PipelineLayoutImpl*) desc.pipelineLayout; + state->m_inputLayout = (InputLayoutImpl*) desc.inputLayout; + state->m_rtvCount = desc.renderTargetCount; + + *outState = state.detach(); + return SLANG_OK; +} + +Result D3D11Renderer::createComputePipelineState(const ComputePipelineStateDesc& desc, PipelineState** outState) +{ + auto programImpl = (ShaderProgramImpl*) desc.program; + auto pipelineLayoutImpl = (PipelineLayoutImpl*) desc.pipelineLayout; + + RefPtr<ComputePipelineStateImpl> state = new ComputePipelineStateImpl(); + state->m_program = programImpl; + state->m_pipelineLayout = pipelineLayoutImpl; + + *outState = state.detach(); + return SLANG_OK; +} + +void D3D11Renderer::dispatchCompute(int x, int y, int z) +{ + _flushComputeState(); + m_immediateContext->Dispatch(x, y, z); +} + +Result D3D11Renderer::createDescriptorSetLayout(const DescriptorSetLayout::Desc& desc, DescriptorSetLayout** outLayout) +{ + RefPtr<DescriptorSetLayoutImpl> descriptorSetLayoutImpl = new DescriptorSetLayoutImpl(); + + UInt counts[int(D3D11DescriptorSlotType::CountOf)] = { 0, }; + + UInt rangeCount = desc.slotRangeCount; + for(UInt rr = 0; rr < rangeCount; ++rr) + { + auto rangeDesc = desc.slotRanges[rr]; + + DescriptorSetLayoutImpl::RangeInfo rangeInfo; + + switch(rangeDesc.type) + { + default: + assert(!"invalid slot type"); + return SLANG_FAIL; + + case DescriptorSlotType::Sampler: + rangeInfo.type = D3D11DescriptorSlotType::Sampler; + break; + + case DescriptorSlotType::CombinedImageSampler: + rangeInfo.type = D3D11DescriptorSlotType::CombinedTextureSampler; + break; + + case DescriptorSlotType::UniformBuffer: + case DescriptorSlotType::DynamicUniformBuffer: + rangeInfo.type = D3D11DescriptorSlotType::ConstantBuffer; + break; + + case DescriptorSlotType::SampledImage: + case DescriptorSlotType::UniformTexelBuffer: + case DescriptorSlotType::InputAttachment: + rangeInfo.type = D3D11DescriptorSlotType::ShaderResourceView; + break; + + case DescriptorSlotType::StorageImage: + case DescriptorSlotType::StorageTexelBuffer: + case DescriptorSlotType::StorageBuffer: + case DescriptorSlotType::DynamicStorageBuffer: + rangeInfo.type = D3D11DescriptorSlotType::UnorderedAccessView; + break; + } + + if(rangeInfo.type == D3D11DescriptorSlotType::CombinedTextureSampler) + { + auto srvTypeIndex = int(D3D11DescriptorSlotType::ShaderResourceView); + auto samplerTypeIndex = int(D3D11DescriptorSlotType::Sampler); + + rangeInfo.arrayIndex = counts[srvTypeIndex]; + rangeInfo.pairedSamplerArrayIndex = counts[samplerTypeIndex]; + + counts[srvTypeIndex] += rangeDesc.count; + counts[samplerTypeIndex] += rangeDesc.count; + } + else + { + auto typeIndex = int(rangeInfo.type); + + rangeInfo.arrayIndex = counts[typeIndex]; + counts[typeIndex] += rangeDesc.count; + } + descriptorSetLayoutImpl->m_ranges.Add(rangeInfo); + } + + for(int ii = 0; ii < int(D3D11DescriptorSlotType::CountOf); ++ii) + { + descriptorSetLayoutImpl->m_counts[ii] = counts[ii]; + } + + *outLayout = descriptorSetLayoutImpl.detach(); + return SLANG_OK; +} + +Result D3D11Renderer::createPipelineLayout(const PipelineLayout::Desc& desc, PipelineLayout** outLayout) +{ + RefPtr<PipelineLayoutImpl> pipelineLayoutImpl = new PipelineLayoutImpl(); + + UInt counts[int(D3D11DescriptorSlotType::CountOf)] = { 0, }; + + UInt setCount = desc.descriptorSetCount; + for(UInt ii = 0; ii < setCount; ++ii) + { + auto setDesc = desc.descriptorSets[ii]; + PipelineLayoutImpl::DescriptorSetInfo setInfo; + + setInfo.layout = (DescriptorSetLayoutImpl*) setDesc.layout; + + for(int jj = 0; jj < int(D3D11DescriptorSlotType::CountOf); ++jj) + { + setInfo.baseIndices[jj] = counts[jj]; + counts[jj] += setInfo.layout->m_counts[jj]; + } + + pipelineLayoutImpl->m_descriptorSets.Add(setInfo); + } + + pipelineLayoutImpl->m_uavCount = counts[int(D3D11DescriptorSlotType::UnorderedAccessView)]; + + *outLayout = pipelineLayoutImpl.detach(); + return SLANG_OK; +} + +Result D3D11Renderer::createDescriptorSet(DescriptorSetLayout* layout, DescriptorSet** outDescriptorSet) +{ + auto layoutImpl = (DescriptorSetLayoutImpl*)layout; + + RefPtr<DescriptorSetImpl> descriptorSetImpl = new DescriptorSetImpl(); + + descriptorSetImpl->m_layout = layoutImpl; + descriptorSetImpl->m_cbs .SetSize(layoutImpl->m_counts[int(D3D11DescriptorSlotType::ConstantBuffer)]); + descriptorSetImpl->m_srvs .SetSize(layoutImpl->m_counts[int(D3D11DescriptorSlotType::ShaderResourceView)]); + descriptorSetImpl->m_uavs .SetSize(layoutImpl->m_counts[int(D3D11DescriptorSlotType::UnorderedAccessView)]); + descriptorSetImpl->m_samplers.SetSize(layoutImpl->m_counts[int(D3D11DescriptorSlotType::Sampler)]); + + *outDescriptorSet = descriptorSetImpl.detach(); + return SLANG_OK; +} + + +#if 0 +BindingState* D3D11Renderer::createBindingState(const BindingState::Desc& bindingStateDesc) +{ + RefPtr<BindingStateImpl> 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<BufferResourceImpl*>(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<TextureResourceImpl*>(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<BufferResourceImpl*>(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.Buffer()->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<BindingStateImpl*>(state); +} +#endif + +void D3D11Renderer::_flushGraphicsState() +{ + auto pipelineType = int(PipelineType::Graphics); + if(m_targetBindingsDirty[pipelineType]) + { + m_targetBindingsDirty[pipelineType] = false; + + auto pipelineState = m_currentGraphicsState.Ptr(); + + auto rtvCount = pipelineState->m_rtvCount; + auto uavCount = pipelineState->m_pipelineLayout->m_uavCount; + + m_immediateContext->OMSetRenderTargetsAndUnorderedAccessViews( + rtvCount, + m_rtvBindings[0].readRef(), + m_dsvBinding, + rtvCount, + uavCount, + m_uavBindings[pipelineType][0].readRef(), + nullptr); + } +} + +void D3D11Renderer::_flushComputeState() +{ + auto pipelineType = int(PipelineType::Compute); + if(m_targetBindingsDirty[pipelineType]) + { + m_targetBindingsDirty[pipelineType] = false; + + auto pipelineState = m_currentComputeState.Ptr(); + + auto uavCount = pipelineState->m_pipelineLayout->m_uavCount; + + m_immediateContext->CSSetUnorderedAccessViews( + 0, + uavCount, + m_uavBindings[pipelineType][0].readRef(), + nullptr); + } +} + +void D3D11Renderer::DescriptorSetImpl::setConstantBuffer(UInt range, UInt index, BufferResource* buffer) +{ + auto bufferImpl = (BufferResourceImpl*) buffer; + auto& rangeInfo = m_layout->m_ranges[range]; + + assert(rangeInfo.type == D3D11DescriptorSlotType::ConstantBuffer); + + m_cbs[rangeInfo.arrayIndex + index] = bufferImpl->m_buffer; +} + +void D3D11Renderer::DescriptorSetImpl::setResource(UInt range, UInt index, ResourceView* view) +{ + auto viewImpl = (ResourceViewImpl*)view; + auto& rangeInfo = m_layout->m_ranges[range]; + + 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; + } + break; + + case D3D11DescriptorSlotType::UnorderedAccessView: + { + assert(viewImpl->m_type == ResourceViewImpl::Type::UAV); + auto uavImpl = (UnorderedAccessViewImpl*)viewImpl; + m_uavs[rangeInfo.arrayIndex + index] = uavImpl->m_uav; + } + break; + + default: + assert(!"invalid to bind a resource view to this descriptor range"); + break; + } +} + +void D3D11Renderer::DescriptorSetImpl::setSampler(UInt range, UInt index, SamplerState* sampler) +{ + auto samplerImpl = (SamplerStateImpl*) sampler; + auto& rangeInfo = m_layout->m_ranges[range]; + + assert(rangeInfo.type == D3D11DescriptorSlotType::Sampler); + + m_samplers[rangeInfo.arrayIndex + index] = samplerImpl->m_sampler; +} + +void D3D11Renderer::DescriptorSetImpl::setCombinedTextureSampler( + UInt range, + UInt index, + ResourceView* textureView, + SamplerState* sampler) +{ + auto viewImpl = (ResourceViewImpl*) textureView; + auto samplerImpl = (SamplerStateImpl*)sampler; + + auto& rangeInfo = m_layout->m_ranges[range]; + assert(rangeInfo.type == D3D11DescriptorSlotType::CombinedTextureSampler); + + assert(viewImpl->m_type == ResourceViewImpl::Type::SRV); + auto srvImpl = (ShaderResourceViewImpl*)viewImpl; + m_srvs[rangeInfo.arrayIndex + index] = srvImpl->m_srv; + + m_samplers[rangeInfo.arrayIndex + index] = samplerImpl->m_sampler; + + // TODO: need a place to bind the matching sampler... + m_srvs[rangeInfo.pairedSamplerArrayIndex + index] = srvImpl->m_srv; +} + +void D3D11Renderer::setDescriptorSet(PipelineType pipelineType, PipelineLayout* layout, UInt index, DescriptorSet* descriptorSet) +{ + auto pipelineLayoutImpl = (PipelineLayoutImpl*)layout; + auto descriptorSetImpl = (DescriptorSetImpl*) descriptorSet; + + auto descriptorSetLayoutImpl = descriptorSetImpl->m_layout; + auto& setInfo = pipelineLayoutImpl->m_descriptorSets[index]; + + // Note: `setInfo->layout` and `descriptorSetLayoutImpl` need to be compatible + + // TODO: If/when we add per-stage visibility masks, it would be best to organize + // this as a loop over stages, so that we only do the binding that is required + // for each stage. + + { + int slotType = int(D3D11DescriptorSlotType::ConstantBuffer); + UInt slotCount = setInfo.layout->m_counts[slotType]; + if(slotCount) + { + UInt startSlot = setInfo.baseIndices[slotType]; + + auto cbs = descriptorSetImpl->m_cbs[0].readRef(); + + m_immediateContext->VSSetConstantBuffers(startSlot, slotCount, cbs); + // ... + m_immediateContext->PSSetConstantBuffers(startSlot, slotCount, cbs); + + m_immediateContext->CSSetConstantBuffers(startSlot, slotCount, cbs); + } + } + + { + int slotType = int(D3D11DescriptorSlotType::ShaderResourceView); + UInt slotCount = setInfo.layout->m_counts[slotType]; + if(slotCount) + { + UInt startSlot = setInfo.baseIndices[slotType]; + + auto srvs = descriptorSetImpl->m_srvs[0].readRef(); + + m_immediateContext->VSSetShaderResources(startSlot, slotCount, srvs); + // ... + m_immediateContext->PSSetShaderResources(startSlot, slotCount, srvs); + + m_immediateContext->CSSetShaderResources(startSlot, slotCount, srvs); + } + } + + { + int slotType = int(D3D11DescriptorSlotType::Sampler); + UInt slotCount = setInfo.layout->m_counts[slotType]; + if(slotCount) + { + UInt startSlot = setInfo.baseIndices[slotType]; + + auto samplers = descriptorSetImpl->m_samplers[0].readRef(); + + m_immediateContext->VSSetSamplers(startSlot, slotCount, samplers); + // ... + m_immediateContext->PSSetSamplers(startSlot, slotCount, samplers); + + m_immediateContext->CSSetSamplers(startSlot, slotCount, samplers); + } + } + + { + // Note: UAVs are handled differently from other bindings, because + // D3D11 requires all UAVs to be set with a single call, rather + // than allowing incremental updates. We will therefore shadow + // the UAV bindings with `m_uavBindings` and then flush them + // as needed right before a draw/dispatch. + // + int slotType = int(D3D11DescriptorSlotType::UnorderedAccessView); + UInt slotCount = setInfo.layout->m_counts[slotType]; + if(slotCount) + { + UInt startSlot = setInfo.baseIndices[slotType]; + + auto uavs = descriptorSetImpl->m_uavs[0].readRef(); + + for(UINT ii = 0; ii < slotCount; ++ii) + { + m_uavBindings[int(pipelineType)][startSlot + ii] = uavs[ii]; + } + m_targetBindingsDirty[int(pipelineType)] = true; + } + } + + +} + +} // renderer_test |
