From 38ad2d5cb659074ad9b4bde16bcbf71481719b94 Mon Sep 17 00:00:00 2001 From: assiduous Date: Sun, 26 Jan 2020 13:44:37 -0800 Subject: Renamed .h -> .hpp in GraphicsEngine, GraphicsEngineD3DBase, GraphicsEningeD3D11, and GraphicsEningeD3D12 projects --- Graphics/GraphicsEngine/CMakeLists.txt | 38 +- Graphics/GraphicsEngine/include/BufferBase.h | 282 ---- Graphics/GraphicsEngine/include/BufferBase.hpp | 282 ++++ Graphics/GraphicsEngine/include/BufferViewBase.h | 108 -- Graphics/GraphicsEngine/include/BufferViewBase.hpp | 108 ++ Graphics/GraphicsEngine/include/CommandListBase.h | 72 - .../GraphicsEngine/include/CommandListBase.hpp | 72 + .../GraphicsEngine/include/DeviceContextBase.h | 1518 -------------------- .../GraphicsEngine/include/DeviceContextBase.hpp | 1518 ++++++++++++++++++++ Graphics/GraphicsEngine/include/DeviceObjectBase.h | 178 --- .../GraphicsEngine/include/DeviceObjectBase.hpp | 178 +++ .../GraphicsEngine/include/EngineFactoryBase.h | 155 -- .../GraphicsEngine/include/EngineFactoryBase.hpp | 155 ++ Graphics/GraphicsEngine/include/FenceBase.h | 71 - Graphics/GraphicsEngine/include/FenceBase.hpp | 71 + .../GraphicsEngine/include/PipelineStateBase.h | 390 ----- .../GraphicsEngine/include/PipelineStateBase.hpp | 390 +++++ Graphics/GraphicsEngine/include/QueryBase.h | 243 ---- Graphics/GraphicsEngine/include/QueryBase.hpp | 243 ++++ Graphics/GraphicsEngine/include/RenderDeviceBase.h | 502 ------- .../GraphicsEngine/include/RenderDeviceBase.hpp | 502 +++++++ .../GraphicsEngine/include/ResourceMappingImpl.h | 136 -- .../GraphicsEngine/include/ResourceMappingImpl.hpp | 136 ++ Graphics/GraphicsEngine/include/SamplerBase.h | 74 - Graphics/GraphicsEngine/include/SamplerBase.hpp | 74 + Graphics/GraphicsEngine/include/ShaderBase.h | 122 -- Graphics/GraphicsEngine/include/ShaderBase.hpp | 122 ++ .../include/ShaderResourceBindingBase.h | 90 -- .../include/ShaderResourceBindingBase.hpp | 90 ++ .../include/ShaderResourceVariableBase.h | 379 ----- .../include/ShaderResourceVariableBase.hpp | 379 +++++ .../GraphicsEngine/include/StateObjectsRegistry.h | 213 --- .../include/StateObjectsRegistry.hpp | 213 +++ Graphics/GraphicsEngine/include/SwapChainBase.h | 118 -- Graphics/GraphicsEngine/include/SwapChainBase.hpp | 118 ++ Graphics/GraphicsEngine/include/TextureBase.h | 515 ------- Graphics/GraphicsEngine/include/TextureBase.hpp | 515 +++++++ Graphics/GraphicsEngine/include/TextureViewBase.h | 135 -- .../GraphicsEngine/include/TextureViewBase.hpp | 135 ++ Graphics/GraphicsEngine/include/pch.h | 4 +- Graphics/GraphicsEngine/src/ResourceMapping.cpp | 4 +- 41 files changed, 5324 insertions(+), 5324 deletions(-) delete mode 100644 Graphics/GraphicsEngine/include/BufferBase.h create mode 100644 Graphics/GraphicsEngine/include/BufferBase.hpp delete mode 100644 Graphics/GraphicsEngine/include/BufferViewBase.h create mode 100644 Graphics/GraphicsEngine/include/BufferViewBase.hpp delete mode 100644 Graphics/GraphicsEngine/include/CommandListBase.h create mode 100644 Graphics/GraphicsEngine/include/CommandListBase.hpp delete mode 100644 Graphics/GraphicsEngine/include/DeviceContextBase.h create mode 100644 Graphics/GraphicsEngine/include/DeviceContextBase.hpp delete mode 100644 Graphics/GraphicsEngine/include/DeviceObjectBase.h create mode 100644 Graphics/GraphicsEngine/include/DeviceObjectBase.hpp delete mode 100644 Graphics/GraphicsEngine/include/EngineFactoryBase.h create mode 100644 Graphics/GraphicsEngine/include/EngineFactoryBase.hpp delete mode 100644 Graphics/GraphicsEngine/include/FenceBase.h create mode 100644 Graphics/GraphicsEngine/include/FenceBase.hpp delete mode 100644 Graphics/GraphicsEngine/include/PipelineStateBase.h create mode 100644 Graphics/GraphicsEngine/include/PipelineStateBase.hpp delete mode 100644 Graphics/GraphicsEngine/include/QueryBase.h create mode 100644 Graphics/GraphicsEngine/include/QueryBase.hpp delete mode 100644 Graphics/GraphicsEngine/include/RenderDeviceBase.h create mode 100644 Graphics/GraphicsEngine/include/RenderDeviceBase.hpp delete mode 100644 Graphics/GraphicsEngine/include/ResourceMappingImpl.h create mode 100644 Graphics/GraphicsEngine/include/ResourceMappingImpl.hpp delete mode 100644 Graphics/GraphicsEngine/include/SamplerBase.h create mode 100644 Graphics/GraphicsEngine/include/SamplerBase.hpp delete mode 100644 Graphics/GraphicsEngine/include/ShaderBase.h create mode 100644 Graphics/GraphicsEngine/include/ShaderBase.hpp delete mode 100644 Graphics/GraphicsEngine/include/ShaderResourceBindingBase.h create mode 100644 Graphics/GraphicsEngine/include/ShaderResourceBindingBase.hpp delete mode 100644 Graphics/GraphicsEngine/include/ShaderResourceVariableBase.h create mode 100644 Graphics/GraphicsEngine/include/ShaderResourceVariableBase.hpp delete mode 100644 Graphics/GraphicsEngine/include/StateObjectsRegistry.h create mode 100644 Graphics/GraphicsEngine/include/StateObjectsRegistry.hpp delete mode 100644 Graphics/GraphicsEngine/include/SwapChainBase.h create mode 100644 Graphics/GraphicsEngine/include/SwapChainBase.hpp delete mode 100644 Graphics/GraphicsEngine/include/TextureBase.h create mode 100644 Graphics/GraphicsEngine/include/TextureBase.hpp delete mode 100644 Graphics/GraphicsEngine/include/TextureViewBase.h create mode 100644 Graphics/GraphicsEngine/include/TextureViewBase.hpp (limited to 'Graphics/GraphicsEngine') diff --git a/Graphics/GraphicsEngine/CMakeLists.txt b/Graphics/GraphicsEngine/CMakeLists.txt index f28c3db1..037af6fa 100644 --- a/Graphics/GraphicsEngine/CMakeLists.txt +++ b/Graphics/GraphicsEngine/CMakeLists.txt @@ -3,29 +3,29 @@ cmake_minimum_required (VERSION 3.6) project(Diligent-GraphicsEngine CXX) set(INCLUDE - include/BufferBase.h - include/BufferViewBase.h - include/CommandListBase.h + include/BufferBase.hpp + include/BufferViewBase.hpp + include/CommandListBase.hpp include/DefaultShaderSourceStreamFactory.h include/Defines.h - include/DeviceContextBase.h - include/DeviceObjectBase.h - include/EngineFactoryBase.h + include/DeviceContextBase.hpp + include/DeviceObjectBase.hpp + include/EngineFactoryBase.hpp include/EngineMemory.h - include/FenceBase.h + include/FenceBase.hpp include/pch.h - include/PipelineStateBase.h - include/QueryBase.h - include/RenderDeviceBase.h - include/ResourceMappingImpl.h - include/SamplerBase.h - include/ShaderBase.h - include/ShaderResourceBindingBase.h - include/ShaderResourceVariableBase.h - include/StateObjectsRegistry.h - include/SwapChainBase.h - include/TextureBase.h - include/TextureViewBase.h + include/PipelineStateBase.hpp + include/QueryBase.hpp + include/RenderDeviceBase.hpp + include/ResourceMappingImpl.hpp + include/SamplerBase.hpp + include/ShaderBase.hpp + include/ShaderResourceBindingBase.hpp + include/ShaderResourceVariableBase.hpp + include/StateObjectsRegistry.hpp + include/SwapChainBase.hpp + include/TextureBase.hpp + include/TextureViewBase.hpp ) set(INTERFACE diff --git a/Graphics/GraphicsEngine/include/BufferBase.h b/Graphics/GraphicsEngine/include/BufferBase.h deleted file mode 100644 index 388625b1..00000000 --- a/Graphics/GraphicsEngine/include/BufferBase.h +++ /dev/null @@ -1,282 +0,0 @@ -/* - * Copyright 2019-2020 Diligent Graphics LLC - * Copyright 2015-2019 Egor Yusov - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - * - * In no event and under no legal theory, whether in tort (including negligence), - * contract, or otherwise, unless required by applicable law (such as deliberate - * and grossly negligent acts) or agreed to in writing, shall any Contributor be - * liable for any damages, including any direct, indirect, special, incidental, - * or consequential damages of any character arising as a result of this License or - * out of the use or inability to use the software (including but not limited to damages - * for loss of goodwill, work stoppage, computer failure or malfunction, or any and - * all other commercial damages or losses), even if such Contributor has been advised - * of the possibility of such damages. - */ - -#pragma once - -/// \file -/// Implementation of the Diligent::BufferBase template class - -#include "Buffer.h" -#include "GraphicsTypes.h" -#include "DeviceObjectBase.h" -#include "GraphicsAccessories.hpp" -#include "STDAllocator.h" -#include - -namespace Diligent -{ - -/// Template class implementing base functionality for a buffer object - -/// \tparam BaseInterface - base interface that this class will inheret -/// (Diligent::IBufferD3D11, Diligent::IBufferD3D12, -/// Diligent::IBufferGL or Diligent::IBufferVk). -/// \tparam RenderDeviceImplType - type of the render device implementation -/// (Diligent::RenderDeviceD3D11Impl, Diligent::RenderDeviceD3D12Impl, -/// Diligent::RenderDeviceGLImpl, or Diligent::RenderDeviceVkImpl) -/// \tparam BufferViewImplType - type of the buffer view implementation -/// (Diligent::BufferViewD3D11Impl, Diligent::BufferViewD3D12Impl, -/// Diligent::BufferViewGLImpl or Diligent::BufferViewVkImpl) -/// \tparam TBuffViewObjAllocator - type of the allocator that is used to allocate memory for the buffer view object instances -template -class BufferBase : public DeviceObjectBase -{ -public: - using TDeviceObjectBase = DeviceObjectBase; - - /// \param pRefCounters - reference counters object that controls the lifetime of this buffer. - /// \param BuffViewObjAllocator - allocator that is used to allocate memory for the buffer view instances. - /// This parameter is only used for debug purposes. - /// \param pDevice - pointer to the device. - /// \param BuffDesc - buffer description. - /// \param bIsDeviceInternal - flag indicating if the buffer is an internal device object and - /// must not keep a strong reference to the device. - BufferBase(IReferenceCounters* pRefCounters, - TBuffViewObjAllocator& BuffViewObjAllocator, - RenderDeviceImplType* pDevice, - const BufferDesc& BuffDesc, - bool bIsDeviceInternal) : - TDeviceObjectBase{pRefCounters, pDevice, BuffDesc, bIsDeviceInternal}, -#ifdef _DEBUG - m_dbgBuffViewAllocator{BuffViewObjAllocator}, -#endif - m_pDefaultUAV{nullptr, STDDeleter(BuffViewObjAllocator)}, - m_pDefaultSRV{nullptr, STDDeleter(BuffViewObjAllocator)} - { -#define VERIFY_BUFFER(Expr, ...) \ - do \ - { \ - if (!(Expr)) \ - { \ - LOG_ERROR_AND_THROW("Buffer '", this->m_Desc.Name ? this->m_Desc.Name : "", "': ", ##__VA_ARGS__); \ - } \ - } while (false) - - Uint32 AllowedBindFlags = - BIND_VERTEX_BUFFER | BIND_INDEX_BUFFER | BIND_UNIFORM_BUFFER | - BIND_SHADER_RESOURCE | BIND_STREAM_OUTPUT | BIND_UNORDERED_ACCESS | - BIND_INDIRECT_DRAW_ARGS; - const Char* strAllowedBindFlags = - "BIND_VERTEX_BUFFER (1), BIND_INDEX_BUFFER (2), BIND_UNIFORM_BUFFER (4), " - "BIND_SHADER_RESOURCE (8), BIND_STREAM_OUTPUT (16), BIND_UNORDERED_ACCESS (128), " - "BIND_INDIRECT_DRAW_ARGS (256)"; - - VERIFY_BUFFER((BuffDesc.BindFlags & ~AllowedBindFlags) == 0, "Incorrect bind flags specified (", BuffDesc.BindFlags & ~AllowedBindFlags, "). Only the following flags are allowed:\n", strAllowedBindFlags); - - if ((this->m_Desc.BindFlags & BIND_UNORDERED_ACCESS) || - (this->m_Desc.BindFlags & BIND_SHADER_RESOURCE)) - { - VERIFY_BUFFER(this->m_Desc.Mode > BUFFER_MODE_UNDEFINED && this->m_Desc.Mode < BUFFER_MODE_NUM_MODES, GetBufferModeString(this->m_Desc.Mode), " is not a valid mode for a buffer created with BIND_SHADER_RESOURCE or BIND_UNORDERED_ACCESS flags"); - if (this->m_Desc.Mode == BUFFER_MODE_STRUCTURED || this->m_Desc.Mode == BUFFER_MODE_FORMATTED) - { - VERIFY_BUFFER(this->m_Desc.ElementByteStride != 0, "Element stride must not be zero for structured and formatted buffers"); - } - else if (this->m_Desc.Mode == BUFFER_MODE_RAW) - { - } - } - -#undef VERIFY_BUFFER - - Uint64 DeviceQueuesMask = pDevice->GetCommandQueueMask(); - DEV_CHECK_ERR((this->m_Desc.CommandQueueMask & DeviceQueuesMask) != 0, "No bits in the command queue mask (0x", std::hex, this->m_Desc.CommandQueueMask, ") correspond to one of ", pDevice->GetCommandQueueCount(), " available device command queues"); - this->m_Desc.CommandQueueMask &= DeviceQueuesMask; - } - - IMPLEMENT_QUERY_INTERFACE_IN_PLACE(IID_Buffer, TDeviceObjectBase) - - /// Implementation of IBuffer::CreateView(); calls CreateViewInternal() virtual function - /// that creates buffer view for the specific engine implementation. - virtual void CreateView(const struct BufferViewDesc& ViewDesc, IBufferView** ppView) override; - - /// Implementation of IBuffer::GetDefaultView(). - virtual IBufferView* GetDefaultView(BUFFER_VIEW_TYPE ViewType) override; - - /// Creates default buffer views. - - /// - /// - Creates default shader resource view addressing the entire buffer if Diligent::BIND_SHADER_RESOURCE flag is set - /// - Creates default unordered access view addressing the entire buffer if Diligent::BIND_UNORDERED_ACCESS flag is set - /// - /// The function calls CreateViewInternal(). - void CreateDefaultViews(); - - virtual void SetState(RESOURCE_STATE State) override final - { - this->m_State = State; - } - - virtual RESOURCE_STATE GetState() const override final - { - return this->m_State; - } - - bool IsInKnownState() const - { - return this->m_State != RESOURCE_STATE_UNKNOWN; - } - - bool CheckState(RESOURCE_STATE State) const - { - VERIFY((State & (State - 1)) == 0, "Single state is expected"); - VERIFY(IsInKnownState(), "Buffer state is unknown"); - return (this->m_State & State) == State; - } - -protected: - /// Pure virtual function that creates buffer view for the specific engine implementation. - virtual void CreateViewInternal(const struct BufferViewDesc& ViewDesc, IBufferView** ppView, bool bIsDefaultView) = 0; - - /// Corrects buffer view description and validates view parameters. - void CorrectBufferViewDesc(struct BufferViewDesc& ViewDesc); - -#ifdef _DEBUG - TBuffViewObjAllocator& m_dbgBuffViewAllocator; -#endif - - RESOURCE_STATE m_State = RESOURCE_STATE_UNKNOWN; - - /// Default UAV addressing the entire buffer - std::unique_ptr> m_pDefaultUAV; - - /// Default SRV addressing the entire buffer - std::unique_ptr> m_pDefaultSRV; -}; - - - - -template -void BufferBase::CreateView(const struct BufferViewDesc& ViewDesc, IBufferView** ppView) -{ - DEV_CHECK_ERR(ViewDesc.ViewType != BUFFER_VIEW_UNDEFINED, "Buffer view type is not specified"); - if (ViewDesc.ViewType == BUFFER_VIEW_SHADER_RESOURCE) - DEV_CHECK_ERR(this->m_Desc.BindFlags & BIND_SHADER_RESOURCE, "Attempting to create SRV for buffer '", this->m_Desc.Name, "' that was not created with BIND_SHADER_RESOURCE flag"); - else if (ViewDesc.ViewType == BUFFER_VIEW_UNORDERED_ACCESS) - DEV_CHECK_ERR(this->m_Desc.BindFlags & BIND_UNORDERED_ACCESS, "Attempting to create UAV for buffer '", this->m_Desc.Name, "' that was not created with BIND_UNORDERED_ACCESS flag"); - else - UNEXPECTED("Unexpected buffer view type"); - - CreateViewInternal(ViewDesc, ppView, false); -} - - -template -void BufferBase::CorrectBufferViewDesc(struct BufferViewDesc& ViewDesc) -{ - if (ViewDesc.ByteWidth == 0) - { - DEV_CHECK_ERR(this->m_Desc.uiSizeInBytes > ViewDesc.ByteOffset, "Byte offset (", ViewDesc.ByteOffset, ") exceeds buffer size (", this->m_Desc.uiSizeInBytes, ")"); - ViewDesc.ByteWidth = this->m_Desc.uiSizeInBytes - ViewDesc.ByteOffset; - } - if (ViewDesc.ByteOffset + ViewDesc.ByteWidth > this->m_Desc.uiSizeInBytes) - LOG_ERROR_AND_THROW("Buffer view range [", ViewDesc.ByteOffset, ", ", ViewDesc.ByteOffset + ViewDesc.ByteWidth, ") is out of the buffer boundaries [0, ", this->m_Desc.uiSizeInBytes, ")."); - if ((this->m_Desc.BindFlags & BIND_UNORDERED_ACCESS) || - (this->m_Desc.BindFlags & BIND_SHADER_RESOURCE)) - { - if (this->m_Desc.Mode == BUFFER_MODE_STRUCTURED || this->m_Desc.Mode == BUFFER_MODE_FORMATTED) - { - VERIFY(this->m_Desc.ElementByteStride != 0, "Element byte stride is zero"); - if ((ViewDesc.ByteOffset % this->m_Desc.ElementByteStride) != 0) - LOG_ERROR_AND_THROW("Buffer view byte offset (", ViewDesc.ByteOffset, ") is not multiple of element byte stride (", this->m_Desc.ElementByteStride, ")."); - if ((ViewDesc.ByteWidth % this->m_Desc.ElementByteStride) != 0) - LOG_ERROR_AND_THROW("Buffer view byte width (", ViewDesc.ByteWidth, ") is not multiple of element byte stride (", this->m_Desc.ElementByteStride, ")."); - } - - if (this->m_Desc.Mode == BUFFER_MODE_FORMATTED && ViewDesc.Format.ValueType == VT_UNDEFINED) - LOG_ERROR_AND_THROW("Format must be specified when creating a view of a formatted buffer"); - - if (this->m_Desc.Mode == BUFFER_MODE_FORMATTED || (this->m_Desc.Mode == BUFFER_MODE_RAW && ViewDesc.Format.ValueType != VT_UNDEFINED)) - { - if (ViewDesc.Format.NumComponents <= 0 || ViewDesc.Format.NumComponents > 4) - LOG_ERROR_AND_THROW("Incorrect number of components (", Uint32{ViewDesc.Format.NumComponents}, "). 1, 2, 3, or 4 are allowed values"); - if (ViewDesc.Format.ValueType == VT_FLOAT32 || ViewDesc.Format.ValueType == VT_FLOAT16) - ViewDesc.Format.IsNormalized = false; - auto ViewElementStride = GetValueSize(ViewDesc.Format.ValueType) * Uint32{ViewDesc.Format.NumComponents}; - if (this->m_Desc.Mode == BUFFER_MODE_RAW && this->m_Desc.ElementByteStride == 0) - LOG_ERROR_AND_THROW("To enable formatted views of a raw buffer, element byte must be specified during buffer initialization"); - if (ViewElementStride != this->m_Desc.ElementByteStride) - LOG_ERROR_AND_THROW("Buffer element byte stride (", this->m_Desc.ElementByteStride, ") is not consistent with the size (", ViewElementStride, ") defined by the format of the view (", GetBufferFormatString(ViewDesc.Format), ')'); - } - - if (this->m_Desc.Mode == BUFFER_MODE_RAW && ViewDesc.Format.ValueType == VT_UNDEFINED) - { - if ((ViewDesc.ByteOffset % 16) != 0) - LOG_ERROR_AND_THROW("When creating a RAW view, the offset of the first element from the start of the buffer (", ViewDesc.ByteOffset, ") must be a multiple of 16 bytes"); - } - } -} - -template -IBufferView* BufferBase::GetDefaultView(BUFFER_VIEW_TYPE ViewType) -{ - switch (ViewType) - { - case BUFFER_VIEW_SHADER_RESOURCE: return m_pDefaultSRV.get(); - case BUFFER_VIEW_UNORDERED_ACCESS: return m_pDefaultUAV.get(); - default: UNEXPECTED("Unknown view type"); return nullptr; - } -} - -template -void BufferBase::CreateDefaultViews() -{ - // Create default views for structured and raw buffers. For formatted buffers we do not know the view format, so - // cannot create views. - - if (this->m_Desc.BindFlags & BIND_UNORDERED_ACCESS && (this->m_Desc.Mode == BUFFER_MODE_STRUCTURED || this->m_Desc.Mode == BUFFER_MODE_RAW)) - { - BufferViewDesc ViewDesc; - ViewDesc.ViewType = BUFFER_VIEW_UNORDERED_ACCESS; - IBufferView* pUAV = nullptr; - CreateViewInternal(ViewDesc, &pUAV, true); - m_pDefaultUAV.reset(static_cast(pUAV)); - VERIFY(m_pDefaultUAV->GetDesc().ViewType == BUFFER_VIEW_UNORDERED_ACCESS, "Unexpected view type"); - } - - if (this->m_Desc.BindFlags & BIND_SHADER_RESOURCE && (this->m_Desc.Mode == BUFFER_MODE_STRUCTURED || this->m_Desc.Mode == BUFFER_MODE_RAW)) - { - BufferViewDesc ViewDesc; - ViewDesc.ViewType = BUFFER_VIEW_SHADER_RESOURCE; - IBufferView* pSRV = nullptr; - CreateViewInternal(ViewDesc, &pSRV, true); - m_pDefaultSRV.reset(static_cast(pSRV)); - VERIFY(m_pDefaultSRV->GetDesc().ViewType == BUFFER_VIEW_SHADER_RESOURCE, "Unexpected view type"); - } -} - -} // namespace Diligent diff --git a/Graphics/GraphicsEngine/include/BufferBase.hpp b/Graphics/GraphicsEngine/include/BufferBase.hpp new file mode 100644 index 00000000..a40978f7 --- /dev/null +++ b/Graphics/GraphicsEngine/include/BufferBase.hpp @@ -0,0 +1,282 @@ +/* + * Copyright 2019-2020 Diligent Graphics LLC + * Copyright 2015-2019 Egor Yusov + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + * + * In no event and under no legal theory, whether in tort (including negligence), + * contract, or otherwise, unless required by applicable law (such as deliberate + * and grossly negligent acts) or agreed to in writing, shall any Contributor be + * liable for any damages, including any direct, indirect, special, incidental, + * or consequential damages of any character arising as a result of this License or + * out of the use or inability to use the software (including but not limited to damages + * for loss of goodwill, work stoppage, computer failure or malfunction, or any and + * all other commercial damages or losses), even if such Contributor has been advised + * of the possibility of such damages. + */ + +#pragma once + +/// \file +/// Implementation of the Diligent::BufferBase template class + +#include "Buffer.h" +#include "GraphicsTypes.h" +#include "DeviceObjectBase.hpp" +#include "GraphicsAccessories.hpp" +#include "STDAllocator.h" +#include + +namespace Diligent +{ + +/// Template class implementing base functionality for a buffer object + +/// \tparam BaseInterface - base interface that this class will inheret +/// (Diligent::IBufferD3D11, Diligent::IBufferD3D12, +/// Diligent::IBufferGL or Diligent::IBufferVk). +/// \tparam RenderDeviceImplType - type of the render device implementation +/// (Diligent::RenderDeviceD3D11Impl, Diligent::RenderDeviceD3D12Impl, +/// Diligent::RenderDeviceGLImpl, or Diligent::RenderDeviceVkImpl) +/// \tparam BufferViewImplType - type of the buffer view implementation +/// (Diligent::BufferViewD3D11Impl, Diligent::BufferViewD3D12Impl, +/// Diligent::BufferViewGLImpl or Diligent::BufferViewVkImpl) +/// \tparam TBuffViewObjAllocator - type of the allocator that is used to allocate memory for the buffer view object instances +template +class BufferBase : public DeviceObjectBase +{ +public: + using TDeviceObjectBase = DeviceObjectBase; + + /// \param pRefCounters - reference counters object that controls the lifetime of this buffer. + /// \param BuffViewObjAllocator - allocator that is used to allocate memory for the buffer view instances. + /// This parameter is only used for debug purposes. + /// \param pDevice - pointer to the device. + /// \param BuffDesc - buffer description. + /// \param bIsDeviceInternal - flag indicating if the buffer is an internal device object and + /// must not keep a strong reference to the device. + BufferBase(IReferenceCounters* pRefCounters, + TBuffViewObjAllocator& BuffViewObjAllocator, + RenderDeviceImplType* pDevice, + const BufferDesc& BuffDesc, + bool bIsDeviceInternal) : + TDeviceObjectBase{pRefCounters, pDevice, BuffDesc, bIsDeviceInternal}, +#ifdef _DEBUG + m_dbgBuffViewAllocator{BuffViewObjAllocator}, +#endif + m_pDefaultUAV{nullptr, STDDeleter(BuffViewObjAllocator)}, + m_pDefaultSRV{nullptr, STDDeleter(BuffViewObjAllocator)} + { +#define VERIFY_BUFFER(Expr, ...) \ + do \ + { \ + if (!(Expr)) \ + { \ + LOG_ERROR_AND_THROW("Buffer '", this->m_Desc.Name ? this->m_Desc.Name : "", "': ", ##__VA_ARGS__); \ + } \ + } while (false) + + Uint32 AllowedBindFlags = + BIND_VERTEX_BUFFER | BIND_INDEX_BUFFER | BIND_UNIFORM_BUFFER | + BIND_SHADER_RESOURCE | BIND_STREAM_OUTPUT | BIND_UNORDERED_ACCESS | + BIND_INDIRECT_DRAW_ARGS; + const Char* strAllowedBindFlags = + "BIND_VERTEX_BUFFER (1), BIND_INDEX_BUFFER (2), BIND_UNIFORM_BUFFER (4), " + "BIND_SHADER_RESOURCE (8), BIND_STREAM_OUTPUT (16), BIND_UNORDERED_ACCESS (128), " + "BIND_INDIRECT_DRAW_ARGS (256)"; + + VERIFY_BUFFER((BuffDesc.BindFlags & ~AllowedBindFlags) == 0, "Incorrect bind flags specified (", BuffDesc.BindFlags & ~AllowedBindFlags, "). Only the following flags are allowed:\n", strAllowedBindFlags); + + if ((this->m_Desc.BindFlags & BIND_UNORDERED_ACCESS) || + (this->m_Desc.BindFlags & BIND_SHADER_RESOURCE)) + { + VERIFY_BUFFER(this->m_Desc.Mode > BUFFER_MODE_UNDEFINED && this->m_Desc.Mode < BUFFER_MODE_NUM_MODES, GetBufferModeString(this->m_Desc.Mode), " is not a valid mode for a buffer created with BIND_SHADER_RESOURCE or BIND_UNORDERED_ACCESS flags"); + if (this->m_Desc.Mode == BUFFER_MODE_STRUCTURED || this->m_Desc.Mode == BUFFER_MODE_FORMATTED) + { + VERIFY_BUFFER(this->m_Desc.ElementByteStride != 0, "Element stride must not be zero for structured and formatted buffers"); + } + else if (this->m_Desc.Mode == BUFFER_MODE_RAW) + { + } + } + +#undef VERIFY_BUFFER + + Uint64 DeviceQueuesMask = pDevice->GetCommandQueueMask(); + DEV_CHECK_ERR((this->m_Desc.CommandQueueMask & DeviceQueuesMask) != 0, "No bits in the command queue mask (0x", std::hex, this->m_Desc.CommandQueueMask, ") correspond to one of ", pDevice->GetCommandQueueCount(), " available device command queues"); + this->m_Desc.CommandQueueMask &= DeviceQueuesMask; + } + + IMPLEMENT_QUERY_INTERFACE_IN_PLACE(IID_Buffer, TDeviceObjectBase) + + /// Implementation of IBuffer::CreateView(); calls CreateViewInternal() virtual function + /// that creates buffer view for the specific engine implementation. + virtual void CreateView(const struct BufferViewDesc& ViewDesc, IBufferView** ppView) override; + + /// Implementation of IBuffer::GetDefaultView(). + virtual IBufferView* GetDefaultView(BUFFER_VIEW_TYPE ViewType) override; + + /// Creates default buffer views. + + /// + /// - Creates default shader resource view addressing the entire buffer if Diligent::BIND_SHADER_RESOURCE flag is set + /// - Creates default unordered access view addressing the entire buffer if Diligent::BIND_UNORDERED_ACCESS flag is set + /// + /// The function calls CreateViewInternal(). + void CreateDefaultViews(); + + virtual void SetState(RESOURCE_STATE State) override final + { + this->m_State = State; + } + + virtual RESOURCE_STATE GetState() const override final + { + return this->m_State; + } + + bool IsInKnownState() const + { + return this->m_State != RESOURCE_STATE_UNKNOWN; + } + + bool CheckState(RESOURCE_STATE State) const + { + VERIFY((State & (State - 1)) == 0, "Single state is expected"); + VERIFY(IsInKnownState(), "Buffer state is unknown"); + return (this->m_State & State) == State; + } + +protected: + /// Pure virtual function that creates buffer view for the specific engine implementation. + virtual void CreateViewInternal(const struct BufferViewDesc& ViewDesc, IBufferView** ppView, bool bIsDefaultView) = 0; + + /// Corrects buffer view description and validates view parameters. + void CorrectBufferViewDesc(struct BufferViewDesc& ViewDesc); + +#ifdef _DEBUG + TBuffViewObjAllocator& m_dbgBuffViewAllocator; +#endif + + RESOURCE_STATE m_State = RESOURCE_STATE_UNKNOWN; + + /// Default UAV addressing the entire buffer + std::unique_ptr> m_pDefaultUAV; + + /// Default SRV addressing the entire buffer + std::unique_ptr> m_pDefaultSRV; +}; + + + + +template +void BufferBase::CreateView(const struct BufferViewDesc& ViewDesc, IBufferView** ppView) +{ + DEV_CHECK_ERR(ViewDesc.ViewType != BUFFER_VIEW_UNDEFINED, "Buffer view type is not specified"); + if (ViewDesc.ViewType == BUFFER_VIEW_SHADER_RESOURCE) + DEV_CHECK_ERR(this->m_Desc.BindFlags & BIND_SHADER_RESOURCE, "Attempting to create SRV for buffer '", this->m_Desc.Name, "' that was not created with BIND_SHADER_RESOURCE flag"); + else if (ViewDesc.ViewType == BUFFER_VIEW_UNORDERED_ACCESS) + DEV_CHECK_ERR(this->m_Desc.BindFlags & BIND_UNORDERED_ACCESS, "Attempting to create UAV for buffer '", this->m_Desc.Name, "' that was not created with BIND_UNORDERED_ACCESS flag"); + else + UNEXPECTED("Unexpected buffer view type"); + + CreateViewInternal(ViewDesc, ppView, false); +} + + +template +void BufferBase::CorrectBufferViewDesc(struct BufferViewDesc& ViewDesc) +{ + if (ViewDesc.ByteWidth == 0) + { + DEV_CHECK_ERR(this->m_Desc.uiSizeInBytes > ViewDesc.ByteOffset, "Byte offset (", ViewDesc.ByteOffset, ") exceeds buffer size (", this->m_Desc.uiSizeInBytes, ")"); + ViewDesc.ByteWidth = this->m_Desc.uiSizeInBytes - ViewDesc.ByteOffset; + } + if (ViewDesc.ByteOffset + ViewDesc.ByteWidth > this->m_Desc.uiSizeInBytes) + LOG_ERROR_AND_THROW("Buffer view range [", ViewDesc.ByteOffset, ", ", ViewDesc.ByteOffset + ViewDesc.ByteWidth, ") is out of the buffer boundaries [0, ", this->m_Desc.uiSizeInBytes, ")."); + if ((this->m_Desc.BindFlags & BIND_UNORDERED_ACCESS) || + (this->m_Desc.BindFlags & BIND_SHADER_RESOURCE)) + { + if (this->m_Desc.Mode == BUFFER_MODE_STRUCTURED || this->m_Desc.Mode == BUFFER_MODE_FORMATTED) + { + VERIFY(this->m_Desc.ElementByteStride != 0, "Element byte stride is zero"); + if ((ViewDesc.ByteOffset % this->m_Desc.ElementByteStride) != 0) + LOG_ERROR_AND_THROW("Buffer view byte offset (", ViewDesc.ByteOffset, ") is not multiple of element byte stride (", this->m_Desc.ElementByteStride, ")."); + if ((ViewDesc.ByteWidth % this->m_Desc.ElementByteStride) != 0) + LOG_ERROR_AND_THROW("Buffer view byte width (", ViewDesc.ByteWidth, ") is not multiple of element byte stride (", this->m_Desc.ElementByteStride, ")."); + } + + if (this->m_Desc.Mode == BUFFER_MODE_FORMATTED && ViewDesc.Format.ValueType == VT_UNDEFINED) + LOG_ERROR_AND_THROW("Format must be specified when creating a view of a formatted buffer"); + + if (this->m_Desc.Mode == BUFFER_MODE_FORMATTED || (this->m_Desc.Mode == BUFFER_MODE_RAW && ViewDesc.Format.ValueType != VT_UNDEFINED)) + { + if (ViewDesc.Format.NumComponents <= 0 || ViewDesc.Format.NumComponents > 4) + LOG_ERROR_AND_THROW("Incorrect number of components (", Uint32{ViewDesc.Format.NumComponents}, "). 1, 2, 3, or 4 are allowed values"); + if (ViewDesc.Format.ValueType == VT_FLOAT32 || ViewDesc.Format.ValueType == VT_FLOAT16) + ViewDesc.Format.IsNormalized = false; + auto ViewElementStride = GetValueSize(ViewDesc.Format.ValueType) * Uint32{ViewDesc.Format.NumComponents}; + if (this->m_Desc.Mode == BUFFER_MODE_RAW && this->m_Desc.ElementByteStride == 0) + LOG_ERROR_AND_THROW("To enable formatted views of a raw buffer, element byte must be specified during buffer initialization"); + if (ViewElementStride != this->m_Desc.ElementByteStride) + LOG_ERROR_AND_THROW("Buffer element byte stride (", this->m_Desc.ElementByteStride, ") is not consistent with the size (", ViewElementStride, ") defined by the format of the view (", GetBufferFormatString(ViewDesc.Format), ')'); + } + + if (this->m_Desc.Mode == BUFFER_MODE_RAW && ViewDesc.Format.ValueType == VT_UNDEFINED) + { + if ((ViewDesc.ByteOffset % 16) != 0) + LOG_ERROR_AND_THROW("When creating a RAW view, the offset of the first element from the start of the buffer (", ViewDesc.ByteOffset, ") must be a multiple of 16 bytes"); + } + } +} + +template +IBufferView* BufferBase::GetDefaultView(BUFFER_VIEW_TYPE ViewType) +{ + switch (ViewType) + { + case BUFFER_VIEW_SHADER_RESOURCE: return m_pDefaultSRV.get(); + case BUFFER_VIEW_UNORDERED_ACCESS: return m_pDefaultUAV.get(); + default: UNEXPECTED("Unknown view type"); return nullptr; + } +} + +template +void BufferBase::CreateDefaultViews() +{ + // Create default views for structured and raw buffers. For formatted buffers we do not know the view format, so + // cannot create views. + + if (this->m_Desc.BindFlags & BIND_UNORDERED_ACCESS && (this->m_Desc.Mode == BUFFER_MODE_STRUCTURED || this->m_Desc.Mode == BUFFER_MODE_RAW)) + { + BufferViewDesc ViewDesc; + ViewDesc.ViewType = BUFFER_VIEW_UNORDERED_ACCESS; + IBufferView* pUAV = nullptr; + CreateViewInternal(ViewDesc, &pUAV, true); + m_pDefaultUAV.reset(static_cast(pUAV)); + VERIFY(m_pDefaultUAV->GetDesc().ViewType == BUFFER_VIEW_UNORDERED_ACCESS, "Unexpected view type"); + } + + if (this->m_Desc.BindFlags & BIND_SHADER_RESOURCE && (this->m_Desc.Mode == BUFFER_MODE_STRUCTURED || this->m_Desc.Mode == BUFFER_MODE_RAW)) + { + BufferViewDesc ViewDesc; + ViewDesc.ViewType = BUFFER_VIEW_SHADER_RESOURCE; + IBufferView* pSRV = nullptr; + CreateViewInternal(ViewDesc, &pSRV, true); + m_pDefaultSRV.reset(static_cast(pSRV)); + VERIFY(m_pDefaultSRV->GetDesc().ViewType == BUFFER_VIEW_SHADER_RESOURCE, "Unexpected view type"); + } +} + +} // namespace Diligent diff --git a/Graphics/GraphicsEngine/include/BufferViewBase.h b/Graphics/GraphicsEngine/include/BufferViewBase.h deleted file mode 100644 index 59e9a14a..00000000 --- a/Graphics/GraphicsEngine/include/BufferViewBase.h +++ /dev/null @@ -1,108 +0,0 @@ -/* - * Copyright 2019-2020 Diligent Graphics LLC - * Copyright 2015-2019 Egor Yusov - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - * - * In no event and under no legal theory, whether in tort (including negligence), - * contract, or otherwise, unless required by applicable law (such as deliberate - * and grossly negligent acts) or agreed to in writing, shall any Contributor be - * liable for any damages, including any direct, indirect, special, incidental, - * or consequential damages of any character arising as a result of this License or - * out of the use or inability to use the software (including but not limited to damages - * for loss of goodwill, work stoppage, computer failure or malfunction, or any and - * all other commercial damages or losses), even if such Contributor has been advised - * of the possibility of such damages. - */ - -#pragma once - -/// \file -/// Implementation of the Diligent::BufferViewBase template class - -#include "BufferView.h" -#include "DeviceObjectBase.h" -#include "GraphicsTypes.h" -#include "RefCntAutoPtr.h" - -namespace Diligent -{ - -class IRenderDevice; -class IBuffer; - -/// Template class implementing base functionality for a buffer view object - -/// \tparam BaseInterface - base interface that this class will inheret -/// (Diligent::IBufferViewD3D11, Diligent::IBufferViewD3D12, -/// Diligent::IBufferViewGL or Diligent::IBufferViewVk). -/// \tparam RenderDeviceImplType - type of the render device implementation -/// (Diligent::RenderDeviceD3D11Impl, Diligent::RenderDeviceD3D12Impl, -/// Diligent::RenderDeviceGLImpl, or Diligent::RenderDeviceVkImpl) -template -class BufferViewBase : public DeviceObjectBase -{ -public: - using TDeviceObjectBase = DeviceObjectBase; - - /// \param pRefCounters - reference counters object that controls the lifetime of this buffer view. - /// \param pDevice - pointer to the render device. - /// \param ViewDesc - buffer view description. - /// \param pBuffer - pointer to the buffer that the view is to be created for. - /// \param bIsDefaultView - flag indicating if the view is default view, and is thus - /// part of the buffer object. In this case the view will attach - /// to the buffer's reference counters. - BufferViewBase(IReferenceCounters* pRefCounters, - RenderDeviceImplType* pDevice, - const BufferViewDesc& ViewDesc, - IBuffer* pBuffer, - bool bIsDefaultView) : - // Default views are created as part of the buffer, so we cannot not keep strong - // reference to the buffer to avoid cyclic links. Instead, we will attach to the - // reference counters of the buffer. - TDeviceObjectBase(pRefCounters, pDevice, ViewDesc), - m_pBuffer{pBuffer}, - // For non-default view, we will keep strong reference to buffer - m_spBuffer{bIsDefaultView ? nullptr : pBuffer} - {} - - IMPLEMENT_QUERY_INTERFACE_IN_PLACE(IID_BufferView, TDeviceObjectBase) - - /// Implementation of IBufferView::GetBuffer() - virtual IBuffer* GetBuffer() override final - { - return m_pBuffer; - } - - template - BufferType* GetBuffer() - { - return ValidatedCast(m_pBuffer); - } - - template - BufferType* GetBuffer() const - { - return ValidatedCast(m_pBuffer); - } - -protected: - /// Pointer to the buffer - IBuffer* const m_pBuffer; - - /// Strong reference to the buffer. Used for non-default views - /// to keep the buffer alive - RefCntAutoPtr m_spBuffer; -}; - -} // namespace Diligent diff --git a/Graphics/GraphicsEngine/include/BufferViewBase.hpp b/Graphics/GraphicsEngine/include/BufferViewBase.hpp new file mode 100644 index 00000000..37e80756 --- /dev/null +++ b/Graphics/GraphicsEngine/include/BufferViewBase.hpp @@ -0,0 +1,108 @@ +/* + * Copyright 2019-2020 Diligent Graphics LLC + * Copyright 2015-2019 Egor Yusov + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + * + * In no event and under no legal theory, whether in tort (including negligence), + * contract, or otherwise, unless required by applicable law (such as deliberate + * and grossly negligent acts) or agreed to in writing, shall any Contributor be + * liable for any damages, including any direct, indirect, special, incidental, + * or consequential damages of any character arising as a result of this License or + * out of the use or inability to use the software (including but not limited to damages + * for loss of goodwill, work stoppage, computer failure or malfunction, or any and + * all other commercial damages or losses), even if such Contributor has been advised + * of the possibility of such damages. + */ + +#pragma once + +/// \file +/// Implementation of the Diligent::BufferViewBase template class + +#include "BufferView.h" +#include "DeviceObjectBase.hpp" +#include "GraphicsTypes.h" +#include "RefCntAutoPtr.h" + +namespace Diligent +{ + +class IRenderDevice; +class IBuffer; + +/// Template class implementing base functionality for a buffer view object + +/// \tparam BaseInterface - base interface that this class will inheret +/// (Diligent::IBufferViewD3D11, Diligent::IBufferViewD3D12, +/// Diligent::IBufferViewGL or Diligent::IBufferViewVk). +/// \tparam RenderDeviceImplType - type of the render device implementation +/// (Diligent::RenderDeviceD3D11Impl, Diligent::RenderDeviceD3D12Impl, +/// Diligent::RenderDeviceGLImpl, or Diligent::RenderDeviceVkImpl) +template +class BufferViewBase : public DeviceObjectBase +{ +public: + using TDeviceObjectBase = DeviceObjectBase; + + /// \param pRefCounters - reference counters object that controls the lifetime of this buffer view. + /// \param pDevice - pointer to the render device. + /// \param ViewDesc - buffer view description. + /// \param pBuffer - pointer to the buffer that the view is to be created for. + /// \param bIsDefaultView - flag indicating if the view is default view, and is thus + /// part of the buffer object. In this case the view will attach + /// to the buffer's reference counters. + BufferViewBase(IReferenceCounters* pRefCounters, + RenderDeviceImplType* pDevice, + const BufferViewDesc& ViewDesc, + IBuffer* pBuffer, + bool bIsDefaultView) : + // Default views are created as part of the buffer, so we cannot not keep strong + // reference to the buffer to avoid cyclic links. Instead, we will attach to the + // reference counters of the buffer. + TDeviceObjectBase(pRefCounters, pDevice, ViewDesc), + m_pBuffer{pBuffer}, + // For non-default view, we will keep strong reference to buffer + m_spBuffer{bIsDefaultView ? nullptr : pBuffer} + {} + + IMPLEMENT_QUERY_INTERFACE_IN_PLACE(IID_BufferView, TDeviceObjectBase) + + /// Implementation of IBufferView::GetBuffer() + virtual IBuffer* GetBuffer() override final + { + return m_pBuffer; + } + + template + BufferType* GetBuffer() + { + return ValidatedCast(m_pBuffer); + } + + template + BufferType* GetBuffer() const + { + return ValidatedCast(m_pBuffer); + } + +protected: + /// Pointer to the buffer + IBuffer* const m_pBuffer; + + /// Strong reference to the buffer. Used for non-default views + /// to keep the buffer alive + RefCntAutoPtr m_spBuffer; +}; + +} // namespace Diligent diff --git a/Graphics/GraphicsEngine/include/CommandListBase.h b/Graphics/GraphicsEngine/include/CommandListBase.h deleted file mode 100644 index 2fda804b..00000000 --- a/Graphics/GraphicsEngine/include/CommandListBase.h +++ /dev/null @@ -1,72 +0,0 @@ -/* - * Copyright 2019-2020 Diligent Graphics LLC - * Copyright 2015-2019 Egor Yusov - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - * - * In no event and under no legal theory, whether in tort (including negligence), - * contract, or otherwise, unless required by applicable law (such as deliberate - * and grossly negligent acts) or agreed to in writing, shall any Contributor be - * liable for any damages, including any direct, indirect, special, incidental, - * or consequential damages of any character arising as a result of this License or - * out of the use or inability to use the software (including but not limited to damages - * for loss of goodwill, work stoppage, computer failure or malfunction, or any and - * all other commercial damages or losses), even if such Contributor has been advised - * of the possibility of such damages. - */ - -#pragma once - -/// \file -/// Implementation of the Diligent::CommandListBase template class - -#include "CommandList.h" -#include "DeviceObjectBase.h" -#include "RenderDeviceBase.h" - -namespace Diligent -{ - -struct CommandListDesc : public DeviceObjectAttribs -{ -}; - -/// Template class implementing base functionality for a command list object. - -/// \tparam BaseInterface - base interface that this class will inheret -/// (Diligent::ICommandListD3D11, Diligent::ICommandListD3D12 or Diligent::ICommandListVk). -/// \tparam RenderDeviceImplType - type of the render device implementation -/// (Diligent::RenderDeviceD3D11Impl, Diligent::RenderDeviceD3D12Impl, -/// Diligent::RenderDeviceGLImpl, or Diligent::RenderDeviceVkImpl) -template -class CommandListBase : public DeviceObjectBase -{ -public: - using TDeviceObjectBase = DeviceObjectBase; - - /// \param pRefCounters - reference counters object that controls the lifetime of this command list. - /// \param pDevice - pointer to the device. - /// \param bIsDeviceInternal - flag indicating if the CommandList is an internal device object and - /// must not keep a strong reference to the device. - CommandListBase(IReferenceCounters* pRefCounters, RenderDeviceImplType* pDevice, bool bIsDeviceInternal = false) : - TDeviceObjectBase{pRefCounters, pDevice, CommandListDesc(), bIsDeviceInternal} - {} - - ~CommandListBase() - { - } - - IMPLEMENT_QUERY_INTERFACE_IN_PLACE(IID_CommandList, TDeviceObjectBase) -}; - -} // namespace Diligent diff --git a/Graphics/GraphicsEngine/include/CommandListBase.hpp b/Graphics/GraphicsEngine/include/CommandListBase.hpp new file mode 100644 index 00000000..051eb737 --- /dev/null +++ b/Graphics/GraphicsEngine/include/CommandListBase.hpp @@ -0,0 +1,72 @@ +/* + * Copyright 2019-2020 Diligent Graphics LLC + * Copyright 2015-2019 Egor Yusov + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + * + * In no event and under no legal theory, whether in tort (including negligence), + * contract, or otherwise, unless required by applicable law (such as deliberate + * and grossly negligent acts) or agreed to in writing, shall any Contributor be + * liable for any damages, including any direct, indirect, special, incidental, + * or consequential damages of any character arising as a result of this License or + * out of the use or inability to use the software (including but not limited to damages + * for loss of goodwill, work stoppage, computer failure or malfunction, or any and + * all other commercial damages or losses), even if such Contributor has been advised + * of the possibility of such damages. + */ + +#pragma once + +/// \file +/// Implementation of the Diligent::CommandListBase template class + +#include "CommandList.h" +#include "DeviceObjectBase.hpp" +#include "RenderDeviceBase.hpp" + +namespace Diligent +{ + +struct CommandListDesc : public DeviceObjectAttribs +{ +}; + +/// Template class implementing base functionality for a command list object. + +/// \tparam BaseInterface - base interface that this class will inheret +/// (Diligent::ICommandListD3D11, Diligent::ICommandListD3D12 or Diligent::ICommandListVk). +/// \tparam RenderDeviceImplType - type of the render device implementation +/// (Diligent::RenderDeviceD3D11Impl, Diligent::RenderDeviceD3D12Impl, +/// Diligent::RenderDeviceGLImpl, or Diligent::RenderDeviceVkImpl) +template +class CommandListBase : public DeviceObjectBase +{ +public: + using TDeviceObjectBase = DeviceObjectBase; + + /// \param pRefCounters - reference counters object that controls the lifetime of this command list. + /// \param pDevice - pointer to the device. + /// \param bIsDeviceInternal - flag indicating if the CommandList is an internal device object and + /// must not keep a strong reference to the device. + CommandListBase(IReferenceCounters* pRefCounters, RenderDeviceImplType* pDevice, bool bIsDeviceInternal = false) : + TDeviceObjectBase{pRefCounters, pDevice, CommandListDesc(), bIsDeviceInternal} + {} + + ~CommandListBase() + { + } + + IMPLEMENT_QUERY_INTERFACE_IN_PLACE(IID_CommandList, TDeviceObjectBase) +}; + +} // namespace Diligent diff --git a/Graphics/GraphicsEngine/include/DeviceContextBase.h b/Graphics/GraphicsEngine/include/DeviceContextBase.h deleted file mode 100644 index c6ed1793..00000000 --- a/Graphics/GraphicsEngine/include/DeviceContextBase.h +++ /dev/null @@ -1,1518 +0,0 @@ -/* - * Copyright 2019-2020 Diligent Graphics LLC - * Copyright 2015-2019 Egor Yusov - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - * - * In no event and under no legal theory, whether in tort (including negligence), - * contract, or otherwise, unless required by applicable law (such as deliberate - * and grossly negligent acts) or agreed to in writing, shall any Contributor be - * liable for any damages, including any direct, indirect, special, incidental, - * or consequential damages of any character arising as a result of this License or - * out of the use or inability to use the software (including but not limited to damages - * for loss of goodwill, work stoppage, computer failure or malfunction, or any and - * all other commercial damages or losses), even if such Contributor has been advised - * of the possibility of such damages. - */ - -#pragma once - -/// \file -/// Implementation of the Diligent::DeviceContextBase template class and related structures - -#include - -#include "DeviceContext.h" -#include "DeviceObjectBase.h" -#include "ResourceMapping.h" -#include "Sampler.h" -#include "ObjectBase.h" -#include "DebugUtilities.h" -#include "ValidatedCast.h" -#include "GraphicsAccessories.hpp" -#include "TextureBase.h" - -namespace Diligent -{ - -/// Describes input vertex stream -template -struct VertexStreamInfo -{ - VertexStreamInfo() {} - - /// Strong reference to the buffer object - RefCntAutoPtr pBuffer; - Uint32 Offset = 0; ///< Offset in bytes -}; - -/// Base implementation of the device context. - -/// \tparam BaseInterface - base interface that this class will inheret. -/// \tparam ImplementationTraits - implementation traits that define specific implementation details -/// (texture implemenation type, buffer implementation type, etc.) -/// \remarks Device context keeps strong references to all objects currently bound to -/// the pipeline: buffers, states, samplers, shaders, etc. -/// The context also keeps strong references to the device and -/// the swap chain. -template -class DeviceContextBase : public ObjectBase -{ -public: - using TObjectBase = ObjectBase; - using DeviceImplType = typename ImplementationTraits::DeviceType; - using BufferImplType = typename ImplementationTraits::BufferType; - using TextureImplType = typename ImplementationTraits::TextureType; - using PipelineStateImplType = typename ImplementationTraits::PipelineStateType; - using TextureViewImplType = typename TextureImplType::ViewImplType; - using QueryImplType = typename ImplementationTraits::QueryType; - - /// \param pRefCounters - reference counters object that controls the lifetime of this device context. - /// \param pRenderDevice - render device. - /// \param bIsDeferred - flag indicating if this instance is a deferred context - DeviceContextBase(IReferenceCounters* pRefCounters, DeviceImplType* pRenderDevice, bool bIsDeferred) : - // clang-format off - TObjectBase {pRefCounters }, - m_pDevice {pRenderDevice}, - m_bIsDeferred{bIsDeferred } - // clang-format on - { - } - - ~DeviceContextBase() - { - } - - IMPLEMENT_QUERY_INTERFACE_IN_PLACE(IID_DeviceContext, TObjectBase) - - /// Base implementation of IDeviceContext::SetVertexBuffers(); validates parameters and - /// caches references to the buffers. - inline virtual void SetVertexBuffers(Uint32 StartSlot, - Uint32 NumBuffersSet, - IBuffer** ppBuffers, - Uint32* pOffsets, - RESOURCE_STATE_TRANSITION_MODE StateTransitionMode, - SET_VERTEX_BUFFERS_FLAGS Flags) override = 0; - - inline virtual void InvalidateState() override = 0; - - /// Base implementation of IDeviceContext::CommitShaderResources(); validates parameters. - inline bool CommitShaderResources(IShaderResourceBinding* pShaderResourceBinding, - RESOURCE_STATE_TRANSITION_MODE StateTransitionMode, - int); - - /// Base implementation of IDeviceContext::SetIndexBuffer(); caches the strong reference to the index buffer - inline virtual void SetIndexBuffer(IBuffer* pIndexBuffer, Uint32 ByteOffset, RESOURCE_STATE_TRANSITION_MODE StateTransitionMode) override = 0; - - /// Caches the viewports - inline void SetViewports(Uint32 NumViewports, const Viewport* pViewports, Uint32& RTWidth, Uint32& RTHeight); - - /// Caches the scissor rects - inline void SetScissorRects(Uint32 NumRects, const Rect* pRects, Uint32& RTWidth, Uint32& RTHeight); - - /// Caches the render target and depth stencil views. Returns true if any view is different - /// from the cached value and false otherwise. - inline bool SetRenderTargets(Uint32 NumRenderTargets, ITextureView* ppRenderTargets[], ITextureView* pDepthStencil); - - /// Base implementation of IDeviceContext::UpdateBuffer(); validates input parameters. - virtual void UpdateBuffer(IBuffer* pBuffer, Uint32 Offset, Uint32 Size, const void* pData, RESOURCE_STATE_TRANSITION_MODE StateTransitionMode) override = 0; - - /// Base implementation of IDeviceContext::CopyBuffer(); validates input parameters. - virtual void CopyBuffer(IBuffer* pSrcBuffer, - Uint32 SrcOffset, - RESOURCE_STATE_TRANSITION_MODE SrcBufferTransitionMode, - IBuffer* pDstBuffer, - Uint32 DstOffset, - Uint32 Size, - RESOURCE_STATE_TRANSITION_MODE DstBufferTransitionMode) override = 0; - - /// Base implementation of IDeviceContext::MapBuffer(); validates input parameters. - virtual void MapBuffer(IBuffer* pBuffer, MAP_TYPE MapType, MAP_FLAGS MapFlags, PVoid& pMappedData) override = 0; - - /// Base implementation of IDeviceContext::UnmapBuffer() - virtual void UnmapBuffer(IBuffer* pBuffer, MAP_TYPE MapType) override = 0; - - /// Base implementaiton of IDeviceContext::UpdateData(); validates input parameters - virtual void UpdateTexture(ITexture* pTexture, - Uint32 MipLevel, - Uint32 Slice, - const Box& DstBox, - const TextureSubResData& SubresData, - RESOURCE_STATE_TRANSITION_MODE SrcBufferTransitionMode, - RESOURCE_STATE_TRANSITION_MODE TextureTransitionMode) override = 0; - - /// Base implementaiton of IDeviceContext::CopyTexture(); validates input parameters - virtual void CopyTexture(const CopyTextureAttribs& CopyAttribs) override = 0; - - /// Base implementaiton of IDeviceContext::MapTextureSubresource() - virtual void MapTextureSubresource(ITexture* pTexture, - Uint32 MipLevel, - Uint32 ArraySlice, - MAP_TYPE MapType, - MAP_FLAGS MapFlags, - const Box* pMapRegion, - MappedTextureSubresource& MappedData) override = 0; - - /// Base implementaiton of IDeviceContext::UnmapTextureSubresource() - virtual void UnmapTextureSubresource(ITexture* pTexture, Uint32 MipLevel, Uint32 ArraySlice) override = 0; - - virtual void GenerateMips(ITextureView* pTexView) override = 0; - - virtual void ResolveTextureSubresource(ITexture* pSrcTexture, - ITexture* pDstTexture, - const ResolveTextureSubresourceAttribs& ResolveAttribs) override = 0; - - /// Returns currently bound pipeline state and blend factors - inline void GetPipelineState(IPipelineState** ppPSO, float* BlendFactors, Uint32& StencilRef); - - /// Returns currently bound render targets - inline void GetRenderTargets(Uint32& NumRenderTargets, ITextureView** ppRTVs, ITextureView** ppDSV); - - /// Returns currently set viewports - inline void GetViewports(Uint32& NumViewports, Viewport* pViewports); - - /// Returns the render device - IRenderDevice* GetDevice() { return m_pDevice; } - - virtual void ResetRenderTargets(); - - bool IsDeferred() const { return m_bIsDeferred; } - - /// Checks if a texture is bound as a render target or depth-stencil buffer and - /// resets render targets if it is. - bool UnbindTextureFromFramebuffer(TextureImplType* pTexture, bool bShowMessage); - -protected: - inline bool SetBlendFactors(const float* BlendFactors, int Dummy); - - inline bool SetStencilRef(Uint32 StencilRef, int Dummy); - - inline void SetPipelineState(PipelineStateImplType* pPipelineState, int /*Dummy*/); - - /// Clears all cached resources - inline void ClearStateCache(); - - /// Checks if the texture is currently bound as a render target. - bool CheckIfBoundAsRenderTarget(TextureImplType* pTexture); - - /// Checks if the texture is currently bound as depth-stencil buffer. - bool CheckIfBoundAsDepthStencil(TextureImplType* pTexture); - - bool ClearDepthStencil(ITextureView* pView); - - bool ClearRenderTarget(ITextureView* pView); - - bool BeginQuery(IQuery* pQuery, int); - - bool EndQuery(IQuery* pQuery, int); - -#ifdef DEVELOPMENT - // clang-format off - bool DvpVerifyDrawArguments (const DrawAttribs& Attribs)const; - bool DvpVerifyDrawIndexedArguments (const DrawIndexedAttribs& Attribs)const; - bool DvpVerifyDrawIndirectArguments (const DrawIndirectAttribs& Attribs, const IBuffer* pAttribsBuffer)const; - bool DvpVerifyDrawIndexedIndirectArguments(const DrawIndexedIndirectAttribs& Attribs, const IBuffer* pAttribsBuffer)const; - - bool DvpVerifyDispatchArguments (const DispatchComputeAttribs& Attribs)const; - bool DvpVerifyDispatchIndirectArguments(const DispatchComputeIndirectAttribs& Attribs, const IBuffer* pAttribsBuffer)const; - - void DvpVerifyRenderTargets()const; - void DvpVerifyStateTransitionDesc(const StateTransitionDesc& Barrier)const; - bool DvpVerifyTextureState(const TextureImplType& Texture, RESOURCE_STATE RequiredState, const char* OperationName)const; - bool DvpVerifyBufferState (const BufferImplType& Buffer, RESOURCE_STATE RequiredState, const char* OperationName)const; -#else - bool DvpVerifyDrawArguments (const DrawAttribs& Attribs)const {return true;} - bool DvpVerifyDrawIndexedArguments (const DrawIndexedAttribs& Attribs)const {return true;} - bool DvpVerifyDrawIndirectArguments (const DrawIndirectAttribs& Attribs, const IBuffer* pAttribsBuffer)const {return true;} - bool DvpVerifyDrawIndexedIndirectArguments(const DrawIndexedIndirectAttribs& Attribs, const IBuffer* pAttribsBuffer)const {return true;} - - bool DvpVerifyDispatchArguments (const DispatchComputeAttribs& Attribs)const {return true;} - bool DvpVerifyDispatchIndirectArguments(const DispatchComputeIndirectAttribs& Attribs, const IBuffer* pAttribsBuffer)const {return true;} - - void DvpVerifyRenderTargets()const {} - void DvpVerifyStateTransitionDesc(const StateTransitionDesc& Barrier)const {} - bool DvpVerifyTextureState(const TextureImplType& Texture, RESOURCE_STATE RequiredState, const char* OperationName)const {return true;} - bool DvpVerifyBufferState (const BufferImplType& Buffer, RESOURCE_STATE RequiredState, const char* OperationName)const {return true;} - // clang-format on -#endif - - /// Strong reference to the device. - RefCntAutoPtr m_pDevice; - - /// Vertex streams. Every stream holds strong reference to the buffer - VertexStreamInfo m_VertexStreams[MAX_BUFFER_SLOTS]; - - /// Number of bound vertex streams - Uint32 m_NumVertexStreams = 0; - - /// Strong reference to the bound pipeline state object. - /// Use final PSO implementation type to avoid virtual calls to AddRef()/Release(). - /// We need to keep strong reference as we examine previous pipeline state in - /// SetPipelineState() - RefCntAutoPtr m_pPipelineState; - - /// Strong reference to the bound index buffer. - /// Use final buffer implementation type to avoid virtual calls to AddRef()/Release() - RefCntAutoPtr m_pIndexBuffer; - - /// Offset from the beginning of the index buffer to the start of the index data, in bytes. - Uint32 m_IndexDataStartOffset = 0; - - /// Current stencil reference value - Uint32 m_StencilRef = 0; - - /// Curent blend factors - Float32 m_BlendFactors[4] = {-1, -1, -1, -1}; - - /// Current viewports - Viewport m_Viewports[MAX_VIEWPORTS]; - /// Number of current viewports - Uint32 m_NumViewports = 0; - - /// Current scissor rects - Rect m_ScissorRects[MAX_VIEWPORTS]; - /// Number of current scissor rects - Uint32 m_NumScissorRects = 0; - - /// Vector of strong references to the bound render targets. - /// Use final texture view implementation type to avoid virtual calls to AddRef()/Release() - RefCntAutoPtr m_pBoundRenderTargets[MAX_RENDER_TARGETS]; - /// Number of bound render targets - Uint32 m_NumBoundRenderTargets = 0; - /// Width of the currently bound framebuffer - Uint32 m_FramebufferWidth = 0; - /// Height of the currently bound framebuffer - Uint32 m_FramebufferHeight = 0; - /// Number of array slices in the currently bound framebuffer - Uint32 m_FramebufferSlices = 0; - - /// Strong references to the bound depth stencil view. - /// Use final texture view implementation type to avoid virtual calls to AddRef()/Release() - RefCntAutoPtr m_pBoundDepthStencil; - - const bool m_bIsDeferred = false; - -#ifdef _DEBUG - // std::unordered_map is unbelievably slow. Keeping track of mapped buffers - // in release builds is not feasible - struct DbgMappedBufferInfo - { - MAP_TYPE MapType; - }; - std::unordered_map m_DbgMappedBuffers; -#endif -}; - - -template -inline void DeviceContextBase:: - SetVertexBuffers(Uint32 StartSlot, - Uint32 NumBuffersSet, - IBuffer** ppBuffers, - Uint32* pOffsets, - RESOURCE_STATE_TRANSITION_MODE StateTransitionMode, - SET_VERTEX_BUFFERS_FLAGS Flags) -{ -#ifdef DEVELOPMENT - if (StartSlot >= MAX_BUFFER_SLOTS) - { - LOG_ERROR_MESSAGE("Start vertex buffer slot ", StartSlot, " is out of allowed range [0, ", MAX_BUFFER_SLOTS - 1, "]."); - return; - } - - if (StartSlot + NumBuffersSet > MAX_BUFFER_SLOTS) - { - LOG_ERROR_MESSAGE("The range of vertex buffer slots being set [", StartSlot, ", ", StartSlot + NumBuffersSet - 1, "] is out of allowed range [0, ", MAX_BUFFER_SLOTS - 1, "]."); - NumBuffersSet = MAX_BUFFER_SLOTS - StartSlot; - } -#endif - - if (Flags & SET_VERTEX_BUFFERS_FLAG_RESET) - { - // Reset only these buffer slots that are not being set. - // It is very important to not reset buffers that stay unchanged - // as AddRef()/Release() are not free - for (Uint32 s = 0; s < StartSlot; ++s) - m_VertexStreams[s] = VertexStreamInfo{}; - for (Uint32 s = StartSlot + NumBuffersSet; s < m_NumVertexStreams; ++s) - m_VertexStreams[s] = VertexStreamInfo{}; - m_NumVertexStreams = 0; - } - m_NumVertexStreams = std::max(m_NumVertexStreams, StartSlot + NumBuffersSet); - - for (Uint32 Buff = 0; Buff < NumBuffersSet; ++Buff) - { - auto& CurrStream = m_VertexStreams[StartSlot + Buff]; - CurrStream.pBuffer = ppBuffers ? ValidatedCast(ppBuffers[Buff]) : nullptr; - CurrStream.Offset = pOffsets ? pOffsets[Buff] : 0; -#ifdef DEVELOPMENT - if (CurrStream.pBuffer) - { - const auto& BuffDesc = CurrStream.pBuffer->GetDesc(); - if (!(BuffDesc.BindFlags & BIND_VERTEX_BUFFER)) - { - LOG_ERROR_MESSAGE("Buffer '", BuffDesc.Name ? BuffDesc.Name : "", "' being bound as vertex buffer to slot ", Buff, " was not created with BIND_VERTEX_BUFFER flag"); - } - } -#endif - } - // Remove null buffers from the end of the array - while (m_NumVertexStreams > 0 && !m_VertexStreams[m_NumVertexStreams - 1].pBuffer) - m_VertexStreams[m_NumVertexStreams--] = VertexStreamInfo{}; -} - -template -inline void DeviceContextBase:: - SetPipelineState(PipelineStateImplType* pPipelineState, int /*Dummy*/) -{ - m_pPipelineState = pPipelineState; -} - -template -inline bool DeviceContextBase:: - CommitShaderResources(IShaderResourceBinding* pShaderResourceBinding, RESOURCE_STATE_TRANSITION_MODE StateTransitionMode, int) -{ -#ifdef DEVELOPMENT - if (!m_pPipelineState) - { - LOG_ERROR_MESSAGE("No pipeline state is bound to the pipeline"); - return false; - } - - if (pShaderResourceBinding) - { - if (m_pPipelineState->IsIncompatibleWith(pShaderResourceBinding->GetPipelineState())) - { - LOG_ERROR_MESSAGE("Shader resource binding object is not compatible with the currently bound pipeline state '", m_pPipelineState->GetDesc().Name, '\''); - return false; - } - } -#endif - return true; -} - -template -inline void DeviceContextBase::InvalidateState() -{ - DeviceContextBase::ClearStateCache(); -} - -template -inline void DeviceContextBase:: - SetIndexBuffer(IBuffer* pIndexBuffer, Uint32 ByteOffset, RESOURCE_STATE_TRANSITION_MODE StateTransitionMode) -{ - m_pIndexBuffer = ValidatedCast(pIndexBuffer); - m_IndexDataStartOffset = ByteOffset; -#ifdef DEVELOPMENT - if (m_pIndexBuffer) - { - const auto& BuffDesc = m_pIndexBuffer->GetDesc(); - if (!(BuffDesc.BindFlags & BIND_INDEX_BUFFER)) - { - LOG_ERROR_MESSAGE("Buffer '", BuffDesc.Name ? BuffDesc.Name : "", "' being bound as index buffer was not created with BIND_INDEX_BUFFER flag"); - } - } -#endif -} - - -template -inline void DeviceContextBase::GetPipelineState(IPipelineState** ppPSO, float* BlendFactors, Uint32& StencilRef) -{ - VERIFY(ppPSO != nullptr, "Null pointer provided null"); - VERIFY(*ppPSO == nullptr, "Memory address contains a pointer to a non-null blend state"); - if (m_pPipelineState) - { - m_pPipelineState->QueryInterface(IID_PipelineState, reinterpret_cast(ppPSO)); - } - else - { - *ppPSO = nullptr; - } - - for (Uint32 f = 0; f < 4; ++f) - BlendFactors[f] = m_BlendFactors[f]; - StencilRef = m_StencilRef; -}; - -template -inline bool DeviceContextBase::SetBlendFactors(const float* BlendFactors, int) -{ - bool FactorsDiffer = false; - for (Uint32 f = 0; f < 4; ++f) - { - if (m_BlendFactors[f] != BlendFactors[f]) - FactorsDiffer = true; - m_BlendFactors[f] = BlendFactors[f]; - } - return FactorsDiffer; -} - -template -inline bool DeviceContextBase::SetStencilRef(Uint32 StencilRef, int) -{ - if (m_StencilRef != StencilRef) - { - m_StencilRef = StencilRef; - return true; - } - return false; -} - -template -inline void DeviceContextBase:: - SetViewports(Uint32 NumViewports, const Viewport* pViewports, Uint32& RTWidth, Uint32& RTHeight) -{ - if (RTWidth == 0 || RTHeight == 0) - { - RTWidth = m_FramebufferWidth; - RTHeight = m_FramebufferHeight; - } - - VERIFY(NumViewports < MAX_VIEWPORTS, "Number of viewports (", NumViewports, ") exceeds the limit (", MAX_VIEWPORTS, ")"); - m_NumViewports = std::min(MAX_VIEWPORTS, NumViewports); - - Viewport DefaultVP(0, 0, static_cast(RTWidth), static_cast(RTHeight)); - // If no viewports are specified, use default viewport - if (m_NumViewports == 1 && pViewports == nullptr) - { - pViewports = &DefaultVP; - } - - for (Uint32 vp = 0; vp < m_NumViewports; ++vp) - { - m_Viewports[vp] = pViewports[vp]; - VERIFY(m_Viewports[vp].Width >= 0, "Incorrect viewport width (", m_Viewports[vp].Width, ")"); - VERIFY(m_Viewports[vp].Height >= 0, "Incorrect viewport height (", m_Viewports[vp].Height, ")"); - VERIFY(m_Viewports[vp].MaxDepth >= m_Viewports[vp].MinDepth, "Incorrect viewport depth range [", m_Viewports[vp].MinDepth, ", ", m_Viewports[vp].MaxDepth, "]"); - } -} - -template -inline void DeviceContextBase::GetViewports(Uint32& NumViewports, Viewport* pViewports) -{ - NumViewports = m_NumViewports; - if (pViewports) - { - for (Uint32 vp = 0; vp < m_NumViewports; ++vp) - pViewports[vp] = m_Viewports[vp]; - } -} - -template -inline void DeviceContextBase:: - SetScissorRects(Uint32 NumRects, const Rect* pRects, Uint32& RTWidth, Uint32& RTHeight) -{ - if (RTWidth == 0 || RTHeight == 0) - { - RTWidth = m_FramebufferWidth; - RTHeight = m_FramebufferHeight; - } - - VERIFY(NumRects < MAX_VIEWPORTS, "Number of scissor rects (", NumRects, ") exceeds the limit (", MAX_VIEWPORTS, ")"); - m_NumScissorRects = std::min(MAX_VIEWPORTS, NumRects); - - for (Uint32 sr = 0; sr < m_NumScissorRects; ++sr) - { - m_ScissorRects[sr] = pRects[sr]; - VERIFY(m_ScissorRects[sr].left <= m_ScissorRects[sr].right, "Incorrect horizontal bounds for a scissor rect [", m_ScissorRects[sr].left, ", ", m_ScissorRects[sr].right, ")"); - VERIFY(m_ScissorRects[sr].top <= m_ScissorRects[sr].bottom, "Incorrect vertical bounds for a scissor rect [", m_ScissorRects[sr].top, ", ", m_ScissorRects[sr].bottom, ")"); - } -} - -template -inline bool DeviceContextBase:: - SetRenderTargets(Uint32 NumRenderTargets, ITextureView* ppRenderTargets[], ITextureView* pDepthStencil) -{ - if (NumRenderTargets == 0 && pDepthStencil == nullptr) - { - ResetRenderTargets(); - return false; - } - - bool bBindRenderTargets = false; - m_FramebufferWidth = 0; - m_FramebufferHeight = 0; - m_FramebufferSlices = 0; - - if (NumRenderTargets != m_NumBoundRenderTargets) - { - bBindRenderTargets = true; - for (Uint32 rt = NumRenderTargets; rt < m_NumBoundRenderTargets; ++rt) - m_pBoundRenderTargets[rt].Release(); - - m_NumBoundRenderTargets = NumRenderTargets; - } - - for (Uint32 rt = 0; rt < NumRenderTargets; ++rt) - { - auto* pRTView = ppRenderTargets[rt]; - if (pRTView) - { - const auto& RTVDesc = pRTView->GetDesc(); -#ifdef DEVELOPMENT - if (RTVDesc.ViewType != TEXTURE_VIEW_RENDER_TARGET) - LOG_ERROR("Texture view object named '", RTVDesc.Name ? RTVDesc.Name : "", "' has incorrect view type (", GetTexViewTypeLiteralName(RTVDesc.ViewType), "). Render target view is expected"); -#endif - // Use this RTV to set the render target size - if (m_FramebufferWidth == 0) - { - auto* pTex = pRTView->GetTexture(); - const auto& TexDesc = pTex->GetDesc(); - m_FramebufferWidth = std::max(TexDesc.Width >> RTVDesc.MostDetailedMip, 1U); - m_FramebufferHeight = std::max(TexDesc.Height >> RTVDesc.MostDetailedMip, 1U); - m_FramebufferSlices = RTVDesc.NumArraySlices; - } - else - { -#ifdef DEVELOPMENT - const auto& TexDesc = pRTView->GetTexture()->GetDesc(); - if (m_FramebufferWidth != std::max(TexDesc.Width >> RTVDesc.MostDetailedMip, 1U)) - LOG_ERROR("Render target width (", std::max(TexDesc.Width >> RTVDesc.MostDetailedMip, 1U), ") specified by RTV '", RTVDesc.Name, "' is inconsistent with the width of previously bound render targets (", m_FramebufferWidth, ")"); - if (m_FramebufferHeight != std::max(TexDesc.Height >> RTVDesc.MostDetailedMip, 1U)) - LOG_ERROR("Render target height (", std::max(TexDesc.Height >> RTVDesc.MostDetailedMip, 1U), ") specified by RTV '", RTVDesc.Name, "' is inconsistent with the height of previously bound render targets (", m_FramebufferHeight, ")"); - if (m_FramebufferSlices != RTVDesc.NumArraySlices) - LOG_ERROR("Number of slices (", RTVDesc.NumArraySlices, ") specified by RTV '", RTVDesc.Name, "' is inconsistent with the number of slices in previously bound render targets (", m_FramebufferSlices, ")"); -#endif - } - } - - // Here both views are certainly live objects, since we store - // strong references to all bound render targets. So we - // can safely compare pointers. - if (m_pBoundRenderTargets[rt] != pRTView) - { - m_pBoundRenderTargets[rt] = ValidatedCast(pRTView); - bBindRenderTargets = true; - } - } - - if (pDepthStencil != nullptr) - { - const auto& DSVDesc = pDepthStencil->GetDesc(); -#ifdef DEVELOPMENT - if (DSVDesc.ViewType != TEXTURE_VIEW_DEPTH_STENCIL) - LOG_ERROR("Texture view object named '", DSVDesc.Name ? DSVDesc.Name : "", "' has incorrect view type (", GetTexViewTypeLiteralName(DSVDesc.ViewType), "). Depth stencil view is expected"); -#endif - - // Use depth stencil size to set render target size - if (m_FramebufferWidth == 0) - { - auto* pTex = pDepthStencil->GetTexture(); - const auto& TexDesc = pTex->GetDesc(); - m_FramebufferWidth = std::max(TexDesc.Width >> DSVDesc.MostDetailedMip, 1U); - m_FramebufferHeight = std::max(TexDesc.Height >> DSVDesc.MostDetailedMip, 1U); - m_FramebufferSlices = DSVDesc.NumArraySlices; - } - else - { -#ifdef DEVELOPMENT - const auto& TexDesc = pDepthStencil->GetTexture()->GetDesc(); - if (m_FramebufferWidth != std::max(TexDesc.Width >> DSVDesc.MostDetailedMip, 1U)) - LOG_ERROR("Depth-stencil target width (", std::max(TexDesc.Width >> DSVDesc.MostDetailedMip, 1U), ") specified by DSV '", DSVDesc.Name, "' is inconsistent with the width of previously bound render targets (", m_FramebufferWidth, ")"); - if (m_FramebufferHeight != std::max(TexDesc.Height >> DSVDesc.MostDetailedMip, 1U)) - LOG_ERROR("Depth-stencil target height (", std::max(TexDesc.Height >> DSVDesc.MostDetailedMip, 1U), ") specified by DSV '", DSVDesc.Name, "' is inconsistent with the height of previously bound render targets (", m_FramebufferHeight, ")"); - if (m_FramebufferSlices != DSVDesc.NumArraySlices) - LOG_ERROR("Number of slices (", DSVDesc.NumArraySlices, ") specified by DSV '", DSVDesc.Name, "' is inconsistent with the number of slices in previously bound render targets (", m_FramebufferSlices, ")"); -#endif - } - } - - if (m_pBoundDepthStencil != pDepthStencil) - { - m_pBoundDepthStencil = ValidatedCast(pDepthStencil); - bBindRenderTargets = true; - } - - - VERIFY_EXPR(m_FramebufferWidth > 0 && m_FramebufferHeight > 0 && m_FramebufferSlices > 0); - - return bBindRenderTargets; -} - -template -inline void DeviceContextBase:: - GetRenderTargets(Uint32& NumRenderTargets, ITextureView** ppRTVs, ITextureView** ppDSV) -{ - NumRenderTargets = m_NumBoundRenderTargets; - - if (ppRTVs) - { - for (Uint32 rt = 0; rt < NumRenderTargets; ++rt) - { - VERIFY(ppRTVs[rt] == nullptr, "Non-null pointer found in RTV array element #", rt); - auto pBoundRTV = m_pBoundRenderTargets[rt]; - if (pBoundRTV) - pBoundRTV->QueryInterface(IID_TextureView, reinterpret_cast(ppRTVs + rt)); - else - ppRTVs[rt] = nullptr; - } - for (Uint32 rt = NumRenderTargets; rt < MAX_RENDER_TARGETS; ++rt) - { - VERIFY(ppRTVs[rt] == nullptr, "Non-null pointer found in RTV array element #", rt); - ppRTVs[rt] = nullptr; - } - } - - if (ppDSV) - { - VERIFY(*ppDSV == nullptr, "Non-null DSV pointer found"); - if (m_pBoundDepthStencil) - m_pBoundDepthStencil->QueryInterface(IID_TextureView, reinterpret_cast(ppDSV)); - else - *ppDSV = nullptr; - } -} - -template -inline void DeviceContextBase::ClearStateCache() -{ - for (Uint32 stream = 0; stream < m_NumVertexStreams; ++stream) - m_VertexStreams[stream] = VertexStreamInfo{}; -#ifdef _DEBUG - for (Uint32 stream = m_NumVertexStreams; stream < _countof(m_VertexStreams); ++stream) - { - VERIFY(m_VertexStreams[stream].pBuffer == nullptr, "Unexpected non-null buffer"); - VERIFY(m_VertexStreams[stream].Offset == 0, "Unexpected non-zero offset"); - } -#endif - m_NumVertexStreams = 0; - - m_pPipelineState.Release(); - - m_pIndexBuffer.Release(); - m_IndexDataStartOffset = 0; - - m_StencilRef = 0; - - for (int i = 0; i < 4; ++i) - m_BlendFactors[i] = -1; - - for (Uint32 vp = 0; vp < m_NumViewports; ++vp) - m_Viewports[vp] = Viewport(); - m_NumViewports = 0; - - for (Uint32 sr = 0; sr < m_NumScissorRects; ++sr) - m_ScissorRects[sr] = Rect(); - m_NumScissorRects = 0; - - ResetRenderTargets(); -} - -template -bool DeviceContextBase::CheckIfBoundAsRenderTarget(TextureImplType* pTexture) -{ - if (pTexture == nullptr) - return false; - - for (Uint32 rt = 0; rt < m_NumBoundRenderTargets; ++rt) - { - if (m_pBoundRenderTargets[rt] && m_pBoundRenderTargets[rt]->GetTexture() == pTexture) - { - return true; - } - } - - return false; -} - -template -bool DeviceContextBase::CheckIfBoundAsDepthStencil(TextureImplType* pTexture) -{ - if (pTexture == nullptr) - return false; - - return m_pBoundDepthStencil && m_pBoundDepthStencil->GetTexture() == pTexture; -} - -template -bool DeviceContextBase::UnbindTextureFromFramebuffer(TextureImplType* pTexture, bool bShowMessage) -{ - if (pTexture == nullptr) - return false; - - const auto& TexDesc = pTexture->GetDesc(); - - bool bResetRenderTargets = false; - if (TexDesc.BindFlags & BIND_RENDER_TARGET) - { - if (CheckIfBoundAsRenderTarget(pTexture)) - { - if (bShowMessage) - { - LOG_INFO_MESSAGE("Texture '", TexDesc.Name, - "' is currently bound as render target and will be unset along with all " - "other render targets and depth-stencil buffer. " - "Call SetRenderTargets() to reset the render targets.\n" - "To silence this message, explicitly unbind the texture with " - "SetRenderTargets(0, nullptr, nullptr, RESOURCE_STATE_TRANSITION_MODE_NONE)"); - } - - bResetRenderTargets = true; - } - } - - if (TexDesc.BindFlags & BIND_DEPTH_STENCIL) - { - if (CheckIfBoundAsDepthStencil(pTexture)) - { - if (bShowMessage) - { - LOG_INFO_MESSAGE("Texture '", TexDesc.Name, - "' is currently bound as depth buffer and will be unset along with " - "all render targets. Call SetRenderTargets() to reset the render targets.\n" - "To silence this message, explicitly unbind the texture with " - "SetRenderTargets(0, nullptr, nullptr, RESOURCE_STATE_TRANSITION_MODE_NONE)"); - } - - bResetRenderTargets = true; - } - } - - if (bResetRenderTargets) - { - ResetRenderTargets(); - } - - return bResetRenderTargets; -} - -template -void DeviceContextBase::ResetRenderTargets() -{ - for (Uint32 rt = 0; rt < m_NumBoundRenderTargets; ++rt) - m_pBoundRenderTargets[rt].Release(); -#ifdef _DEBUG - for (Uint32 rt = m_NumBoundRenderTargets; rt < _countof(m_pBoundRenderTargets); ++rt) - { - VERIFY(m_pBoundRenderTargets[rt] == nullptr, "Non-null render target found"); - } -#endif - m_NumBoundRenderTargets = 0; - m_FramebufferWidth = 0; - m_FramebufferHeight = 0; - m_FramebufferSlices = 0; - - m_pBoundDepthStencil.Release(); -} - - -template -inline bool DeviceContextBase::ClearDepthStencil(ITextureView* pView) -{ - if (pView == nullptr) - { - LOG_ERROR_MESSAGE("Depth-stencil view to clear must not be null"); - return false; - } - -#ifdef DEVELOPMENT - { - const auto& ViewDesc = pView->GetDesc(); - if (ViewDesc.ViewType != TEXTURE_VIEW_DEPTH_STENCIL) - { - LOG_ERROR_MESSAGE("The type (", GetTexViewTypeLiteralName(ViewDesc.ViewType), ") of the texture view '", ViewDesc.Name, - "' is invalid: ClearDepthStencil command expects depth-stencil view (TEXTURE_VIEW_DEPTH_STENCIL)."); - return false; - } - - if (pView != m_pBoundDepthStencil) - { - if (m_pDevice->GetDeviceCaps().IsGLDevice()) - { - LOG_ERROR_MESSAGE("Depth-stencil view '", ViewDesc.Name, - "' is not bound to the device context. ClearDepthStencil command requires " - "depth-stencil view be bound to the device contex in OpenGL backend"); - return false; - } - else - { - LOG_WARNING_MESSAGE("Depth-stencil view '", ViewDesc.Name, - "' is not bound to the device context. " - "ClearDepthStencil command is more efficient when depth-stencil " - "view is bound to the context. In OpenGL backend this is a requirement."); - } - } - } -#endif - - return true; -} - -template -inline bool DeviceContextBase::ClearRenderTarget(ITextureView* pView) -{ - if (pView == nullptr) - { - LOG_ERROR_MESSAGE("Render target view to clear must not be null"); - return false; - } - -#ifdef DEVELOPMENT - { - const auto& ViewDesc = pView->GetDesc(); - if (ViewDesc.ViewType != TEXTURE_VIEW_RENDER_TARGET) - { - LOG_ERROR_MESSAGE("The type (", GetTexViewTypeLiteralName(ViewDesc.ViewType), ") of texture view '", pView->GetDesc().Name, - "' is invalid: ClearRenderTarget command expects render target view (TEXTURE_VIEW_RENDER_TARGET)."); - return false; - } - - bool RTFound = false; - for (Uint32 i = 0; i < m_NumBoundRenderTargets && !RTFound; ++i) - { - RTFound = m_pBoundRenderTargets[i] == pView; - } - if (!RTFound) - { - if (m_pDevice->GetDeviceCaps().IsGLDevice()) - { - LOG_ERROR_MESSAGE("Render target view '", ViewDesc.Name, - "' is not bound to the device context. ClearRenderTarget command " - "requires render target view be bound to the device contex in OpenGL backend"); - return false; - } - else - { - LOG_WARNING_MESSAGE("Render target view '", ViewDesc.Name, - "' is not bound to the device context. ClearRenderTarget command is more efficient " - "if render target view is bound to the device context. In OpenGL backend this is a requirement."); - } - } - } -#endif - - return true; -} - -template -inline bool DeviceContextBase::BeginQuery(IQuery* pQuery, int) -{ - if (pQuery == nullptr) - { - LOG_ERROR_MESSAGE("IDeviceContext::BeginQuery: pQuery must not be null"); - return false; - } - - if (m_bIsDeferred) - { - LOG_ERROR_MESSAGE("IDeviceContext::BeginQuery: Deferred contexts do not support queries"); - return false; - } - - if (pQuery->GetDesc().Type == QUERY_TYPE_TIMESTAMP) - { - LOG_ERROR_MESSAGE("BeginQuery() is disabled for timestamp queries. Call EndQuery() to set the timestamp."); - return false; - } - - if (!ValidatedCast(pQuery)->OnBeginQuery(this)) - return false; - - return true; -} - -template -inline bool DeviceContextBase::EndQuery(IQuery* pQuery, int) -{ - if (pQuery == nullptr) - { - LOG_ERROR_MESSAGE("IDeviceContext::EndQuery: pQuery must not be null"); - return false; - } - - if (m_bIsDeferred) - { - LOG_ERROR_MESSAGE("IDeviceContext::EndQuery: Deferred contexts do not support queries"); - return false; - } - - if (!ValidatedCast(pQuery)->OnEndQuery(this)) - return false; - - return true; -} - -template -inline void DeviceContextBase:: - UpdateBuffer(IBuffer* pBuffer, Uint32 Offset, Uint32 Size, const void* pData, RESOURCE_STATE_TRANSITION_MODE StateTransitionMode) -{ - VERIFY(pBuffer != nullptr, "Buffer must not be null"); -#ifdef DEVELOPMENT - const auto& BuffDesc = ValidatedCast(pBuffer)->GetDesc(); - DEV_CHECK_ERR(BuffDesc.Usage == USAGE_DEFAULT, "Unable to update buffer '", BuffDesc.Name, "': only USAGE_DEFAULT buffers can be updated with UpdateData()"); - DEV_CHECK_ERR(Offset < BuffDesc.uiSizeInBytes, "Unable to update buffer '", BuffDesc.Name, "': offset (", Offset, ") exceeds the buffer size (", BuffDesc.uiSizeInBytes, ")"); - DEV_CHECK_ERR(Size + Offset <= BuffDesc.uiSizeInBytes, "Unable to update buffer '", BuffDesc.Name, "': Update region [", Offset, ",", Size + Offset, ") is out of buffer bounds [0,", BuffDesc.uiSizeInBytes, ")"); -#endif -} - -template -inline void DeviceContextBase:: - CopyBuffer(IBuffer* pSrcBuffer, - Uint32 SrcOffset, - RESOURCE_STATE_TRANSITION_MODE SrcBufferTransitionMode, - IBuffer* pDstBuffer, - Uint32 DstOffset, - Uint32 Size, - RESOURCE_STATE_TRANSITION_MODE DstBufferTransitionMode) -{ - VERIFY(pSrcBuffer != nullptr, "Source buffer must not be null"); - VERIFY(pDstBuffer != nullptr, "Destination buffer must not be null"); -#ifdef DEVELOPMENT - const auto& SrcBufferDesc = ValidatedCast(pSrcBuffer)->GetDesc(); - const auto& DstBufferDesc = ValidatedCast(pDstBuffer)->GetDesc(); - DEV_CHECK_ERR(DstOffset + Size <= DstBufferDesc.uiSizeInBytes, "Failed to copy buffer '", SrcBufferDesc.Name, "' to '", DstBufferDesc.Name, "': Destination range [", DstOffset, ",", DstOffset + Size, ") is out of buffer bounds [0,", DstBufferDesc.uiSizeInBytes, ")"); - DEV_CHECK_ERR(SrcOffset + Size <= SrcBufferDesc.uiSizeInBytes, "Failed to copy buffer '", SrcBufferDesc.Name, "' to '", DstBufferDesc.Name, "': Source range [", SrcOffset, ",", SrcOffset + Size, ") is out of buffer bounds [0,", SrcBufferDesc.uiSizeInBytes, ")"); -#endif -} - -template -inline void DeviceContextBase:: - MapBuffer(IBuffer* pBuffer, MAP_TYPE MapType, MAP_FLAGS MapFlags, PVoid& pMappedData) -{ - VERIFY(pBuffer, "pBuffer must not be null"); - - const auto& BuffDesc = pBuffer->GetDesc(); - -#ifdef _DEBUG - VERIFY(m_DbgMappedBuffers.find(pBuffer) == m_DbgMappedBuffers.end(), "Buffer '", BuffDesc.Name, "' has already been mapped"); - m_DbgMappedBuffers[pBuffer] = DbgMappedBufferInfo{MapType}; -#endif - - pMappedData = nullptr; - switch (MapType) - { - case MAP_READ: - DEV_CHECK_ERR(BuffDesc.Usage == USAGE_STAGING, "Only buffers with usage USAGE_STAGING can be read from"); - DEV_CHECK_ERR((BuffDesc.CPUAccessFlags & CPU_ACCESS_READ), "Buffer being mapped for reading was not created with CPU_ACCESS_READ flag"); - DEV_CHECK_ERR((MapFlags & MAP_FLAG_DISCARD) == 0, "MAP_FLAG_DISCARD is not valid when mapping buffer for reading"); - break; - - case MAP_WRITE: - DEV_CHECK_ERR(BuffDesc.Usage == USAGE_DYNAMIC || BuffDesc.Usage == USAGE_STAGING, "Only buffers with usage USAGE_STAGING or USAGE_DYNAMIC can be mapped for writing"); - DEV_CHECK_ERR((BuffDesc.CPUAccessFlags & CPU_ACCESS_WRITE), "Buffer being mapped for writing was not created with CPU_ACCESS_WRITE flag"); - break; - - case MAP_READ_WRITE: - DEV_CHECK_ERR(BuffDesc.Usage == USAGE_STAGING, "Only buffers with usage USAGE_STAGING can be mapped for reading and writing"); - DEV_CHECK_ERR((BuffDesc.CPUAccessFlags & CPU_ACCESS_WRITE), "Buffer being mapped for reading & writing was not created with CPU_ACCESS_WRITE flag"); - DEV_CHECK_ERR((BuffDesc.CPUAccessFlags & CPU_ACCESS_READ), "Buffer being mapped for reading & writing was not created with CPU_ACCESS_READ flag"); - DEV_CHECK_ERR((MapFlags & MAP_FLAG_DISCARD) == 0, "MAP_FLAG_DISCARD is not valid when mapping buffer for reading and writing"); - break; - - default: UNEXPECTED("Unknown map type"); - } - - if (BuffDesc.Usage == USAGE_DYNAMIC) - { - DEV_CHECK_ERR((MapFlags & (MAP_FLAG_DISCARD | MAP_FLAG_NO_OVERWRITE)) != 0 && MapType == MAP_WRITE, "Dynamic buffers can only be mapped for writing with MAP_FLAG_DISCARD or MAP_FLAG_NO_OVERWRITE flag"); - DEV_CHECK_ERR((MapFlags & (MAP_FLAG_DISCARD | MAP_FLAG_NO_OVERWRITE)) != (MAP_FLAG_DISCARD | MAP_FLAG_NO_OVERWRITE), "When mapping dynamic buffer, only one of MAP_FLAG_DISCARD or MAP_FLAG_NO_OVERWRITE flags must be specified"); - } - - if ((MapFlags & MAP_FLAG_DISCARD) != 0) - { - DEV_CHECK_ERR(BuffDesc.Usage == USAGE_DYNAMIC || BuffDesc.Usage == USAGE_STAGING, "Only dynamic and staging buffers can be mapped with discard flag"); - DEV_CHECK_ERR(MapType == MAP_WRITE, "MAP_FLAG_DISCARD is only valid when mapping buffer for writing"); - } -} - -template -inline void DeviceContextBase:: - UnmapBuffer(IBuffer* pBuffer, MAP_TYPE MapType) -{ - VERIFY(pBuffer, "pBuffer must not be null"); -#ifdef _DEBUG - auto MappedBufferIt = m_DbgMappedBuffers.find(pBuffer); - VERIFY(MappedBufferIt != m_DbgMappedBuffers.end(), "Buffer '", pBuffer->GetDesc().Name, "' has not been mapped."); - VERIFY(MappedBufferIt->second.MapType == MapType, "MapType (", MapType, ") does not match the map type that was used to map the buffer ", MappedBufferIt->second.MapType); - m_DbgMappedBuffers.erase(MappedBufferIt); -#endif -} - - -template -inline void DeviceContextBase:: - UpdateTexture(ITexture* pTexture, Uint32 MipLevel, Uint32 Slice, const Box& DstBox, const TextureSubResData& SubresData, RESOURCE_STATE_TRANSITION_MODE SrcBufferTransitionMode, RESOURCE_STATE_TRANSITION_MODE TextureTransitionMode) -{ - VERIFY(pTexture != nullptr, "pTexture must not be null"); - ValidateUpdateTextureParams(pTexture->GetDesc(), MipLevel, Slice, DstBox, SubresData); -} - -template -inline void DeviceContextBase:: - CopyTexture(const CopyTextureAttribs& CopyAttribs) -{ - VERIFY(CopyAttribs.pSrcTexture, "Src texture must not be null"); - VERIFY(CopyAttribs.pDstTexture, "Dst texture must not be null"); - ValidateCopyTextureParams(CopyAttribs); -} - -template -inline void DeviceContextBase:: - MapTextureSubresource(ITexture* pTexture, - Uint32 MipLevel, - Uint32 ArraySlice, - MAP_TYPE MapType, - MAP_FLAGS MapFlags, - const Box* pMapRegion, - MappedTextureSubresource& MappedData) -{ - VERIFY(pTexture, "pTexture must not be null"); - ValidateMapTextureParams(pTexture->GetDesc(), MipLevel, ArraySlice, MapType, MapFlags, pMapRegion); -} - -template -inline void DeviceContextBase:: - UnmapTextureSubresource(ITexture* pTexture, Uint32 MipLevel, Uint32 ArraySlice) -{ - VERIFY(pTexture, "pTexture must not be null"); - DEV_CHECK_ERR(MipLevel < pTexture->GetDesc().MipLevels, "Mip level is out of range"); - DEV_CHECK_ERR(ArraySlice < pTexture->GetDesc().ArraySize, "Array slice is out of range"); -} - -template -inline void DeviceContextBase:: - GenerateMips(ITextureView* pTexView) -{ - VERIFY(pTexView != nullptr, "pTexView must not be null"); -#ifdef DEVELOPMENT - const auto& ViewDesc = pTexView->GetDesc(); - DEV_CHECK_ERR(ViewDesc.ViewType == TEXTURE_VIEW_SHADER_RESOURCE, "Shader resource view '", ViewDesc.Name, - "' can't be used to generate mipmaps because its type is ", GetTexViewTypeLiteralName(ViewDesc.ViewType), ". Required view type: TEXTURE_VIEW_SHADER_RESOURCE."); - DEV_CHECK_ERR((ViewDesc.Flags & TEXTURE_VIEW_FLAG_ALLOW_MIP_MAP_GENERATION) != 0, "Shader resource view '", ViewDesc.Name, - "' was not created with TEXTURE_VIEW_FLAG_ALLOW_MIP_MAP_GENERATION flag and can't be used to generate mipmaps."); -#endif -} - - -template -void DeviceContextBase:: - ResolveTextureSubresource(ITexture* pSrcTexture, - ITexture* pDstTexture, - const ResolveTextureSubresourceAttribs& ResolveAttribs) -{ -#ifdef DEVELOPMENT - VERIFY_EXPR(pSrcTexture != nullptr && pDstTexture != nullptr); - const auto& SrcTexDesc = pSrcTexture->GetDesc(); - const auto& DstTexDesc = pDstTexture->GetDesc(); - DEV_CHECK_ERR(SrcTexDesc.SampleCount > 1, - "Source texture '", SrcTexDesc.Name, "' of a resolve operation is not multi-sampled"); - DEV_CHECK_ERR(DstTexDesc.SampleCount == 1, - "Destination texture '", DstTexDesc.Name, "' of a resolve operation is multi-sampled"); - auto SrcMipLevelProps = GetMipLevelProperties(SrcTexDesc, ResolveAttribs.SrcMipLevel); - auto DstMipLevelProps = GetMipLevelProperties(DstTexDesc, ResolveAttribs.DstMipLevel); - DEV_CHECK_ERR(SrcMipLevelProps.LogicalWidth == DstMipLevelProps.LogicalWidth && SrcMipLevelProps.LogicalHeight == DstMipLevelProps.LogicalHeight, - "The size (", SrcMipLevelProps.LogicalWidth, "x", SrcMipLevelProps.LogicalHeight, - ") of the source subresource of a resolve operation (texture '", - SrcTexDesc.Name, "', mip ", ResolveAttribs.SrcMipLevel, ", slice ", ResolveAttribs.SrcSlice, - ") does not match the size (", DstMipLevelProps.LogicalWidth, "x", DstMipLevelProps.LogicalHeight, - ") of the destination subresource (texture '", DstTexDesc.Name, "', mip ", ResolveAttribs.DstMipLevel, ", slice ", - ResolveAttribs.DstSlice, ")"); - - const auto& SrcFmtAttribs = GetTextureFormatAttribs(SrcTexDesc.Format); - const auto& DstFmtAttribs = GetTextureFormatAttribs(DstTexDesc.Format); - const auto& ResolveFmtAttribs = GetTextureFormatAttribs(ResolveAttribs.Format); - if (!SrcFmtAttribs.IsTypeless && !DstFmtAttribs.IsTypeless) - { - DEV_CHECK_ERR(SrcTexDesc.Format == DstTexDesc.Format, - "Source (", SrcFmtAttribs.Name, ") and destination (", DstFmtAttribs.Name, - ") texture formats of a resolve operation must match exaclty or be compatible typeless formats"); - DEV_CHECK_ERR(ResolveAttribs.Format == TEX_FORMAT_UNKNOWN || SrcTexDesc.Format == ResolveAttribs.Format, "Invalid format of a resolve operation"); - } - if (SrcFmtAttribs.IsTypeless && DstFmtAttribs.IsTypeless) - { - DEV_CHECK_ERR(ResolveAttribs.Format != TEX_FORMAT_UNKNOWN, - "Format of a resolve operation must not be unknown when both src and dst texture formats are typeless"); - } - if (SrcFmtAttribs.IsTypeless || DstFmtAttribs.IsTypeless) - { - DEV_CHECK_ERR(!ResolveFmtAttribs.IsTypeless, - "Format of a resolve operation must not be typeless when one of the texture formats is typeless"); - } -#endif -} - -#ifdef DEVELOPMENT -template -inline bool DeviceContextBase:: - DvpVerifyDrawArguments(const DrawAttribs& Attribs) const -{ - if ((Attribs.Flags & DRAW_FLAG_VERIFY_DRAW_ATTRIBS) == 0) - return true; - - if (!m_pPipelineState) - { - LOG_ERROR_MESSAGE("Draw command arguments are invalid: no pipeline state is bound."); - return false; - } - - if (m_pPipelineState->GetDesc().IsComputePipeline) - { - LOG_ERROR_MESSAGE("Draw command arguments are invalid: pipeline state '", m_pPipelineState->GetDesc().Name, "' is a compute pipeline."); - return false; - } - - if (Attribs.NumVertices == 0) - { - LOG_WARNING_MESSAGE("Draw command arguments are invalid: number of vertices to draw is zero."); - } - - return true; -} - -template -inline bool DeviceContextBase:: - DvpVerifyDrawIndexedArguments(const DrawIndexedAttribs& Attribs) const -{ - if ((Attribs.Flags & DRAW_FLAG_VERIFY_DRAW_ATTRIBS) == 0) - return true; - - if (!m_pPipelineState) - { - LOG_ERROR_MESSAGE("DrawIndexed command arguments are invalid: no pipeline state is bound."); - return false; - } - - if (m_pPipelineState->GetDesc().IsComputePipeline) - { - LOG_ERROR_MESSAGE("DrawIndexed command arguments are invalid: pipeline state '", - m_pPipelineState->GetDesc().Name, "' is a compute pipeline."); - return false; - } - - if (Attribs.IndexType != VT_UINT16 && Attribs.IndexType != VT_UINT32) - { - LOG_ERROR_MESSAGE("DrawIndexed command arguments are invalid: IndexType (", - GetValueTypeString(Attribs.IndexType), ") must be VT_UINT16 or VT_UINT32."); - return false; - } - - if (!m_pIndexBuffer) - { - LOG_ERROR_MESSAGE("DrawIndexed command arguments are invalid: no index buffer is bound."); - return false; - } - - if (Attribs.NumIndices == 0) - { - LOG_WARNING_MESSAGE("DrawIndexed command arguments are invalid: number of indices to draw is zero."); - } - - return true; -} - -template -inline bool DeviceContextBase:: - DvpVerifyDrawIndirectArguments(const DrawIndirectAttribs& Attribs, const IBuffer* pAttribsBuffer) const -{ - if ((Attribs.Flags & DRAW_FLAG_VERIFY_DRAW_ATTRIBS) == 0) - return true; - - if (!m_pPipelineState) - { - LOG_ERROR_MESSAGE("DrawIndirect command arguments are invalid: no pipeline state is bound."); - return false; - } - - if (m_pPipelineState->GetDesc().IsComputePipeline) - { - - LOG_ERROR_MESSAGE("DrawIndirect command arguments are invalid: pipeline state '", - m_pPipelineState->GetDesc().Name, "' is a compute pipeline."); - return false; - } - - if (pAttribsBuffer != nullptr) - { - if ((pAttribsBuffer->GetDesc().BindFlags & BIND_INDIRECT_DRAW_ARGS) == 0) - { - LOG_ERROR_MESSAGE("DrawIndirect command arguments are invalid: indirect draw arguments buffer '", - pAttribsBuffer->GetDesc().Name, "' was not created with BIND_INDIRECT_DRAW_ARGS flag."); - return false; - } - } - else - { - LOG_ERROR_MESSAGE("DrawIndirect command arguments are invalid: indirect draw arguments buffer is null."); - return false; - } - - return true; -} - -template -inline bool DeviceContextBase:: - DvpVerifyDrawIndexedIndirectArguments(const DrawIndexedIndirectAttribs& Attribs, const IBuffer* pAttribsBuffer) const -{ - if ((Attribs.Flags & DRAW_FLAG_VERIFY_DRAW_ATTRIBS) == 0) - return true; - - if (!m_pPipelineState) - { - LOG_ERROR_MESSAGE("DrawIndexedIndirect command arguments are invalid: no pipeline state is bound."); - return false; - } - - if (m_pPipelineState->GetDesc().IsComputePipeline) - { - LOG_ERROR_MESSAGE("DrawIndexedIndirect command arguments are invalid: pipeline state '", - m_pPipelineState->GetDesc().Name, "' is a compute pipeline."); - return false; - } - - if (Attribs.IndexType != VT_UINT16 && Attribs.IndexType != VT_UINT32) - { - LOG_ERROR_MESSAGE("DrawIndexedIndirect command arguments are invalid: IndexType (", - GetValueTypeString(Attribs.IndexType), ") must be VT_UINT16 or VT_UINT32."); - return false; - } - - if (!m_pIndexBuffer) - { - LOG_ERROR_MESSAGE("DrawIndexedIndirect command arguments are invalid: no index buffer is bound."); - return false; - } - - if (pAttribsBuffer != nullptr) - { - if ((pAttribsBuffer->GetDesc().BindFlags & BIND_INDIRECT_DRAW_ARGS) == 0) - { - LOG_ERROR_MESSAGE("DrawIndexedIndirect command arguments are invalid: indirect draw arguments buffer '", - pAttribsBuffer->GetDesc().Name, "' was not created with BIND_INDIRECT_DRAW_ARGS flag."); - return false; - } - } - else - { - LOG_ERROR_MESSAGE("DrawIndexedIndirect command arguments are invalid: indirect draw arguments buffer is null."); - return false; - } - - return true; -} - -template -inline void DeviceContextBase:: - DvpVerifyRenderTargets() const -{ - if (!m_pPipelineState) - { - LOG_ERROR("No pipeline state is bound"); - return; - } - - TEXTURE_FORMAT BoundRTVFormats[8] = {TEX_FORMAT_UNKNOWN}; - TEXTURE_FORMAT BoundDSVFormat = TEX_FORMAT_UNKNOWN; - - for (Uint32 rt = 0; rt < m_NumBoundRenderTargets; ++rt) - { - if (auto* pRT = m_pBoundRenderTargets[rt].RawPtr()) - BoundRTVFormats[rt] = pRT->GetDesc().Format; - else - BoundRTVFormats[rt] = TEX_FORMAT_UNKNOWN; - } - - BoundDSVFormat = m_pBoundDepthStencil ? m_pBoundDepthStencil->GetDesc().Format : TEX_FORMAT_UNKNOWN; - - const auto& PSODesc = m_pPipelineState->GetDesc(); - const auto& GraphicsPipeline = PSODesc.GraphicsPipeline; - if (GraphicsPipeline.NumRenderTargets != m_NumBoundRenderTargets) - { - LOG_WARNING_MESSAGE("Number of currently bound render targets (", m_NumBoundRenderTargets, - ") does not match the number of outputs specified by the PSO '", PSODesc.Name, - "' (", Uint32{GraphicsPipeline.NumRenderTargets}, ")."); - } - - if (BoundDSVFormat != GraphicsPipeline.DSVFormat) - { - LOG_WARNING_MESSAGE("Currently bound depth-stencil buffer format (", GetTextureFormatAttribs(BoundDSVFormat).Name, - ") does not match the DSV format specified by the PSO '", PSODesc.Name, - "' (", GetTextureFormatAttribs(GraphicsPipeline.DSVFormat).Name, ")."); - } - - for (Uint32 rt = 0; rt < m_NumBoundRenderTargets; ++rt) - { - auto BoundFmt = BoundRTVFormats[rt]; - auto PSOFmt = GraphicsPipeline.RTVFormats[rt]; - if (BoundFmt != PSOFmt) - { - LOG_WARNING_MESSAGE("Render target bound to slot ", rt, " (", GetTextureFormatAttribs(BoundFmt).Name, - ") does not match the RTV format specified by the PSO '", PSODesc.Name, - "' (", GetTextureFormatAttribs(PSOFmt).Name, ")."); - } - } -} - - - -template -inline bool DeviceContextBase:: - DvpVerifyDispatchArguments(const DispatchComputeAttribs& Attribs) const -{ - if (!m_pPipelineState) - { - LOG_ERROR_MESSAGE("DispatchCompute command arguments are invalid: no pipeline state is bound."); - return false; - } - - if (!m_pPipelineState->GetDesc().IsComputePipeline) - { - LOG_ERROR_MESSAGE("DispatchCompute command arguments are invalid: pipeline state '", m_pPipelineState->GetDesc().Name, - "' is a graphics pipeline."); - return false; - } - - if (Attribs.ThreadGroupCountX == 0) - LOG_WARNING_MESSAGE("DispatchCompute command arguments are invalid: ThreadGroupCountX is zero."); - - if (Attribs.ThreadGroupCountY == 0) - LOG_WARNING_MESSAGE("DispatchCompute command arguments are invalid: ThreadGroupCountY is zero."); - - if (Attribs.ThreadGroupCountZ == 0) - LOG_WARNING_MESSAGE("DispatchCompute command arguments are invalid: ThreadGroupCountZ is zero."); - - return true; -} - -template -inline bool DeviceContextBase:: - DvpVerifyDispatchIndirectArguments(const DispatchComputeIndirectAttribs& Attribs, const IBuffer* pAttribsBuffer) const -{ - if (!m_pPipelineState) - { - LOG_ERROR_MESSAGE("DispatchComputeIndirect command arguments are invalid: no pipeline state is bound."); - return false; - } - - if (!m_pPipelineState->GetDesc().IsComputePipeline) - { - LOG_ERROR_MESSAGE("DispatchComputeIndirect command arguments are invalid: pipeline state '", - m_pPipelineState->GetDesc().Name, "' is a graphics pipeline."); - return false; - } - - if (pAttribsBuffer != nullptr) - { - if ((pAttribsBuffer->GetDesc().BindFlags & BIND_INDIRECT_DRAW_ARGS) == 0) - { - LOG_ERROR_MESSAGE("DispatchComputeIndirect command arguments are invalid: indirect dispatch arguments buffer '", - pAttribsBuffer->GetDesc().Name, "' was not created with BIND_INDIRECT_DRAW_ARGS flag."); - return false; - } - } - else - { - LOG_ERROR_MESSAGE("DispatchComputeIndirect command arguments are invalid: indirect dispatch arguments buffer is null."); - return false; - } - - return true; -} - - -template -void DeviceContextBase:: - DvpVerifyStateTransitionDesc(const StateTransitionDesc& Barrier) const -{ - DEV_CHECK_ERR((Barrier.pTexture != nullptr) ^ (Barrier.pBuffer != nullptr), "Exactly one of pTexture or pBuffer members of StateTransitionDesc must not be null"); - DEV_CHECK_ERR(Barrier.NewState != RESOURCE_STATE_UNKNOWN, "New resource state can't be unknown"); - RESOURCE_STATE OldState = RESOURCE_STATE_UNKNOWN; - if (Barrier.pTexture) - { - const auto& TexDesc = Barrier.pTexture->GetDesc(); - - DEV_CHECK_ERR(VerifyResourceStates(Barrier.NewState, true), "Invlaid new state specified for texture '", TexDesc.Name, "'"); - OldState = Barrier.OldState != RESOURCE_STATE_UNKNOWN ? Barrier.OldState : Barrier.pTexture->GetState(); - DEV_CHECK_ERR(OldState != RESOURCE_STATE_UNKNOWN, - "The state of texture '", TexDesc.Name, - "' is unknown to the engine and is not explicitly specified in the barrier"); - DEV_CHECK_ERR(VerifyResourceStates(OldState, true), "Invlaid old state specified for texture '", TexDesc.Name, "'"); - - DEV_CHECK_ERR(Barrier.FirstMipLevel < TexDesc.MipLevels, "First mip level (", Barrier.FirstMipLevel, - ") specified by the barrier is out of range. Texture '", - TexDesc.Name, "' has only ", TexDesc.MipLevels, " mip level(s)"); - DEV_CHECK_ERR(Barrier.MipLevelsCount == REMAINING_MIP_LEVELS || Barrier.FirstMipLevel + Barrier.MipLevelsCount <= TexDesc.MipLevels, - "Mip level range ", Barrier.FirstMipLevel, "..", Barrier.FirstMipLevel + Barrier.MipLevelsCount - 1, - " specified by the barrier is out of range. Texture '", - TexDesc.Name, "' has only ", TexDesc.MipLevels, " mip level(s)"); - - DEV_CHECK_ERR(Barrier.FirstArraySlice < TexDesc.ArraySize, "First array slice (", Barrier.FirstArraySlice, - ") specified by the barrier is out of range. Array size of texture '", - TexDesc.Name, "' is ", TexDesc.ArraySize); - DEV_CHECK_ERR(Barrier.ArraySliceCount == REMAINING_ARRAY_SLICES || Barrier.FirstArraySlice + Barrier.ArraySliceCount <= TexDesc.ArraySize, - "Array slice range ", Barrier.FirstArraySlice, "..", Barrier.FirstArraySlice + Barrier.ArraySliceCount - 1, - " specified by the barrier is out of range. Array size of texture '", - TexDesc.Name, "' is ", TexDesc.ArraySize); - - auto DevType = m_pDevice->GetDeviceCaps().DevType; - if (DevType != RENDER_DEVICE_TYPE_D3D12 && DevType != RENDER_DEVICE_TYPE_VULKAN) - { - DEV_CHECK_ERR(Barrier.FirstMipLevel == 0 && (Barrier.MipLevelsCount == REMAINING_MIP_LEVELS || Barrier.MipLevelsCount == TexDesc.MipLevels), - "Failed to transition texture '", TexDesc.Name, "': only whole resources can be transitioned on this device"); - DEV_CHECK_ERR(Barrier.FirstArraySlice == 0 && (Barrier.ArraySliceCount == REMAINING_ARRAY_SLICES || Barrier.ArraySliceCount == TexDesc.ArraySize), - "Failed to transition texture '", TexDesc.Name, "': only whole resources can be transitioned on this device"); - } - } - else - { - const auto& BuffDesc = Barrier.pBuffer->GetDesc(); - DEV_CHECK_ERR(VerifyResourceStates(Barrier.NewState, false), "Invlaid new state specified for buffer '", BuffDesc.Name, "'"); - OldState = Barrier.OldState != RESOURCE_STATE_UNKNOWN ? Barrier.OldState : Barrier.pBuffer->GetState(); - DEV_CHECK_ERR(OldState != RESOURCE_STATE_UNKNOWN, "The state of buffer '", BuffDesc.Name, "' is unknown to the engine and is not explicitly specified in the barrier"); - DEV_CHECK_ERR(VerifyResourceStates(OldState, false), "Invlaid old state specified for buffer '", BuffDesc.Name, "'"); - } - - if (OldState == RESOURCE_STATE_UNORDERED_ACCESS && Barrier.NewState == RESOURCE_STATE_UNORDERED_ACCESS) - { - DEV_CHECK_ERR(Barrier.TransitionType == STATE_TRANSITION_TYPE_IMMEDIATE, "For UAV barriers, transition type must be STATE_TRANSITION_TYPE_IMMEDIATE"); - } - - if (Barrier.TransitionType == STATE_TRANSITION_TYPE_BEGIN) - { - DEV_CHECK_ERR(!Barrier.UpdateResourceState, "Resource state can't be updated in begin-split barrier"); - } -} - -template -bool DeviceContextBase:: - DvpVerifyTextureState(const TextureImplType& Texture, RESOURCE_STATE RequiredState, const char* OperationName) const -{ - if (Texture.IsInKnownState() && !Texture.CheckState(RequiredState)) - { - LOG_ERROR_MESSAGE(OperationName, " requires texture '", Texture.GetDesc().Name, "' to be transitioned to ", GetResourceStateString(RequiredState), - " state. Actual texture state: ", GetResourceStateString(Texture.GetState()), - ". Use appropriate state transiton flags or explicitly transition the texture using IDeviceContext::TransitionResourceStates() method."); - return false; - } - - return true; -} - -template -bool DeviceContextBase:: - DvpVerifyBufferState(const BufferImplType& Buffer, RESOURCE_STATE RequiredState, const char* OperationName) const -{ - if (Buffer.IsInKnownState() && !Buffer.CheckState(RequiredState)) - { - LOG_ERROR_MESSAGE(OperationName, " requires buffer '", Buffer.GetDesc().Name, "' to be transitioned to ", GetResourceStateString(RequiredState), - " state. Actual buffer state: ", GetResourceStateString(Buffer.GetState()), - ". Use appropriate state transiton flags or explicitly transition the buffer using IDeviceContext::TransitionResourceStates() method."); - return false; - } - - return true; -} - -#endif // DEVELOPMENT - -} // namespace Diligent diff --git a/Graphics/GraphicsEngine/include/DeviceContextBase.hpp b/Graphics/GraphicsEngine/include/DeviceContextBase.hpp new file mode 100644 index 00000000..87604f84 --- /dev/null +++ b/Graphics/GraphicsEngine/include/DeviceContextBase.hpp @@ -0,0 +1,1518 @@ +/* + * Copyright 2019-2020 Diligent Graphics LLC + * Copyright 2015-2019 Egor Yusov + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + * + * In no event and under no legal theory, whether in tort (including negligence), + * contract, or otherwise, unless required by applicable law (such as deliberate + * and grossly negligent acts) or agreed to in writing, shall any Contributor be + * liable for any damages, including any direct, indirect, special, incidental, + * or consequential damages of any character arising as a result of this License or + * out of the use or inability to use the software (including but not limited to damages + * for loss of goodwill, work stoppage, computer failure or malfunction, or any and + * all other commercial damages or losses), even if such Contributor has been advised + * of the possibility of such damages. + */ + +#pragma once + +/// \file +/// Implementation of the Diligent::DeviceContextBase template class and related structures + +#include + +#include "DeviceContext.h" +#include "DeviceObjectBase.hpp" +#include "ResourceMapping.h" +#include "Sampler.h" +#include "ObjectBase.h" +#include "DebugUtilities.h" +#include "ValidatedCast.h" +#include "GraphicsAccessories.hpp" +#include "TextureBase.hpp" + +namespace Diligent +{ + +/// Describes input vertex stream +template +struct VertexStreamInfo +{ + VertexStreamInfo() {} + + /// Strong reference to the buffer object + RefCntAutoPtr pBuffer; + Uint32 Offset = 0; ///< Offset in bytes +}; + +/// Base implementation of the device context. + +/// \tparam BaseInterface - base interface that this class will inheret. +/// \tparam ImplementationTraits - implementation traits that define specific implementation details +/// (texture implemenation type, buffer implementation type, etc.) +/// \remarks Device context keeps strong references to all objects currently bound to +/// the pipeline: buffers, states, samplers, shaders, etc. +/// The context also keeps strong references to the device and +/// the swap chain. +template +class DeviceContextBase : public ObjectBase +{ +public: + using TObjectBase = ObjectBase; + using DeviceImplType = typename ImplementationTraits::DeviceType; + using BufferImplType = typename ImplementationTraits::BufferType; + using TextureImplType = typename ImplementationTraits::TextureType; + using PipelineStateImplType = typename ImplementationTraits::PipelineStateType; + using TextureViewImplType = typename TextureImplType::ViewImplType; + using QueryImplType = typename ImplementationTraits::QueryType; + + /// \param pRefCounters - reference counters object that controls the lifetime of this device context. + /// \param pRenderDevice - render device. + /// \param bIsDeferred - flag indicating if this instance is a deferred context + DeviceContextBase(IReferenceCounters* pRefCounters, DeviceImplType* pRenderDevice, bool bIsDeferred) : + // clang-format off + TObjectBase {pRefCounters }, + m_pDevice {pRenderDevice}, + m_bIsDeferred{bIsDeferred } + // clang-format on + { + } + + ~DeviceContextBase() + { + } + + IMPLEMENT_QUERY_INTERFACE_IN_PLACE(IID_DeviceContext, TObjectBase) + + /// Base implementation of IDeviceContext::SetVertexBuffers(); validates parameters and + /// caches references to the buffers. + inline virtual void SetVertexBuffers(Uint32 StartSlot, + Uint32 NumBuffersSet, + IBuffer** ppBuffers, + Uint32* pOffsets, + RESOURCE_STATE_TRANSITION_MODE StateTransitionMode, + SET_VERTEX_BUFFERS_FLAGS Flags) override = 0; + + inline virtual void InvalidateState() override = 0; + + /// Base implementation of IDeviceContext::CommitShaderResources(); validates parameters. + inline bool CommitShaderResources(IShaderResourceBinding* pShaderResourceBinding, + RESOURCE_STATE_TRANSITION_MODE StateTransitionMode, + int); + + /// Base implementation of IDeviceContext::SetIndexBuffer(); caches the strong reference to the index buffer + inline virtual void SetIndexBuffer(IBuffer* pIndexBuffer, Uint32 ByteOffset, RESOURCE_STATE_TRANSITION_MODE StateTransitionMode) override = 0; + + /// Caches the viewports + inline void SetViewports(Uint32 NumViewports, const Viewport* pViewports, Uint32& RTWidth, Uint32& RTHeight); + + /// Caches the scissor rects + inline void SetScissorRects(Uint32 NumRects, const Rect* pRects, Uint32& RTWidth, Uint32& RTHeight); + + /// Caches the render target and depth stencil views. Returns true if any view is different + /// from the cached value and false otherwise. + inline bool SetRenderTargets(Uint32 NumRenderTargets, ITextureView* ppRenderTargets[], ITextureView* pDepthStencil); + + /// Base implementation of IDeviceContext::UpdateBuffer(); validates input parameters. + virtual void UpdateBuffer(IBuffer* pBuffer, Uint32 Offset, Uint32 Size, const void* pData, RESOURCE_STATE_TRANSITION_MODE StateTransitionMode) override = 0; + + /// Base implementation of IDeviceContext::CopyBuffer(); validates input parameters. + virtual void CopyBuffer(IBuffer* pSrcBuffer, + Uint32 SrcOffset, + RESOURCE_STATE_TRANSITION_MODE SrcBufferTransitionMode, + IBuffer* pDstBuffer, + Uint32 DstOffset, + Uint32 Size, + RESOURCE_STATE_TRANSITION_MODE DstBufferTransitionMode) override = 0; + + /// Base implementation of IDeviceContext::MapBuffer(); validates input parameters. + virtual void MapBuffer(IBuffer* pBuffer, MAP_TYPE MapType, MAP_FLAGS MapFlags, PVoid& pMappedData) override = 0; + + /// Base implementation of IDeviceContext::UnmapBuffer() + virtual void UnmapBuffer(IBuffer* pBuffer, MAP_TYPE MapType) override = 0; + + /// Base implementaiton of IDeviceContext::UpdateData(); validates input parameters + virtual void UpdateTexture(ITexture* pTexture, + Uint32 MipLevel, + Uint32 Slice, + const Box& DstBox, + const TextureSubResData& SubresData, + RESOURCE_STATE_TRANSITION_MODE SrcBufferTransitionMode, + RESOURCE_STATE_TRANSITION_MODE TextureTransitionMode) override = 0; + + /// Base implementaiton of IDeviceContext::CopyTexture(); validates input parameters + virtual void CopyTexture(const CopyTextureAttribs& CopyAttribs) override = 0; + + /// Base implementaiton of IDeviceContext::MapTextureSubresource() + virtual void MapTextureSubresource(ITexture* pTexture, + Uint32 MipLevel, + Uint32 ArraySlice, + MAP_TYPE MapType, + MAP_FLAGS MapFlags, + const Box* pMapRegion, + MappedTextureSubresource& MappedData) override = 0; + + /// Base implementaiton of IDeviceContext::UnmapTextureSubresource() + virtual void UnmapTextureSubresource(ITexture* pTexture, Uint32 MipLevel, Uint32 ArraySlice) override = 0; + + virtual void GenerateMips(ITextureView* pTexView) override = 0; + + virtual void ResolveTextureSubresource(ITexture* pSrcTexture, + ITexture* pDstTexture, + const ResolveTextureSubresourceAttribs& ResolveAttribs) override = 0; + + /// Returns currently bound pipeline state and blend factors + inline void GetPipelineState(IPipelineState** ppPSO, float* BlendFactors, Uint32& StencilRef); + + /// Returns currently bound render targets + inline void GetRenderTargets(Uint32& NumRenderTargets, ITextureView** ppRTVs, ITextureView** ppDSV); + + /// Returns currently set viewports + inline void GetViewports(Uint32& NumViewports, Viewport* pViewports); + + /// Returns the render device + IRenderDevice* GetDevice() { return m_pDevice; } + + virtual void ResetRenderTargets(); + + bool IsDeferred() const { return m_bIsDeferred; } + + /// Checks if a texture is bound as a render target or depth-stencil buffer and + /// resets render targets if it is. + bool UnbindTextureFromFramebuffer(TextureImplType* pTexture, bool bShowMessage); + +protected: + inline bool SetBlendFactors(const float* BlendFactors, int Dummy); + + inline bool SetStencilRef(Uint32 StencilRef, int Dummy); + + inline void SetPipelineState(PipelineStateImplType* pPipelineState, int /*Dummy*/); + + /// Clears all cached resources + inline void ClearStateCache(); + + /// Checks if the texture is currently bound as a render target. + bool CheckIfBoundAsRenderTarget(TextureImplType* pTexture); + + /// Checks if the texture is currently bound as depth-stencil buffer. + bool CheckIfBoundAsDepthStencil(TextureImplType* pTexture); + + bool ClearDepthStencil(ITextureView* pView); + + bool ClearRenderTarget(ITextureView* pView); + + bool BeginQuery(IQuery* pQuery, int); + + bool EndQuery(IQuery* pQuery, int); + +#ifdef DEVELOPMENT + // clang-format off + bool DvpVerifyDrawArguments (const DrawAttribs& Attribs)const; + bool DvpVerifyDrawIndexedArguments (const DrawIndexedAttribs& Attribs)const; + bool DvpVerifyDrawIndirectArguments (const DrawIndirectAttribs& Attribs, const IBuffer* pAttribsBuffer)const; + bool DvpVerifyDrawIndexedIndirectArguments(const DrawIndexedIndirectAttribs& Attribs, const IBuffer* pAttribsBuffer)const; + + bool DvpVerifyDispatchArguments (const DispatchComputeAttribs& Attribs)const; + bool DvpVerifyDispatchIndirectArguments(const DispatchComputeIndirectAttribs& Attribs, const IBuffer* pAttribsBuffer)const; + + void DvpVerifyRenderTargets()const; + void DvpVerifyStateTransitionDesc(const StateTransitionDesc& Barrier)const; + bool DvpVerifyTextureState(const TextureImplType& Texture, RESOURCE_STATE RequiredState, const char* OperationName)const; + bool DvpVerifyBufferState (const BufferImplType& Buffer, RESOURCE_STATE RequiredState, const char* OperationName)const; +#else + bool DvpVerifyDrawArguments (const DrawAttribs& Attribs)const {return true;} + bool DvpVerifyDrawIndexedArguments (const DrawIndexedAttribs& Attribs)const {return true;} + bool DvpVerifyDrawIndirectArguments (const DrawIndirectAttribs& Attribs, const IBuffer* pAttribsBuffer)const {return true;} + bool DvpVerifyDrawIndexedIndirectArguments(const DrawIndexedIndirectAttribs& Attribs, const IBuffer* pAttribsBuffer)const {return true;} + + bool DvpVerifyDispatchArguments (const DispatchComputeAttribs& Attribs)const {return true;} + bool DvpVerifyDispatchIndirectArguments(const DispatchComputeIndirectAttribs& Attribs, const IBuffer* pAttribsBuffer)const {return true;} + + void DvpVerifyRenderTargets()const {} + void DvpVerifyStateTransitionDesc(const StateTransitionDesc& Barrier)const {} + bool DvpVerifyTextureState(const TextureImplType& Texture, RESOURCE_STATE RequiredState, const char* OperationName)const {return true;} + bool DvpVerifyBufferState (const BufferImplType& Buffer, RESOURCE_STATE RequiredState, const char* OperationName)const {return true;} + // clang-format on +#endif + + /// Strong reference to the device. + RefCntAutoPtr m_pDevice; + + /// Vertex streams. Every stream holds strong reference to the buffer + VertexStreamInfo m_VertexStreams[MAX_BUFFER_SLOTS]; + + /// Number of bound vertex streams + Uint32 m_NumVertexStreams = 0; + + /// Strong reference to the bound pipeline state object. + /// Use final PSO implementation type to avoid virtual calls to AddRef()/Release(). + /// We need to keep strong reference as we examine previous pipeline state in + /// SetPipelineState() + RefCntAutoPtr m_pPipelineState; + + /// Strong reference to the bound index buffer. + /// Use final buffer implementation type to avoid virtual calls to AddRef()/Release() + RefCntAutoPtr m_pIndexBuffer; + + /// Offset from the beginning of the index buffer to the start of the index data, in bytes. + Uint32 m_IndexDataStartOffset = 0; + + /// Current stencil reference value + Uint32 m_StencilRef = 0; + + /// Curent blend factors + Float32 m_BlendFactors[4] = {-1, -1, -1, -1}; + + /// Current viewports + Viewport m_Viewports[MAX_VIEWPORTS]; + /// Number of current viewports + Uint32 m_NumViewports = 0; + + /// Current scissor rects + Rect m_ScissorRects[MAX_VIEWPORTS]; + /// Number of current scissor rects + Uint32 m_NumScissorRects = 0; + + /// Vector of strong references to the bound render targets. + /// Use final texture view implementation type to avoid virtual calls to AddRef()/Release() + RefCntAutoPtr m_pBoundRenderTargets[MAX_RENDER_TARGETS]; + /// Number of bound render targets + Uint32 m_NumBoundRenderTargets = 0; + /// Width of the currently bound framebuffer + Uint32 m_FramebufferWidth = 0; + /// Height of the currently bound framebuffer + Uint32 m_FramebufferHeight = 0; + /// Number of array slices in the currently bound framebuffer + Uint32 m_FramebufferSlices = 0; + + /// Strong references to the bound depth stencil view. + /// Use final texture view implementation type to avoid virtual calls to AddRef()/Release() + RefCntAutoPtr m_pBoundDepthStencil; + + const bool m_bIsDeferred = false; + +#ifdef _DEBUG + // std::unordered_map is unbelievably slow. Keeping track of mapped buffers + // in release builds is not feasible + struct DbgMappedBufferInfo + { + MAP_TYPE MapType; + }; + std::unordered_map m_DbgMappedBuffers; +#endif +}; + + +template +inline void DeviceContextBase:: + SetVertexBuffers(Uint32 StartSlot, + Uint32 NumBuffersSet, + IBuffer** ppBuffers, + Uint32* pOffsets, + RESOURCE_STATE_TRANSITION_MODE StateTransitionMode, + SET_VERTEX_BUFFERS_FLAGS Flags) +{ +#ifdef DEVELOPMENT + if (StartSlot >= MAX_BUFFER_SLOTS) + { + LOG_ERROR_MESSAGE("Start vertex buffer slot ", StartSlot, " is out of allowed range [0, ", MAX_BUFFER_SLOTS - 1, "]."); + return; + } + + if (StartSlot + NumBuffersSet > MAX_BUFFER_SLOTS) + { + LOG_ERROR_MESSAGE("The range of vertex buffer slots being set [", StartSlot, ", ", StartSlot + NumBuffersSet - 1, "] is out of allowed range [0, ", MAX_BUFFER_SLOTS - 1, "]."); + NumBuffersSet = MAX_BUFFER_SLOTS - StartSlot; + } +#endif + + if (Flags & SET_VERTEX_BUFFERS_FLAG_RESET) + { + // Reset only these buffer slots that are not being set. + // It is very important to not reset buffers that stay unchanged + // as AddRef()/Release() are not free + for (Uint32 s = 0; s < StartSlot; ++s) + m_VertexStreams[s] = VertexStreamInfo{}; + for (Uint32 s = StartSlot + NumBuffersSet; s < m_NumVertexStreams; ++s) + m_VertexStreams[s] = VertexStreamInfo{}; + m_NumVertexStreams = 0; + } + m_NumVertexStreams = std::max(m_NumVertexStreams, StartSlot + NumBuffersSet); + + for (Uint32 Buff = 0; Buff < NumBuffersSet; ++Buff) + { + auto& CurrStream = m_VertexStreams[StartSlot + Buff]; + CurrStream.pBuffer = ppBuffers ? ValidatedCast(ppBuffers[Buff]) : nullptr; + CurrStream.Offset = pOffsets ? pOffsets[Buff] : 0; +#ifdef DEVELOPMENT + if (CurrStream.pBuffer) + { + const auto& BuffDesc = CurrStream.pBuffer->GetDesc(); + if (!(BuffDesc.BindFlags & BIND_VERTEX_BUFFER)) + { + LOG_ERROR_MESSAGE("Buffer '", BuffDesc.Name ? BuffDesc.Name : "", "' being bound as vertex buffer to slot ", Buff, " was not created with BIND_VERTEX_BUFFER flag"); + } + } +#endif + } + // Remove null buffers from the end of the array + while (m_NumVertexStreams > 0 && !m_VertexStreams[m_NumVertexStreams - 1].pBuffer) + m_VertexStreams[m_NumVertexStreams--] = VertexStreamInfo{}; +} + +template +inline void DeviceContextBase:: + SetPipelineState(PipelineStateImplType* pPipelineState, int /*Dummy*/) +{ + m_pPipelineState = pPipelineState; +} + +template +inline bool DeviceContextBase:: + CommitShaderResources(IShaderResourceBinding* pShaderResourceBinding, RESOURCE_STATE_TRANSITION_MODE StateTransitionMode, int) +{ +#ifdef DEVELOPMENT + if (!m_pPipelineState) + { + LOG_ERROR_MESSAGE("No pipeline state is bound to the pipeline"); + return false; + } + + if (pShaderResourceBinding) + { + if (m_pPipelineState->IsIncompatibleWith(pShaderResourceBinding->GetPipelineState())) + { + LOG_ERROR_MESSAGE("Shader resource binding object is not compatible with the currently bound pipeline state '", m_pPipelineState->GetDesc().Name, '\''); + return false; + } + } +#endif + return true; +} + +template +inline void DeviceContextBase::InvalidateState() +{ + DeviceContextBase::ClearStateCache(); +} + +template +inline void DeviceContextBase:: + SetIndexBuffer(IBuffer* pIndexBuffer, Uint32 ByteOffset, RESOURCE_STATE_TRANSITION_MODE StateTransitionMode) +{ + m_pIndexBuffer = ValidatedCast(pIndexBuffer); + m_IndexDataStartOffset = ByteOffset; +#ifdef DEVELOPMENT + if (m_pIndexBuffer) + { + const auto& BuffDesc = m_pIndexBuffer->GetDesc(); + if (!(BuffDesc.BindFlags & BIND_INDEX_BUFFER)) + { + LOG_ERROR_MESSAGE("Buffer '", BuffDesc.Name ? BuffDesc.Name : "", "' being bound as index buffer was not created with BIND_INDEX_BUFFER flag"); + } + } +#endif +} + + +template +inline void DeviceContextBase::GetPipelineState(IPipelineState** ppPSO, float* BlendFactors, Uint32& StencilRef) +{ + VERIFY(ppPSO != nullptr, "Null pointer provided null"); + VERIFY(*ppPSO == nullptr, "Memory address contains a pointer to a non-null blend state"); + if (m_pPipelineState) + { + m_pPipelineState->QueryInterface(IID_PipelineState, reinterpret_cast(ppPSO)); + } + else + { + *ppPSO = nullptr; + } + + for (Uint32 f = 0; f < 4; ++f) + BlendFactors[f] = m_BlendFactors[f]; + StencilRef = m_StencilRef; +}; + +template +inline bool DeviceContextBase::SetBlendFactors(const float* BlendFactors, int) +{ + bool FactorsDiffer = false; + for (Uint32 f = 0; f < 4; ++f) + { + if (m_BlendFactors[f] != BlendFactors[f]) + FactorsDiffer = true; + m_BlendFactors[f] = BlendFactors[f]; + } + return FactorsDiffer; +} + +template +inline bool DeviceContextBase::SetStencilRef(Uint32 StencilRef, int) +{ + if (m_StencilRef != StencilRef) + { + m_StencilRef = StencilRef; + return true; + } + return false; +} + +template +inline void DeviceContextBase:: + SetViewports(Uint32 NumViewports, const Viewport* pViewports, Uint32& RTWidth, Uint32& RTHeight) +{ + if (RTWidth == 0 || RTHeight == 0) + { + RTWidth = m_FramebufferWidth; + RTHeight = m_FramebufferHeight; + } + + VERIFY(NumViewports < MAX_VIEWPORTS, "Number of viewports (", NumViewports, ") exceeds the limit (", MAX_VIEWPORTS, ")"); + m_NumViewports = std::min(MAX_VIEWPORTS, NumViewports); + + Viewport DefaultVP(0, 0, static_cast(RTWidth), static_cast(RTHeight)); + // If no viewports are specified, use default viewport + if (m_NumViewports == 1 && pViewports == nullptr) + { + pViewports = &DefaultVP; + } + + for (Uint32 vp = 0; vp < m_NumViewports; ++vp) + { + m_Viewports[vp] = pViewports[vp]; + VERIFY(m_Viewports[vp].Width >= 0, "Incorrect viewport width (", m_Viewports[vp].Width, ")"); + VERIFY(m_Viewports[vp].Height >= 0, "Incorrect viewport height (", m_Viewports[vp].Height, ")"); + VERIFY(m_Viewports[vp].MaxDepth >= m_Viewports[vp].MinDepth, "Incorrect viewport depth range [", m_Viewports[vp].MinDepth, ", ", m_Viewports[vp].MaxDepth, "]"); + } +} + +template +inline void DeviceContextBase::GetViewports(Uint32& NumViewports, Viewport* pViewports) +{ + NumViewports = m_NumViewports; + if (pViewports) + { + for (Uint32 vp = 0; vp < m_NumViewports; ++vp) + pViewports[vp] = m_Viewports[vp]; + } +} + +template +inline void DeviceContextBase:: + SetScissorRects(Uint32 NumRects, const Rect* pRects, Uint32& RTWidth, Uint32& RTHeight) +{ + if (RTWidth == 0 || RTHeight == 0) + { + RTWidth = m_FramebufferWidth; + RTHeight = m_FramebufferHeight; + } + + VERIFY(NumRects < MAX_VIEWPORTS, "Number of scissor rects (", NumRects, ") exceeds the limit (", MAX_VIEWPORTS, ")"); + m_NumScissorRects = std::min(MAX_VIEWPORTS, NumRects); + + for (Uint32 sr = 0; sr < m_NumScissorRects; ++sr) + { + m_ScissorRects[sr] = pRects[sr]; + VERIFY(m_ScissorRects[sr].left <= m_ScissorRects[sr].right, "Incorrect horizontal bounds for a scissor rect [", m_ScissorRects[sr].left, ", ", m_ScissorRects[sr].right, ")"); + VERIFY(m_ScissorRects[sr].top <= m_ScissorRects[sr].bottom, "Incorrect vertical bounds for a scissor rect [", m_ScissorRects[sr].top, ", ", m_ScissorRects[sr].bottom, ")"); + } +} + +template +inline bool DeviceContextBase:: + SetRenderTargets(Uint32 NumRenderTargets, ITextureView* ppRenderTargets[], ITextureView* pDepthStencil) +{ + if (NumRenderTargets == 0 && pDepthStencil == nullptr) + { + ResetRenderTargets(); + return false; + } + + bool bBindRenderTargets = false; + m_FramebufferWidth = 0; + m_FramebufferHeight = 0; + m_FramebufferSlices = 0; + + if (NumRenderTargets != m_NumBoundRenderTargets) + { + bBindRenderTargets = true; + for (Uint32 rt = NumRenderTargets; rt < m_NumBoundRenderTargets; ++rt) + m_pBoundRenderTargets[rt].Release(); + + m_NumBoundRenderTargets = NumRenderTargets; + } + + for (Uint32 rt = 0; rt < NumRenderTargets; ++rt) + { + auto* pRTView = ppRenderTargets[rt]; + if (pRTView) + { + const auto& RTVDesc = pRTView->GetDesc(); +#ifdef DEVELOPMENT + if (RTVDesc.ViewType != TEXTURE_VIEW_RENDER_TARGET) + LOG_ERROR("Texture view object named '", RTVDesc.Name ? RTVDesc.Name : "", "' has incorrect view type (", GetTexViewTypeLiteralName(RTVDesc.ViewType), "). Render target view is expected"); +#endif + // Use this RTV to set the render target size + if (m_FramebufferWidth == 0) + { + auto* pTex = pRTView->GetTexture(); + const auto& TexDesc = pTex->GetDesc(); + m_FramebufferWidth = std::max(TexDesc.Width >> RTVDesc.MostDetailedMip, 1U); + m_FramebufferHeight = std::max(TexDesc.Height >> RTVDesc.MostDetailedMip, 1U); + m_FramebufferSlices = RTVDesc.NumArraySlices; + } + else + { +#ifdef DEVELOPMENT + const auto& TexDesc = pRTView->GetTexture()->GetDesc(); + if (m_FramebufferWidth != std::max(TexDesc.Width >> RTVDesc.MostDetailedMip, 1U)) + LOG_ERROR("Render target width (", std::max(TexDesc.Width >> RTVDesc.MostDetailedMip, 1U), ") specified by RTV '", RTVDesc.Name, "' is inconsistent with the width of previously bound render targets (", m_FramebufferWidth, ")"); + if (m_FramebufferHeight != std::max(TexDesc.Height >> RTVDesc.MostDetailedMip, 1U)) + LOG_ERROR("Render target height (", std::max(TexDesc.Height >> RTVDesc.MostDetailedMip, 1U), ") specified by RTV '", RTVDesc.Name, "' is inconsistent with the height of previously bound render targets (", m_FramebufferHeight, ")"); + if (m_FramebufferSlices != RTVDesc.NumArraySlices) + LOG_ERROR("Number of slices (", RTVDesc.NumArraySlices, ") specified by RTV '", RTVDesc.Name, "' is inconsistent with the number of slices in previously bound render targets (", m_FramebufferSlices, ")"); +#endif + } + } + + // Here both views are certainly live objects, since we store + // strong references to all bound render targets. So we + // can safely compare pointers. + if (m_pBoundRenderTargets[rt] != pRTView) + { + m_pBoundRenderTargets[rt] = ValidatedCast(pRTView); + bBindRenderTargets = true; + } + } + + if (pDepthStencil != nullptr) + { + const auto& DSVDesc = pDepthStencil->GetDesc(); +#ifdef DEVELOPMENT + if (DSVDesc.ViewType != TEXTURE_VIEW_DEPTH_STENCIL) + LOG_ERROR("Texture view object named '", DSVDesc.Name ? DSVDesc.Name : "", "' has incorrect view type (", GetTexViewTypeLiteralName(DSVDesc.ViewType), "). Depth stencil view is expected"); +#endif + + // Use depth stencil size to set render target size + if (m_FramebufferWidth == 0) + { + auto* pTex = pDepthStencil->GetTexture(); + const auto& TexDesc = pTex->GetDesc(); + m_FramebufferWidth = std::max(TexDesc.Width >> DSVDesc.MostDetailedMip, 1U); + m_FramebufferHeight = std::max(TexDesc.Height >> DSVDesc.MostDetailedMip, 1U); + m_FramebufferSlices = DSVDesc.NumArraySlices; + } + else + { +#ifdef DEVELOPMENT + const auto& TexDesc = pDepthStencil->GetTexture()->GetDesc(); + if (m_FramebufferWidth != std::max(TexDesc.Width >> DSVDesc.MostDetailedMip, 1U)) + LOG_ERROR("Depth-stencil target width (", std::max(TexDesc.Width >> DSVDesc.MostDetailedMip, 1U), ") specified by DSV '", DSVDesc.Name, "' is inconsistent with the width of previously bound render targets (", m_FramebufferWidth, ")"); + if (m_FramebufferHeight != std::max(TexDesc.Height >> DSVDesc.MostDetailedMip, 1U)) + LOG_ERROR("Depth-stencil target height (", std::max(TexDesc.Height >> DSVDesc.MostDetailedMip, 1U), ") specified by DSV '", DSVDesc.Name, "' is inconsistent with the height of previously bound render targets (", m_FramebufferHeight, ")"); + if (m_FramebufferSlices != DSVDesc.NumArraySlices) + LOG_ERROR("Number of slices (", DSVDesc.NumArraySlices, ") specified by DSV '", DSVDesc.Name, "' is inconsistent with the number of slices in previously bound render targets (", m_FramebufferSlices, ")"); +#endif + } + } + + if (m_pBoundDepthStencil != pDepthStencil) + { + m_pBoundDepthStencil = ValidatedCast(pDepthStencil); + bBindRenderTargets = true; + } + + + VERIFY_EXPR(m_FramebufferWidth > 0 && m_FramebufferHeight > 0 && m_FramebufferSlices > 0); + + return bBindRenderTargets; +} + +template +inline void DeviceContextBase:: + GetRenderTargets(Uint32& NumRenderTargets, ITextureView** ppRTVs, ITextureView** ppDSV) +{ + NumRenderTargets = m_NumBoundRenderTargets; + + if (ppRTVs) + { + for (Uint32 rt = 0; rt < NumRenderTargets; ++rt) + { + VERIFY(ppRTVs[rt] == nullptr, "Non-null pointer found in RTV array element #", rt); + auto pBoundRTV = m_pBoundRenderTargets[rt]; + if (pBoundRTV) + pBoundRTV->QueryInterface(IID_TextureView, reinterpret_cast(ppRTVs + rt)); + else + ppRTVs[rt] = nullptr; + } + for (Uint32 rt = NumRenderTargets; rt < MAX_RENDER_TARGETS; ++rt) + { + VERIFY(ppRTVs[rt] == nullptr, "Non-null pointer found in RTV array element #", rt); + ppRTVs[rt] = nullptr; + } + } + + if (ppDSV) + { + VERIFY(*ppDSV == nullptr, "Non-null DSV pointer found"); + if (m_pBoundDepthStencil) + m_pBoundDepthStencil->QueryInterface(IID_TextureView, reinterpret_cast(ppDSV)); + else + *ppDSV = nullptr; + } +} + +template +inline void DeviceContextBase::ClearStateCache() +{ + for (Uint32 stream = 0; stream < m_NumVertexStreams; ++stream) + m_VertexStreams[stream] = VertexStreamInfo{}; +#ifdef _DEBUG + for (Uint32 stream = m_NumVertexStreams; stream < _countof(m_VertexStreams); ++stream) + { + VERIFY(m_VertexStreams[stream].pBuffer == nullptr, "Unexpected non-null buffer"); + VERIFY(m_VertexStreams[stream].Offset == 0, "Unexpected non-zero offset"); + } +#endif + m_NumVertexStreams = 0; + + m_pPipelineState.Release(); + + m_pIndexBuffer.Release(); + m_IndexDataStartOffset = 0; + + m_StencilRef = 0; + + for (int i = 0; i < 4; ++i) + m_BlendFactors[i] = -1; + + for (Uint32 vp = 0; vp < m_NumViewports; ++vp) + m_Viewports[vp] = Viewport(); + m_NumViewports = 0; + + for (Uint32 sr = 0; sr < m_NumScissorRects; ++sr) + m_ScissorRects[sr] = Rect(); + m_NumScissorRects = 0; + + ResetRenderTargets(); +} + +template +bool DeviceContextBase::CheckIfBoundAsRenderTarget(TextureImplType* pTexture) +{ + if (pTexture == nullptr) + return false; + + for (Uint32 rt = 0; rt < m_NumBoundRenderTargets; ++rt) + { + if (m_pBoundRenderTargets[rt] && m_pBoundRenderTargets[rt]->GetTexture() == pTexture) + { + return true; + } + } + + return false; +} + +template +bool DeviceContextBase::CheckIfBoundAsDepthStencil(TextureImplType* pTexture) +{ + if (pTexture == nullptr) + return false; + + return m_pBoundDepthStencil && m_pBoundDepthStencil->GetTexture() == pTexture; +} + +template +bool DeviceContextBase::UnbindTextureFromFramebuffer(TextureImplType* pTexture, bool bShowMessage) +{ + if (pTexture == nullptr) + return false; + + const auto& TexDesc = pTexture->GetDesc(); + + bool bResetRenderTargets = false; + if (TexDesc.BindFlags & BIND_RENDER_TARGET) + { + if (CheckIfBoundAsRenderTarget(pTexture)) + { + if (bShowMessage) + { + LOG_INFO_MESSAGE("Texture '", TexDesc.Name, + "' is currently bound as render target and will be unset along with all " + "other render targets and depth-stencil buffer. " + "Call SetRenderTargets() to reset the render targets.\n" + "To silence this message, explicitly unbind the texture with " + "SetRenderTargets(0, nullptr, nullptr, RESOURCE_STATE_TRANSITION_MODE_NONE)"); + } + + bResetRenderTargets = true; + } + } + + if (TexDesc.BindFlags & BIND_DEPTH_STENCIL) + { + if (CheckIfBoundAsDepthStencil(pTexture)) + { + if (bShowMessage) + { + LOG_INFO_MESSAGE("Texture '", TexDesc.Name, + "' is currently bound as depth buffer and will be unset along with " + "all render targets. Call SetRenderTargets() to reset the render targets.\n" + "To silence this message, explicitly unbind the texture with " + "SetRenderTargets(0, nullptr, nullptr, RESOURCE_STATE_TRANSITION_MODE_NONE)"); + } + + bResetRenderTargets = true; + } + } + + if (bResetRenderTargets) + { + ResetRenderTargets(); + } + + return bResetRenderTargets; +} + +template +void DeviceContextBase::ResetRenderTargets() +{ + for (Uint32 rt = 0; rt < m_NumBoundRenderTargets; ++rt) + m_pBoundRenderTargets[rt].Release(); +#ifdef _DEBUG + for (Uint32 rt = m_NumBoundRenderTargets; rt < _countof(m_pBoundRenderTargets); ++rt) + { + VERIFY(m_pBoundRenderTargets[rt] == nullptr, "Non-null render target found"); + } +#endif + m_NumBoundRenderTargets = 0; + m_FramebufferWidth = 0; + m_FramebufferHeight = 0; + m_FramebufferSlices = 0; + + m_pBoundDepthStencil.Release(); +} + + +template +inline bool DeviceContextBase::ClearDepthStencil(ITextureView* pView) +{ + if (pView == nullptr) + { + LOG_ERROR_MESSAGE("Depth-stencil view to clear must not be null"); + return false; + } + +#ifdef DEVELOPMENT + { + const auto& ViewDesc = pView->GetDesc(); + if (ViewDesc.ViewType != TEXTURE_VIEW_DEPTH_STENCIL) + { + LOG_ERROR_MESSAGE("The type (", GetTexViewTypeLiteralName(ViewDesc.ViewType), ") of the texture view '", ViewDesc.Name, + "' is invalid: ClearDepthStencil command expects depth-stencil view (TEXTURE_VIEW_DEPTH_STENCIL)."); + return false; + } + + if (pView != m_pBoundDepthStencil) + { + if (m_pDevice->GetDeviceCaps().IsGLDevice()) + { + LOG_ERROR_MESSAGE("Depth-stencil view '", ViewDesc.Name, + "' is not bound to the device context. ClearDepthStencil command requires " + "depth-stencil view be bound to the device contex in OpenGL backend"); + return false; + } + else + { + LOG_WARNING_MESSAGE("Depth-stencil view '", ViewDesc.Name, + "' is not bound to the device context. " + "ClearDepthStencil command is more efficient when depth-stencil " + "view is bound to the context. In OpenGL backend this is a requirement."); + } + } + } +#endif + + return true; +} + +template +inline bool DeviceContextBase::ClearRenderTarget(ITextureView* pView) +{ + if (pView == nullptr) + { + LOG_ERROR_MESSAGE("Render target view to clear must not be null"); + return false; + } + +#ifdef DEVELOPMENT + { + const auto& ViewDesc = pView->GetDesc(); + if (ViewDesc.ViewType != TEXTURE_VIEW_RENDER_TARGET) + { + LOG_ERROR_MESSAGE("The type (", GetTexViewTypeLiteralName(ViewDesc.ViewType), ") of texture view '", pView->GetDesc().Name, + "' is invalid: ClearRenderTarget command expects render target view (TEXTURE_VIEW_RENDER_TARGET)."); + return false; + } + + bool RTFound = false; + for (Uint32 i = 0; i < m_NumBoundRenderTargets && !RTFound; ++i) + { + RTFound = m_pBoundRenderTargets[i] == pView; + } + if (!RTFound) + { + if (m_pDevice->GetDeviceCaps().IsGLDevice()) + { + LOG_ERROR_MESSAGE("Render target view '", ViewDesc.Name, + "' is not bound to the device context. ClearRenderTarget command " + "requires render target view be bound to the device contex in OpenGL backend"); + return false; + } + else + { + LOG_WARNING_MESSAGE("Render target view '", ViewDesc.Name, + "' is not bound to the device context. ClearRenderTarget command is more efficient " + "if render target view is bound to the device context. In OpenGL backend this is a requirement."); + } + } + } +#endif + + return true; +} + +template +inline bool DeviceContextBase::BeginQuery(IQuery* pQuery, int) +{ + if (pQuery == nullptr) + { + LOG_ERROR_MESSAGE("IDeviceContext::BeginQuery: pQuery must not be null"); + return false; + } + + if (m_bIsDeferred) + { + LOG_ERROR_MESSAGE("IDeviceContext::BeginQuery: Deferred contexts do not support queries"); + return false; + } + + if (pQuery->GetDesc().Type == QUERY_TYPE_TIMESTAMP) + { + LOG_ERROR_MESSAGE("BeginQuery() is disabled for timestamp queries. Call EndQuery() to set the timestamp."); + return false; + } + + if (!ValidatedCast(pQuery)->OnBeginQuery(this)) + return false; + + return true; +} + +template +inline bool DeviceContextBase::EndQuery(IQuery* pQuery, int) +{ + if (pQuery == nullptr) + { + LOG_ERROR_MESSAGE("IDeviceContext::EndQuery: pQuery must not be null"); + return false; + } + + if (m_bIsDeferred) + { + LOG_ERROR_MESSAGE("IDeviceContext::EndQuery: Deferred contexts do not support queries"); + return false; + } + + if (!ValidatedCast(pQuery)->OnEndQuery(this)) + return false; + + return true; +} + +template +inline void DeviceContextBase:: + UpdateBuffer(IBuffer* pBuffer, Uint32 Offset, Uint32 Size, const void* pData, RESOURCE_STATE_TRANSITION_MODE StateTransitionMode) +{ + VERIFY(pBuffer != nullptr, "Buffer must not be null"); +#ifdef DEVELOPMENT + const auto& BuffDesc = ValidatedCast(pBuffer)->GetDesc(); + DEV_CHECK_ERR(BuffDesc.Usage == USAGE_DEFAULT, "Unable to update buffer '", BuffDesc.Name, "': only USAGE_DEFAULT buffers can be updated with UpdateData()"); + DEV_CHECK_ERR(Offset < BuffDesc.uiSizeInBytes, "Unable to update buffer '", BuffDesc.Name, "': offset (", Offset, ") exceeds the buffer size (", BuffDesc.uiSizeInBytes, ")"); + DEV_CHECK_ERR(Size + Offset <= BuffDesc.uiSizeInBytes, "Unable to update buffer '", BuffDesc.Name, "': Update region [", Offset, ",", Size + Offset, ") is out of buffer bounds [0,", BuffDesc.uiSizeInBytes, ")"); +#endif +} + +template +inline void DeviceContextBase:: + CopyBuffer(IBuffer* pSrcBuffer, + Uint32 SrcOffset, + RESOURCE_STATE_TRANSITION_MODE SrcBufferTransitionMode, + IBuffer* pDstBuffer, + Uint32 DstOffset, + Uint32 Size, + RESOURCE_STATE_TRANSITION_MODE DstBufferTransitionMode) +{ + VERIFY(pSrcBuffer != nullptr, "Source buffer must not be null"); + VERIFY(pDstBuffer != nullptr, "Destination buffer must not be null"); +#ifdef DEVELOPMENT + const auto& SrcBufferDesc = ValidatedCast(pSrcBuffer)->GetDesc(); + const auto& DstBufferDesc = ValidatedCast(pDstBuffer)->GetDesc(); + DEV_CHECK_ERR(DstOffset + Size <= DstBufferDesc.uiSizeInBytes, "Failed to copy buffer '", SrcBufferDesc.Name, "' to '", DstBufferDesc.Name, "': Destination range [", DstOffset, ",", DstOffset + Size, ") is out of buffer bounds [0,", DstBufferDesc.uiSizeInBytes, ")"); + DEV_CHECK_ERR(SrcOffset + Size <= SrcBufferDesc.uiSizeInBytes, "Failed to copy buffer '", SrcBufferDesc.Name, "' to '", DstBufferDesc.Name, "': Source range [", SrcOffset, ",", SrcOffset + Size, ") is out of buffer bounds [0,", SrcBufferDesc.uiSizeInBytes, ")"); +#endif +} + +template +inline void DeviceContextBase:: + MapBuffer(IBuffer* pBuffer, MAP_TYPE MapType, MAP_FLAGS MapFlags, PVoid& pMappedData) +{ + VERIFY(pBuffer, "pBuffer must not be null"); + + const auto& BuffDesc = pBuffer->GetDesc(); + +#ifdef _DEBUG + VERIFY(m_DbgMappedBuffers.find(pBuffer) == m_DbgMappedBuffers.end(), "Buffer '", BuffDesc.Name, "' has already been mapped"); + m_DbgMappedBuffers[pBuffer] = DbgMappedBufferInfo{MapType}; +#endif + + pMappedData = nullptr; + switch (MapType) + { + case MAP_READ: + DEV_CHECK_ERR(BuffDesc.Usage == USAGE_STAGING, "Only buffers with usage USAGE_STAGING can be read from"); + DEV_CHECK_ERR((BuffDesc.CPUAccessFlags & CPU_ACCESS_READ), "Buffer being mapped for reading was not created with CPU_ACCESS_READ flag"); + DEV_CHECK_ERR((MapFlags & MAP_FLAG_DISCARD) == 0, "MAP_FLAG_DISCARD is not valid when mapping buffer for reading"); + break; + + case MAP_WRITE: + DEV_CHECK_ERR(BuffDesc.Usage == USAGE_DYNAMIC || BuffDesc.Usage == USAGE_STAGING, "Only buffers with usage USAGE_STAGING or USAGE_DYNAMIC can be mapped for writing"); + DEV_CHECK_ERR((BuffDesc.CPUAccessFlags & CPU_ACCESS_WRITE), "Buffer being mapped for writing was not created with CPU_ACCESS_WRITE flag"); + break; + + case MAP_READ_WRITE: + DEV_CHECK_ERR(BuffDesc.Usage == USAGE_STAGING, "Only buffers with usage USAGE_STAGING can be mapped for reading and writing"); + DEV_CHECK_ERR((BuffDesc.CPUAccessFlags & CPU_ACCESS_WRITE), "Buffer being mapped for reading & writing was not created with CPU_ACCESS_WRITE flag"); + DEV_CHECK_ERR((BuffDesc.CPUAccessFlags & CPU_ACCESS_READ), "Buffer being mapped for reading & writing was not created with CPU_ACCESS_READ flag"); + DEV_CHECK_ERR((MapFlags & MAP_FLAG_DISCARD) == 0, "MAP_FLAG_DISCARD is not valid when mapping buffer for reading and writing"); + break; + + default: UNEXPECTED("Unknown map type"); + } + + if (BuffDesc.Usage == USAGE_DYNAMIC) + { + DEV_CHECK_ERR((MapFlags & (MAP_FLAG_DISCARD | MAP_FLAG_NO_OVERWRITE)) != 0 && MapType == MAP_WRITE, "Dynamic buffers can only be mapped for writing with MAP_FLAG_DISCARD or MAP_FLAG_NO_OVERWRITE flag"); + DEV_CHECK_ERR((MapFlags & (MAP_FLAG_DISCARD | MAP_FLAG_NO_OVERWRITE)) != (MAP_FLAG_DISCARD | MAP_FLAG_NO_OVERWRITE), "When mapping dynamic buffer, only one of MAP_FLAG_DISCARD or MAP_FLAG_NO_OVERWRITE flags must be specified"); + } + + if ((MapFlags & MAP_FLAG_DISCARD) != 0) + { + DEV_CHECK_ERR(BuffDesc.Usage == USAGE_DYNAMIC || BuffDesc.Usage == USAGE_STAGING, "Only dynamic and staging buffers can be mapped with discard flag"); + DEV_CHECK_ERR(MapType == MAP_WRITE, "MAP_FLAG_DISCARD is only valid when mapping buffer for writing"); + } +} + +template +inline void DeviceContextBase:: + UnmapBuffer(IBuffer* pBuffer, MAP_TYPE MapType) +{ + VERIFY(pBuffer, "pBuffer must not be null"); +#ifdef _DEBUG + auto MappedBufferIt = m_DbgMappedBuffers.find(pBuffer); + VERIFY(MappedBufferIt != m_DbgMappedBuffers.end(), "Buffer '", pBuffer->GetDesc().Name, "' has not been mapped."); + VERIFY(MappedBufferIt->second.MapType == MapType, "MapType (", MapType, ") does not match the map type that was used to map the buffer ", MappedBufferIt->second.MapType); + m_DbgMappedBuffers.erase(MappedBufferIt); +#endif +} + + +template +inline void DeviceContextBase:: + UpdateTexture(ITexture* pTexture, Uint32 MipLevel, Uint32 Slice, const Box& DstBox, const TextureSubResData& SubresData, RESOURCE_STATE_TRANSITION_MODE SrcBufferTransitionMode, RESOURCE_STATE_TRANSITION_MODE TextureTransitionMode) +{ + VERIFY(pTexture != nullptr, "pTexture must not be null"); + ValidateUpdateTextureParams(pTexture->GetDesc(), MipLevel, Slice, DstBox, SubresData); +} + +template +inline void DeviceContextBase:: + CopyTexture(const CopyTextureAttribs& CopyAttribs) +{ + VERIFY(CopyAttribs.pSrcTexture, "Src texture must not be null"); + VERIFY(CopyAttribs.pDstTexture, "Dst texture must not be null"); + ValidateCopyTextureParams(CopyAttribs); +} + +template +inline void DeviceContextBase:: + MapTextureSubresource(ITexture* pTexture, + Uint32 MipLevel, + Uint32 ArraySlice, + MAP_TYPE MapType, + MAP_FLAGS MapFlags, + const Box* pMapRegion, + MappedTextureSubresource& MappedData) +{ + VERIFY(pTexture, "pTexture must not be null"); + ValidateMapTextureParams(pTexture->GetDesc(), MipLevel, ArraySlice, MapType, MapFlags, pMapRegion); +} + +template +inline void DeviceContextBase:: + UnmapTextureSubresource(ITexture* pTexture, Uint32 MipLevel, Uint32 ArraySlice) +{ + VERIFY(pTexture, "pTexture must not be null"); + DEV_CHECK_ERR(MipLevel < pTexture->GetDesc().MipLevels, "Mip level is out of range"); + DEV_CHECK_ERR(ArraySlice < pTexture->GetDesc().ArraySize, "Array slice is out of range"); +} + +template +inline void DeviceContextBase:: + GenerateMips(ITextureView* pTexView) +{ + VERIFY(pTexView != nullptr, "pTexView must not be null"); +#ifdef DEVELOPMENT + const auto& ViewDesc = pTexView->GetDesc(); + DEV_CHECK_ERR(ViewDesc.ViewType == TEXTURE_VIEW_SHADER_RESOURCE, "Shader resource view '", ViewDesc.Name, + "' can't be used to generate mipmaps because its type is ", GetTexViewTypeLiteralName(ViewDesc.ViewType), ". Required view type: TEXTURE_VIEW_SHADER_RESOURCE."); + DEV_CHECK_ERR((ViewDesc.Flags & TEXTURE_VIEW_FLAG_ALLOW_MIP_MAP_GENERATION) != 0, "Shader resource view '", ViewDesc.Name, + "' was not created with TEXTURE_VIEW_FLAG_ALLOW_MIP_MAP_GENERATION flag and can't be used to generate mipmaps."); +#endif +} + + +template +void DeviceContextBase:: + ResolveTextureSubresource(ITexture* pSrcTexture, + ITexture* pDstTexture, + const ResolveTextureSubresourceAttribs& ResolveAttribs) +{ +#ifdef DEVELOPMENT + VERIFY_EXPR(pSrcTexture != nullptr && pDstTexture != nullptr); + const auto& SrcTexDesc = pSrcTexture->GetDesc(); + const auto& DstTexDesc = pDstTexture->GetDesc(); + DEV_CHECK_ERR(SrcTexDesc.SampleCount > 1, + "Source texture '", SrcTexDesc.Name, "' of a resolve operation is not multi-sampled"); + DEV_CHECK_ERR(DstTexDesc.SampleCount == 1, + "Destination texture '", DstTexDesc.Name, "' of a resolve operation is multi-sampled"); + auto SrcMipLevelProps = GetMipLevelProperties(SrcTexDesc, ResolveAttribs.SrcMipLevel); + auto DstMipLevelProps = GetMipLevelProperties(DstTexDesc, ResolveAttribs.DstMipLevel); + DEV_CHECK_ERR(SrcMipLevelProps.LogicalWidth == DstMipLevelProps.LogicalWidth && SrcMipLevelProps.LogicalHeight == DstMipLevelProps.LogicalHeight, + "The size (", SrcMipLevelProps.LogicalWidth, "x", SrcMipLevelProps.LogicalHeight, + ") of the source subresource of a resolve operation (texture '", + SrcTexDesc.Name, "', mip ", ResolveAttribs.SrcMipLevel, ", slice ", ResolveAttribs.SrcSlice, + ") does not match the size (", DstMipLevelProps.LogicalWidth, "x", DstMipLevelProps.LogicalHeight, + ") of the destination subresource (texture '", DstTexDesc.Name, "', mip ", ResolveAttribs.DstMipLevel, ", slice ", + ResolveAttribs.DstSlice, ")"); + + const auto& SrcFmtAttribs = GetTextureFormatAttribs(SrcTexDesc.Format); + const auto& DstFmtAttribs = GetTextureFormatAttribs(DstTexDesc.Format); + const auto& ResolveFmtAttribs = GetTextureFormatAttribs(ResolveAttribs.Format); + if (!SrcFmtAttribs.IsTypeless && !DstFmtAttribs.IsTypeless) + { + DEV_CHECK_ERR(SrcTexDesc.Format == DstTexDesc.Format, + "Source (", SrcFmtAttribs.Name, ") and destination (", DstFmtAttribs.Name, + ") texture formats of a resolve operation must match exaclty or be compatible typeless formats"); + DEV_CHECK_ERR(ResolveAttribs.Format == TEX_FORMAT_UNKNOWN || SrcTexDesc.Format == ResolveAttribs.Format, "Invalid format of a resolve operation"); + } + if (SrcFmtAttribs.IsTypeless && DstFmtAttribs.IsTypeless) + { + DEV_CHECK_ERR(ResolveAttribs.Format != TEX_FORMAT_UNKNOWN, + "Format of a resolve operation must not be unknown when both src and dst texture formats are typeless"); + } + if (SrcFmtAttribs.IsTypeless || DstFmtAttribs.IsTypeless) + { + DEV_CHECK_ERR(!ResolveFmtAttribs.IsTypeless, + "Format of a resolve operation must not be typeless when one of the texture formats is typeless"); + } +#endif +} + +#ifdef DEVELOPMENT +template +inline bool DeviceContextBase:: + DvpVerifyDrawArguments(const DrawAttribs& Attribs) const +{ + if ((Attribs.Flags & DRAW_FLAG_VERIFY_DRAW_ATTRIBS) == 0) + return true; + + if (!m_pPipelineState) + { + LOG_ERROR_MESSAGE("Draw command arguments are invalid: no pipeline state is bound."); + return false; + } + + if (m_pPipelineState->GetDesc().IsComputePipeline) + { + LOG_ERROR_MESSAGE("Draw command arguments are invalid: pipeline state '", m_pPipelineState->GetDesc().Name, "' is a compute pipeline."); + return false; + } + + if (Attribs.NumVertices == 0) + { + LOG_WARNING_MESSAGE("Draw command arguments are invalid: number of vertices to draw is zero."); + } + + return true; +} + +template +inline bool DeviceContextBase:: + DvpVerifyDrawIndexedArguments(const DrawIndexedAttribs& Attribs) const +{ + if ((Attribs.Flags & DRAW_FLAG_VERIFY_DRAW_ATTRIBS) == 0) + return true; + + if (!m_pPipelineState) + { + LOG_ERROR_MESSAGE("DrawIndexed command arguments are invalid: no pipeline state is bound."); + return false; + } + + if (m_pPipelineState->GetDesc().IsComputePipeline) + { + LOG_ERROR_MESSAGE("DrawIndexed command arguments are invalid: pipeline state '", + m_pPipelineState->GetDesc().Name, "' is a compute pipeline."); + return false; + } + + if (Attribs.IndexType != VT_UINT16 && Attribs.IndexType != VT_UINT32) + { + LOG_ERROR_MESSAGE("DrawIndexed command arguments are invalid: IndexType (", + GetValueTypeString(Attribs.IndexType), ") must be VT_UINT16 or VT_UINT32."); + return false; + } + + if (!m_pIndexBuffer) + { + LOG_ERROR_MESSAGE("DrawIndexed command arguments are invalid: no index buffer is bound."); + return false; + } + + if (Attribs.NumIndices == 0) + { + LOG_WARNING_MESSAGE("DrawIndexed command arguments are invalid: number of indices to draw is zero."); + } + + return true; +} + +template +inline bool DeviceContextBase:: + DvpVerifyDrawIndirectArguments(const DrawIndirectAttribs& Attribs, const IBuffer* pAttribsBuffer) const +{ + if ((Attribs.Flags & DRAW_FLAG_VERIFY_DRAW_ATTRIBS) == 0) + return true; + + if (!m_pPipelineState) + { + LOG_ERROR_MESSAGE("DrawIndirect command arguments are invalid: no pipeline state is bound."); + return false; + } + + if (m_pPipelineState->GetDesc().IsComputePipeline) + { + + LOG_ERROR_MESSAGE("DrawIndirect command arguments are invalid: pipeline state '", + m_pPipelineState->GetDesc().Name, "' is a compute pipeline."); + return false; + } + + if (pAttribsBuffer != nullptr) + { + if ((pAttribsBuffer->GetDesc().BindFlags & BIND_INDIRECT_DRAW_ARGS) == 0) + { + LOG_ERROR_MESSAGE("DrawIndirect command arguments are invalid: indirect draw arguments buffer '", + pAttribsBuffer->GetDesc().Name, "' was not created with BIND_INDIRECT_DRAW_ARGS flag."); + return false; + } + } + else + { + LOG_ERROR_MESSAGE("DrawIndirect command arguments are invalid: indirect draw arguments buffer is null."); + return false; + } + + return true; +} + +template +inline bool DeviceContextBase:: + DvpVerifyDrawIndexedIndirectArguments(const DrawIndexedIndirectAttribs& Attribs, const IBuffer* pAttribsBuffer) const +{ + if ((Attribs.Flags & DRAW_FLAG_VERIFY_DRAW_ATTRIBS) == 0) + return true; + + if (!m_pPipelineState) + { + LOG_ERROR_MESSAGE("DrawIndexedIndirect command arguments are invalid: no pipeline state is bound."); + return false; + } + + if (m_pPipelineState->GetDesc().IsComputePipeline) + { + LOG_ERROR_MESSAGE("DrawIndexedIndirect command arguments are invalid: pipeline state '", + m_pPipelineState->GetDesc().Name, "' is a compute pipeline."); + return false; + } + + if (Attribs.IndexType != VT_UINT16 && Attribs.IndexType != VT_UINT32) + { + LOG_ERROR_MESSAGE("DrawIndexedIndirect command arguments are invalid: IndexType (", + GetValueTypeString(Attribs.IndexType), ") must be VT_UINT16 or VT_UINT32."); + return false; + } + + if (!m_pIndexBuffer) + { + LOG_ERROR_MESSAGE("DrawIndexedIndirect command arguments are invalid: no index buffer is bound."); + return false; + } + + if (pAttribsBuffer != nullptr) + { + if ((pAttribsBuffer->GetDesc().BindFlags & BIND_INDIRECT_DRAW_ARGS) == 0) + { + LOG_ERROR_MESSAGE("DrawIndexedIndirect command arguments are invalid: indirect draw arguments buffer '", + pAttribsBuffer->GetDesc().Name, "' was not created with BIND_INDIRECT_DRAW_ARGS flag."); + return false; + } + } + else + { + LOG_ERROR_MESSAGE("DrawIndexedIndirect command arguments are invalid: indirect draw arguments buffer is null."); + return false; + } + + return true; +} + +template +inline void DeviceContextBase:: + DvpVerifyRenderTargets() const +{ + if (!m_pPipelineState) + { + LOG_ERROR("No pipeline state is bound"); + return; + } + + TEXTURE_FORMAT BoundRTVFormats[8] = {TEX_FORMAT_UNKNOWN}; + TEXTURE_FORMAT BoundDSVFormat = TEX_FORMAT_UNKNOWN; + + for (Uint32 rt = 0; rt < m_NumBoundRenderTargets; ++rt) + { + if (auto* pRT = m_pBoundRenderTargets[rt].RawPtr()) + BoundRTVFormats[rt] = pRT->GetDesc().Format; + else + BoundRTVFormats[rt] = TEX_FORMAT_UNKNOWN; + } + + BoundDSVFormat = m_pBoundDepthStencil ? m_pBoundDepthStencil->GetDesc().Format : TEX_FORMAT_UNKNOWN; + + const auto& PSODesc = m_pPipelineState->GetDesc(); + const auto& GraphicsPipeline = PSODesc.GraphicsPipeline; + if (GraphicsPipeline.NumRenderTargets != m_NumBoundRenderTargets) + { + LOG_WARNING_MESSAGE("Number of currently bound render targets (", m_NumBoundRenderTargets, + ") does not match the number of outputs specified by the PSO '", PSODesc.Name, + "' (", Uint32{GraphicsPipeline.NumRenderTargets}, ")."); + } + + if (BoundDSVFormat != GraphicsPipeline.DSVFormat) + { + LOG_WARNING_MESSAGE("Currently bound depth-stencil buffer format (", GetTextureFormatAttribs(BoundDSVFormat).Name, + ") does not match the DSV format specified by the PSO '", PSODesc.Name, + "' (", GetTextureFormatAttribs(GraphicsPipeline.DSVFormat).Name, ")."); + } + + for (Uint32 rt = 0; rt < m_NumBoundRenderTargets; ++rt) + { + auto BoundFmt = BoundRTVFormats[rt]; + auto PSOFmt = GraphicsPipeline.RTVFormats[rt]; + if (BoundFmt != PSOFmt) + { + LOG_WARNING_MESSAGE("Render target bound to slot ", rt, " (", GetTextureFormatAttribs(BoundFmt).Name, + ") does not match the RTV format specified by the PSO '", PSODesc.Name, + "' (", GetTextureFormatAttribs(PSOFmt).Name, ")."); + } + } +} + + + +template +inline bool DeviceContextBase:: + DvpVerifyDispatchArguments(const DispatchComputeAttribs& Attribs) const +{ + if (!m_pPipelineState) + { + LOG_ERROR_MESSAGE("DispatchCompute command arguments are invalid: no pipeline state is bound."); + return false; + } + + if (!m_pPipelineState->GetDesc().IsComputePipeline) + { + LOG_ERROR_MESSAGE("DispatchCompute command arguments are invalid: pipeline state '", m_pPipelineState->GetDesc().Name, + "' is a graphics pipeline."); + return false; + } + + if (Attribs.ThreadGroupCountX == 0) + LOG_WARNING_MESSAGE("DispatchCompute command arguments are invalid: ThreadGroupCountX is zero."); + + if (Attribs.ThreadGroupCountY == 0) + LOG_WARNING_MESSAGE("DispatchCompute command arguments are invalid: ThreadGroupCountY is zero."); + + if (Attribs.ThreadGroupCountZ == 0) + LOG_WARNING_MESSAGE("DispatchCompute command arguments are invalid: ThreadGroupCountZ is zero."); + + return true; +} + +template +inline bool DeviceContextBase:: + DvpVerifyDispatchIndirectArguments(const DispatchComputeIndirectAttribs& Attribs, const IBuffer* pAttribsBuffer) const +{ + if (!m_pPipelineState) + { + LOG_ERROR_MESSAGE("DispatchComputeIndirect command arguments are invalid: no pipeline state is bound."); + return false; + } + + if (!m_pPipelineState->GetDesc().IsComputePipeline) + { + LOG_ERROR_MESSAGE("DispatchComputeIndirect command arguments are invalid: pipeline state '", + m_pPipelineState->GetDesc().Name, "' is a graphics pipeline."); + return false; + } + + if (pAttribsBuffer != nullptr) + { + if ((pAttribsBuffer->GetDesc().BindFlags & BIND_INDIRECT_DRAW_ARGS) == 0) + { + LOG_ERROR_MESSAGE("DispatchComputeIndirect command arguments are invalid: indirect dispatch arguments buffer '", + pAttribsBuffer->GetDesc().Name, "' was not created with BIND_INDIRECT_DRAW_ARGS flag."); + return false; + } + } + else + { + LOG_ERROR_MESSAGE("DispatchComputeIndirect command arguments are invalid: indirect dispatch arguments buffer is null."); + return false; + } + + return true; +} + + +template +void DeviceContextBase:: + DvpVerifyStateTransitionDesc(const StateTransitionDesc& Barrier) const +{ + DEV_CHECK_ERR((Barrier.pTexture != nullptr) ^ (Barrier.pBuffer != nullptr), "Exactly one of pTexture or pBuffer members of StateTransitionDesc must not be null"); + DEV_CHECK_ERR(Barrier.NewState != RESOURCE_STATE_UNKNOWN, "New resource state can't be unknown"); + RESOURCE_STATE OldState = RESOURCE_STATE_UNKNOWN; + if (Barrier.pTexture) + { + const auto& TexDesc = Barrier.pTexture->GetDesc(); + + DEV_CHECK_ERR(VerifyResourceStates(Barrier.NewState, true), "Invlaid new state specified for texture '", TexDesc.Name, "'"); + OldState = Barrier.OldState != RESOURCE_STATE_UNKNOWN ? Barrier.OldState : Barrier.pTexture->GetState(); + DEV_CHECK_ERR(OldState != RESOURCE_STATE_UNKNOWN, + "The state of texture '", TexDesc.Name, + "' is unknown to the engine and is not explicitly specified in the barrier"); + DEV_CHECK_ERR(VerifyResourceStates(OldState, true), "Invlaid old state specified for texture '", TexDesc.Name, "'"); + + DEV_CHECK_ERR(Barrier.FirstMipLevel < TexDesc.MipLevels, "First mip level (", Barrier.FirstMipLevel, + ") specified by the barrier is out of range. Texture '", + TexDesc.Name, "' has only ", TexDesc.MipLevels, " mip level(s)"); + DEV_CHECK_ERR(Barrier.MipLevelsCount == REMAINING_MIP_LEVELS || Barrier.FirstMipLevel + Barrier.MipLevelsCount <= TexDesc.MipLevels, + "Mip level range ", Barrier.FirstMipLevel, "..", Barrier.FirstMipLevel + Barrier.MipLevelsCount - 1, + " specified by the barrier is out of range. Texture '", + TexDesc.Name, "' has only ", TexDesc.MipLevels, " mip level(s)"); + + DEV_CHECK_ERR(Barrier.FirstArraySlice < TexDesc.ArraySize, "First array slice (", Barrier.FirstArraySlice, + ") specified by the barrier is out of range. Array size of texture '", + TexDesc.Name, "' is ", TexDesc.ArraySize); + DEV_CHECK_ERR(Barrier.ArraySliceCount == REMAINING_ARRAY_SLICES || Barrier.FirstArraySlice + Barrier.ArraySliceCount <= TexDesc.ArraySize, + "Array slice range ", Barrier.FirstArraySlice, "..", Barrier.FirstArraySlice + Barrier.ArraySliceCount - 1, + " specified by the barrier is out of range. Array size of texture '", + TexDesc.Name, "' is ", TexDesc.ArraySize); + + auto DevType = m_pDevice->GetDeviceCaps().DevType; + if (DevType != RENDER_DEVICE_TYPE_D3D12 && DevType != RENDER_DEVICE_TYPE_VULKAN) + { + DEV_CHECK_ERR(Barrier.FirstMipLevel == 0 && (Barrier.MipLevelsCount == REMAINING_MIP_LEVELS || Barrier.MipLevelsCount == TexDesc.MipLevels), + "Failed to transition texture '", TexDesc.Name, "': only whole resources can be transitioned on this device"); + DEV_CHECK_ERR(Barrier.FirstArraySlice == 0 && (Barrier.ArraySliceCount == REMAINING_ARRAY_SLICES || Barrier.ArraySliceCount == TexDesc.ArraySize), + "Failed to transition texture '", TexDesc.Name, "': only whole resources can be transitioned on this device"); + } + } + else + { + const auto& BuffDesc = Barrier.pBuffer->GetDesc(); + DEV_CHECK_ERR(VerifyResourceStates(Barrier.NewState, false), "Invlaid new state specified for buffer '", BuffDesc.Name, "'"); + OldState = Barrier.OldState != RESOURCE_STATE_UNKNOWN ? Barrier.OldState : Barrier.pBuffer->GetState(); + DEV_CHECK_ERR(OldState != RESOURCE_STATE_UNKNOWN, "The state of buffer '", BuffDesc.Name, "' is unknown to the engine and is not explicitly specified in the barrier"); + DEV_CHECK_ERR(VerifyResourceStates(OldState, false), "Invlaid old state specified for buffer '", BuffDesc.Name, "'"); + } + + if (OldState == RESOURCE_STATE_UNORDERED_ACCESS && Barrier.NewState == RESOURCE_STATE_UNORDERED_ACCESS) + { + DEV_CHECK_ERR(Barrier.TransitionType == STATE_TRANSITION_TYPE_IMMEDIATE, "For UAV barriers, transition type must be STATE_TRANSITION_TYPE_IMMEDIATE"); + } + + if (Barrier.TransitionType == STATE_TRANSITION_TYPE_BEGIN) + { + DEV_CHECK_ERR(!Barrier.UpdateResourceState, "Resource state can't be updated in begin-split barrier"); + } +} + +template +bool DeviceContextBase:: + DvpVerifyTextureState(const TextureImplType& Texture, RESOURCE_STATE RequiredState, const char* OperationName) const +{ + if (Texture.IsInKnownState() && !Texture.CheckState(RequiredState)) + { + LOG_ERROR_MESSAGE(OperationName, " requires texture '", Texture.GetDesc().Name, "' to be transitioned to ", GetResourceStateString(RequiredState), + " state. Actual texture state: ", GetResourceStateString(Texture.GetState()), + ". Use appropriate state transiton flags or explicitly transition the texture using IDeviceContext::TransitionResourceStates() method."); + return false; + } + + return true; +} + +template +bool DeviceContextBase:: + DvpVerifyBufferState(const BufferImplType& Buffer, RESOURCE_STATE RequiredState, const char* OperationName) const +{ + if (Buffer.IsInKnownState() && !Buffer.CheckState(RequiredState)) + { + LOG_ERROR_MESSAGE(OperationName, " requires buffer '", Buffer.GetDesc().Name, "' to be transitioned to ", GetResourceStateString(RequiredState), + " state. Actual buffer state: ", GetResourceStateString(Buffer.GetState()), + ". Use appropriate state transiton flags or explicitly transition the buffer using IDeviceContext::TransitionResourceStates() method."); + return false; + } + + return true; +} + +#endif // DEVELOPMENT + +} // namespace Diligent diff --git a/Graphics/GraphicsEngine/include/DeviceObjectBase.h b/Graphics/GraphicsEngine/include/DeviceObjectBase.h deleted file mode 100644 index 31a00f40..00000000 --- a/Graphics/GraphicsEngine/include/DeviceObjectBase.h +++ /dev/null @@ -1,178 +0,0 @@ -/* - * Copyright 2019-2020 Diligent Graphics LLC - * Copyright 2015-2019 Egor Yusov - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - * - * In no event and under no legal theory, whether in tort (including negligence), - * contract, or otherwise, unless required by applicable law (such as deliberate - * and grossly negligent acts) or agreed to in writing, shall any Contributor be - * liable for any damages, including any direct, indirect, special, incidental, - * or consequential damages of any character arising as a result of this License or - * out of the use or inability to use the software (including but not limited to damages - * for loss of goodwill, work stoppage, computer failure or malfunction, or any and - * all other commercial damages or losses), even if such Contributor has been advised - * of the possibility of such damages. - */ - -#pragma once - -/// \file -/// Implementation of the Diligent::DeviceObjectBase template class - -#include -#include -#include "RefCntAutoPtr.h" -#include "ObjectBase.h" -#include "UniqueIdentifier.h" -#include "EngineMemory.h" - -namespace Diligent -{ - -/// Template class implementing base functionality for a device object -template -class DeviceObjectBase : public ObjectBase -{ -public: - typedef ObjectBase TBase; - - /// \param pRefCounters - reference counters object that controls the lifetime of this device object - /// \param pDevice - pointer to the render device. - /// \param ObjDesc - object description. - /// \param bIsDeviceInternal - flag indicating if the object is an internal device object - /// and must not keep a strong reference to the device. - DeviceObjectBase(IReferenceCounters* pRefCounters, - RenderDeviceImplType* pDevice, - const ObjectDescType& ObjDesc, - bool bIsDeviceInternal = false) : - // clang-format off - TBase {pRefCounters}, - m_pDevice {pDevice }, - m_Desc {ObjDesc }, - m_bIsDeviceInternal{bIsDeviceInternal} - //clang-format on - { - // Do not keep strong reference to the device if the object is an internal device object - if (!m_bIsDeviceInternal) - { - m_pDevice->AddRef(); - } - - if (ObjDesc.Name != nullptr) - { - auto size = strlen(ObjDesc.Name) + 1; - auto* NameCopy = ALLOCATE(GetStringAllocator(), "Object name copy", char, size); - memcpy(NameCopy, ObjDesc.Name, size); - m_Desc.Name = NameCopy; - } - else - { - size_t size = 16 + 2 + 1; // 0x12345678 - auto* AddressStr = ALLOCATE(GetStringAllocator(), "Object address string", char, size); - snprintf(AddressStr, size, "0x%llX", static_cast(reinterpret_cast(this))); - m_Desc.Name = AddressStr; - } - - // !!!WARNING!!! - // We cannot add resource to the hash table from here, because the object - // has not been completely created yet and the reference counters object - // is not initialized! - //m_pDevice->AddResourceToHash( this ); - ERROR! - } - - // clang-format off - DeviceObjectBase (const DeviceObjectBase& ) = delete; - DeviceObjectBase ( DeviceObjectBase&&) = delete; - DeviceObjectBase& operator = (const DeviceObjectBase& ) = delete; - DeviceObjectBase& operator = ( DeviceObjectBase&&) = delete; - // clang-format on - - virtual ~DeviceObjectBase() - { - FREE(GetStringAllocator(), const_cast(m_Desc.Name)); - - if (!m_bIsDeviceInternal) - { - m_pDevice->Release(); - } - } - - inline virtual Atomics::Long Release() override final - { - // Render device owns allocators for all types of device objects, - // so it must be destroyed after all device objects are released. - // Consider the following scenario: an object A owns the last strong - // reference to the device: - // - // 1. A::~A() completes - // 2. A::~DeviceObjectBase() completes - // 3. A::m_pDevice is released - // Render device is destroyed, all allocators are invalid - // 4. RefCountersImpl::ObjectWrapperBase::DestroyObject() calls - // m_pAllocator->Free(m_pObject) - crash! - - RefCntAutoPtr pDevice; - return TBase::Release( - [&]() // - { - // We must keep the device alive while the object is being destroyed - // Note that internal device objects do not keep strong reference to the device - if (!m_bIsDeviceInternal) - { - pDevice = m_pDevice; - } - }); - } - - IMPLEMENT_QUERY_INTERFACE_IN_PLACE(IID_DeviceObject, TBase) - - virtual const ObjectDescType& GetDesc() const override final - { - return m_Desc; - } - - /// Returns unique identifier - virtual Int32 GetUniqueID() const override final - { - /// \note - /// This unique ID is used to unambiguously identify device object for - /// tracking purposes. - /// Niether GL handle nor pointer could be safely used for this purpose - /// as both GL reuses released handles and the OS reuses released pointers - return m_UniqueID.GetID(); - } - - static bool IsSameObject(DeviceObjectBase* pObj1, DeviceObjectBase* pObj2) - { - UniqueIdentifier Id1 = (pObj1 != nullptr) ? pObj1->GetUniqueID() : 0; - UniqueIdentifier Id2 = (pObj2 != nullptr) ? pObj2->GetUniqueID() : 0; - return Id1 == Id2; - } - - RenderDeviceImplType* GetDevice() const { return m_pDevice; } - -protected: - /// Pointer to the device - RenderDeviceImplType* const m_pDevice; - - /// Object description - ObjectDescType m_Desc; - - // Template argument is only used to separate counters for - // different groups of objects - UniqueIdHelper m_UniqueID; - const bool m_bIsDeviceInternal; -}; - -} // namespace Diligent diff --git a/Graphics/GraphicsEngine/include/DeviceObjectBase.hpp b/Graphics/GraphicsEngine/include/DeviceObjectBase.hpp new file mode 100644 index 00000000..31a00f40 --- /dev/null +++ b/Graphics/GraphicsEngine/include/DeviceObjectBase.hpp @@ -0,0 +1,178 @@ +/* + * Copyright 2019-2020 Diligent Graphics LLC + * Copyright 2015-2019 Egor Yusov + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + * + * In no event and under no legal theory, whether in tort (including negligence), + * contract, or otherwise, unless required by applicable law (such as deliberate + * and grossly negligent acts) or agreed to in writing, shall any Contributor be + * liable for any damages, including any direct, indirect, special, incidental, + * or consequential damages of any character arising as a result of this License or + * out of the use or inability to use the software (including but not limited to damages + * for loss of goodwill, work stoppage, computer failure or malfunction, or any and + * all other commercial damages or losses), even if such Contributor has been advised + * of the possibility of such damages. + */ + +#pragma once + +/// \file +/// Implementation of the Diligent::DeviceObjectBase template class + +#include +#include +#include "RefCntAutoPtr.h" +#include "ObjectBase.h" +#include "UniqueIdentifier.h" +#include "EngineMemory.h" + +namespace Diligent +{ + +/// Template class implementing base functionality for a device object +template +class DeviceObjectBase : public ObjectBase +{ +public: + typedef ObjectBase TBase; + + /// \param pRefCounters - reference counters object that controls the lifetime of this device object + /// \param pDevice - pointer to the render device. + /// \param ObjDesc - object description. + /// \param bIsDeviceInternal - flag indicating if the object is an internal device object + /// and must not keep a strong reference to the device. + DeviceObjectBase(IReferenceCounters* pRefCounters, + RenderDeviceImplType* pDevice, + const ObjectDescType& ObjDesc, + bool bIsDeviceInternal = false) : + // clang-format off + TBase {pRefCounters}, + m_pDevice {pDevice }, + m_Desc {ObjDesc }, + m_bIsDeviceInternal{bIsDeviceInternal} + //clang-format on + { + // Do not keep strong reference to the device if the object is an internal device object + if (!m_bIsDeviceInternal) + { + m_pDevice->AddRef(); + } + + if (ObjDesc.Name != nullptr) + { + auto size = strlen(ObjDesc.Name) + 1; + auto* NameCopy = ALLOCATE(GetStringAllocator(), "Object name copy", char, size); + memcpy(NameCopy, ObjDesc.Name, size); + m_Desc.Name = NameCopy; + } + else + { + size_t size = 16 + 2 + 1; // 0x12345678 + auto* AddressStr = ALLOCATE(GetStringAllocator(), "Object address string", char, size); + snprintf(AddressStr, size, "0x%llX", static_cast(reinterpret_cast(this))); + m_Desc.Name = AddressStr; + } + + // !!!WARNING!!! + // We cannot add resource to the hash table from here, because the object + // has not been completely created yet and the reference counters object + // is not initialized! + //m_pDevice->AddResourceToHash( this ); - ERROR! + } + + // clang-format off + DeviceObjectBase (const DeviceObjectBase& ) = delete; + DeviceObjectBase ( DeviceObjectBase&&) = delete; + DeviceObjectBase& operator = (const DeviceObjectBase& ) = delete; + DeviceObjectBase& operator = ( DeviceObjectBase&&) = delete; + // clang-format on + + virtual ~DeviceObjectBase() + { + FREE(GetStringAllocator(), const_cast(m_Desc.Name)); + + if (!m_bIsDeviceInternal) + { + m_pDevice->Release(); + } + } + + inline virtual Atomics::Long Release() override final + { + // Render device owns allocators for all types of device objects, + // so it must be destroyed after all device objects are released. + // Consider the following scenario: an object A owns the last strong + // reference to the device: + // + // 1. A::~A() completes + // 2. A::~DeviceObjectBase() completes + // 3. A::m_pDevice is released + // Render device is destroyed, all allocators are invalid + // 4. RefCountersImpl::ObjectWrapperBase::DestroyObject() calls + // m_pAllocator->Free(m_pObject) - crash! + + RefCntAutoPtr pDevice; + return TBase::Release( + [&]() // + { + // We must keep the device alive while the object is being destroyed + // Note that internal device objects do not keep strong reference to the device + if (!m_bIsDeviceInternal) + { + pDevice = m_pDevice; + } + }); + } + + IMPLEMENT_QUERY_INTERFACE_IN_PLACE(IID_DeviceObject, TBase) + + virtual const ObjectDescType& GetDesc() const override final + { + return m_Desc; + } + + /// Returns unique identifier + virtual Int32 GetUniqueID() const override final + { + /// \note + /// This unique ID is used to unambiguously identify device object for + /// tracking purposes. + /// Niether GL handle nor pointer could be safely used for this purpose + /// as both GL reuses released handles and the OS reuses released pointers + return m_UniqueID.GetID(); + } + + static bool IsSameObject(DeviceObjectBase* pObj1, DeviceObjectBase* pObj2) + { + UniqueIdentifier Id1 = (pObj1 != nullptr) ? pObj1->GetUniqueID() : 0; + UniqueIdentifier Id2 = (pObj2 != nullptr) ? pObj2->GetUniqueID() : 0; + return Id1 == Id2; + } + + RenderDeviceImplType* GetDevice() const { return m_pDevice; } + +protected: + /// Pointer to the device + RenderDeviceImplType* const m_pDevice; + + /// Object description + ObjectDescType m_Desc; + + // Template argument is only used to separate counters for + // different groups of objects + UniqueIdHelper m_UniqueID; + const bool m_bIsDeviceInternal; +}; + +} // namespace Diligent diff --git a/Graphics/GraphicsEngine/include/EngineFactoryBase.h b/Graphics/GraphicsEngine/include/EngineFactoryBase.h deleted file mode 100644 index 2e0b954c..00000000 --- a/Graphics/GraphicsEngine/include/EngineFactoryBase.h +++ /dev/null @@ -1,155 +0,0 @@ -/* - * Copyright 2019-2020 Diligent Graphics LLC - * Copyright 2015-2019 Egor Yusov - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - * - * In no event and under no legal theory, whether in tort (including negligence), - * contract, or otherwise, unless required by applicable law (such as deliberate - * and grossly negligent acts) or agreed to in writing, shall any Contributor be - * liable for any damages, including any direct, indirect, special, incidental, - * or consequential damages of any character arising as a result of this License or - * out of the use or inability to use the software (including but not limited to damages - * for loss of goodwill, work stoppage, computer failure or malfunction, or any and - * all other commercial damages or losses), even if such Contributor has been advised - * of the possibility of such damages. - */ - -#pragma once - -/// \file -/// Implementation of the Diligent::EngineFactoryBase template class - -#include "Object.h" -#include "EngineFactory.h" -#include "DefaultShaderSourceStreamFactory.h" - -namespace Diligent -{ - -const APIInfo& GetAPIInfo(); - -/// Template class implementing base functionality for an engine factory - -/// \tparam BaseInterface - base interface that this class will inheret -/// (Diligent::IEngineFactoryD3D11, Diligent::IEngineFactoryD3D12, -/// Diligent::IEngineFactoryVk or Diligent::IEngineFactoryOpenGL). -template -class EngineFactoryBase : public BaseInterface -{ -public: - EngineFactoryBase(const INTERFACE_ID& FactoryIID) noexcept : - // clang-format off - m_FactoryIID {FactoryIID}, - m_RefCounters {*this } - // clang-format on - { - } - - virtual void QueryInterface(const INTERFACE_ID& IID, IObject** ppInterface) override final - { - if (ppInterface == nullptr) - return; - - *ppInterface = nullptr; - if (IID == IID_Unknown || IID == m_FactoryIID || IID == IID_EngineFactory) - { - *ppInterface = this; - (*ppInterface)->AddRef(); - } - } - - virtual ReferenceCounterValueType AddRef() override final - { - return m_RefCounters.AddStrongRef(); - } - - virtual ReferenceCounterValueType Release() override final - { - return m_RefCounters.ReleaseStrongRef(); - } - - virtual IReferenceCounters* GetReferenceCounters() const override final - { - return const_cast(static_cast(&m_RefCounters)); - } - - virtual const APIInfo& GetAPIInfo() const override final - { - return Diligent::GetAPIInfo(); - } - - virtual void CreateDefaultShaderSourceStreamFactory(const Char* SearchDirectories, - IShaderSourceInputStreamFactory** ppShaderSourceFactory) const override final - { - Diligent::CreateDefaultShaderSourceStreamFactory(SearchDirectories, ppShaderSourceFactory); - } - -private: - class DummyReferenceCounters final : public IReferenceCounters - { - public: - DummyReferenceCounters(EngineFactoryBase& Factory) noexcept : - m_Factory{Factory} - { - m_lNumStrongReferences = 0; - m_lNumWeakReferences = 0; - } - - virtual ReferenceCounterValueType AddStrongRef() override final - { - return Atomics::AtomicIncrement(m_lNumStrongReferences); - } - - virtual ReferenceCounterValueType ReleaseStrongRef() override final - { - return Atomics::AtomicDecrement(m_lNumStrongReferences); - } - - virtual ReferenceCounterValueType AddWeakRef() override final - { - return Atomics::AtomicIncrement(m_lNumWeakReferences); - } - - virtual ReferenceCounterValueType ReleaseWeakRef() override final - { - return Atomics::AtomicDecrement(m_lNumWeakReferences); - } - - virtual void GetObject(IObject** ppObject) override final - { - if (ppObject != nullptr) - m_Factory.QueryInterface(IID_Unknown, ppObject); - } - - virtual ReferenceCounterValueType GetNumStrongRefs() const override final - { - return m_lNumStrongReferences; - } - - virtual ReferenceCounterValueType GetNumWeakRefs() const override final - { - return m_lNumWeakReferences; - } - - private: - EngineFactoryBase& m_Factory; - Atomics::AtomicLong m_lNumStrongReferences; - Atomics::AtomicLong m_lNumWeakReferences; - }; - - const INTERFACE_ID m_FactoryIID; - DummyReferenceCounters m_RefCounters; -}; - -} // namespace Diligent diff --git a/Graphics/GraphicsEngine/include/EngineFactoryBase.hpp b/Graphics/GraphicsEngine/include/EngineFactoryBase.hpp new file mode 100644 index 00000000..2e0b954c --- /dev/null +++ b/Graphics/GraphicsEngine/include/EngineFactoryBase.hpp @@ -0,0 +1,155 @@ +/* + * Copyright 2019-2020 Diligent Graphics LLC + * Copyright 2015-2019 Egor Yusov + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + * + * In no event and under no legal theory, whether in tort (including negligence), + * contract, or otherwise, unless required by applicable law (such as deliberate + * and grossly negligent acts) or agreed to in writing, shall any Contributor be + * liable for any damages, including any direct, indirect, special, incidental, + * or consequential damages of any character arising as a result of this License or + * out of the use or inability to use the software (including but not limited to damages + * for loss of goodwill, work stoppage, computer failure or malfunction, or any and + * all other commercial damages or losses), even if such Contributor has been advised + * of the possibility of such damages. + */ + +#pragma once + +/// \file +/// Implementation of the Diligent::EngineFactoryBase template class + +#include "Object.h" +#include "EngineFactory.h" +#include "DefaultShaderSourceStreamFactory.h" + +namespace Diligent +{ + +const APIInfo& GetAPIInfo(); + +/// Template class implementing base functionality for an engine factory + +/// \tparam BaseInterface - base interface that this class will inheret +/// (Diligent::IEngineFactoryD3D11, Diligent::IEngineFactoryD3D12, +/// Diligent::IEngineFactoryVk or Diligent::IEngineFactoryOpenGL). +template +class EngineFactoryBase : public BaseInterface +{ +public: + EngineFactoryBase(const INTERFACE_ID& FactoryIID) noexcept : + // clang-format off + m_FactoryIID {FactoryIID}, + m_RefCounters {*this } + // clang-format on + { + } + + virtual void QueryInterface(const INTERFACE_ID& IID, IObject** ppInterface) override final + { + if (ppInterface == nullptr) + return; + + *ppInterface = nullptr; + if (IID == IID_Unknown || IID == m_FactoryIID || IID == IID_EngineFactory) + { + *ppInterface = this; + (*ppInterface)->AddRef(); + } + } + + virtual ReferenceCounterValueType AddRef() override final + { + return m_RefCounters.AddStrongRef(); + } + + virtual ReferenceCounterValueType Release() override final + { + return m_RefCounters.ReleaseStrongRef(); + } + + virtual IReferenceCounters* GetReferenceCounters() const override final + { + return const_cast(static_cast(&m_RefCounters)); + } + + virtual const APIInfo& GetAPIInfo() const override final + { + return Diligent::GetAPIInfo(); + } + + virtual void CreateDefaultShaderSourceStreamFactory(const Char* SearchDirectories, + IShaderSourceInputStreamFactory** ppShaderSourceFactory) const override final + { + Diligent::CreateDefaultShaderSourceStreamFactory(SearchDirectories, ppShaderSourceFactory); + } + +private: + class DummyReferenceCounters final : public IReferenceCounters + { + public: + DummyReferenceCounters(EngineFactoryBase& Factory) noexcept : + m_Factory{Factory} + { + m_lNumStrongReferences = 0; + m_lNumWeakReferences = 0; + } + + virtual ReferenceCounterValueType AddStrongRef() override final + { + return Atomics::AtomicIncrement(m_lNumStrongReferences); + } + + virtual ReferenceCounterValueType ReleaseStrongRef() override final + { + return Atomics::AtomicDecrement(m_lNumStrongReferences); + } + + virtual ReferenceCounterValueType AddWeakRef() override final + { + return Atomics::AtomicIncrement(m_lNumWeakReferences); + } + + virtual ReferenceCounterValueType ReleaseWeakRef() override final + { + return Atomics::AtomicDecrement(m_lNumWeakReferences); + } + + virtual void GetObject(IObject** ppObject) override final + { + if (ppObject != nullptr) + m_Factory.QueryInterface(IID_Unknown, ppObject); + } + + virtual ReferenceCounterValueType GetNumStrongRefs() const override final + { + return m_lNumStrongReferences; + } + + virtual ReferenceCounterValueType GetNumWeakRefs() const override final + { + return m_lNumWeakReferences; + } + + private: + EngineFactoryBase& m_Factory; + Atomics::AtomicLong m_lNumStrongReferences; + Atomics::AtomicLong m_lNumWeakReferences; + }; + + const INTERFACE_ID m_FactoryIID; + DummyReferenceCounters m_RefCounters; +}; + +} // namespace Diligent diff --git a/Graphics/GraphicsEngine/include/FenceBase.h b/Graphics/GraphicsEngine/include/FenceBase.h deleted file mode 100644 index 2fac4355..00000000 --- a/Graphics/GraphicsEngine/include/FenceBase.h +++ /dev/null @@ -1,71 +0,0 @@ -/* - * Copyright 2019-2020 Diligent Graphics LLC - * Copyright 2015-2019 Egor Yusov - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - * - * In no event and under no legal theory, whether in tort (including negligence), - * contract, or otherwise, unless required by applicable law (such as deliberate - * and grossly negligent acts) or agreed to in writing, shall any Contributor be - * liable for any damages, including any direct, indirect, special, incidental, - * or consequential damages of any character arising as a result of this License or - * out of the use or inability to use the software (including but not limited to damages - * for loss of goodwill, work stoppage, computer failure or malfunction, or any and - * all other commercial damages or losses), even if such Contributor has been advised - * of the possibility of such damages. - */ - -#pragma once - -/// \file -/// Implementation of the Diligent::FenceBase template class - -#include "DeviceObjectBase.h" -#include "GraphicsTypes.h" -#include "RefCntAutoPtr.h" - -namespace Diligent -{ - -class IRenderDevice; -class IFence; - -/// Template class implementing base functionality for a Fence object - -/// \tparam BaseInterface - base interface that this class will inheret -/// (Diligent::IFenceD3D11, Diligent::IFenceD3D12, -/// Diligent::IFenceGL or Diligent::IFenceVk). -/// \tparam RenderDeviceImplType - type of the render device implementation -template -class FenceBase : public DeviceObjectBase -{ -public: - typedef DeviceObjectBase TDeviceObjectBase; - - /// \param pRefCounters - reference counters object that controls the lifetime of this command list. - /// \param Desc - fence description - /// \param pDevice - pointer to the device. - /// \param bIsDeviceInternal - flag indicating if the Fence is an internal device object and - /// must not keep a strong reference to the device. - FenceBase(IReferenceCounters* pRefCounters, RenderDeviceImplType* pDevice, const FenceDesc& Desc, bool bIsDeviceInternal = false) : - TDeviceObjectBase{pRefCounters, pDevice, Desc, bIsDeviceInternal} - {} - - ~FenceBase() - { - } - - IMPLEMENT_QUERY_INTERFACE_IN_PLACE(IID_Fence, TDeviceObjectBase) -}; - -} // namespace Diligent diff --git a/Graphics/GraphicsEngine/include/FenceBase.hpp b/Graphics/GraphicsEngine/include/FenceBase.hpp new file mode 100644 index 00000000..b596e8c8 --- /dev/null +++ b/Graphics/GraphicsEngine/include/FenceBase.hpp @@ -0,0 +1,71 @@ +/* + * Copyright 2019-2020 Diligent Graphics LLC + * Copyright 2015-2019 Egor Yusov + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + * + * In no event and under no legal theory, whether in tort (including negligence), + * contract, or otherwise, unless required by applicable law (such as deliberate + * and grossly negligent acts) or agreed to in writing, shall any Contributor be + * liable for any damages, including any direct, indirect, special, incidental, + * or consequential damages of any character arising as a result of this License or + * out of the use or inability to use the software (including but not limited to damages + * for loss of goodwill, work stoppage, computer failure or malfunction, or any and + * all other commercial damages or losses), even if such Contributor has been advised + * of the possibility of such damages. + */ + +#pragma once + +/// \file +/// Implementation of the Diligent::FenceBase template class + +#include "DeviceObjectBase.hpp" +#include "GraphicsTypes.h" +#include "RefCntAutoPtr.h" + +namespace Diligent +{ + +class IRenderDevice; +class IFence; + +/// Template class implementing base functionality for a Fence object + +/// \tparam BaseInterface - base interface that this class will inheret +/// (Diligent::IFenceD3D11, Diligent::IFenceD3D12, +/// Diligent::IFenceGL or Diligent::IFenceVk). +/// \tparam RenderDeviceImplType - type of the render device implementation +template +class FenceBase : public DeviceObjectBase +{ +public: + typedef DeviceObjectBase TDeviceObjectBase; + + /// \param pRefCounters - reference counters object that controls the lifetime of this command list. + /// \param Desc - fence description + /// \param pDevice - pointer to the device. + /// \param bIsDeviceInternal - flag indicating if the Fence is an internal device object and + /// must not keep a strong reference to the device. + FenceBase(IReferenceCounters* pRefCounters, RenderDeviceImplType* pDevice, const FenceDesc& Desc, bool bIsDeviceInternal = false) : + TDeviceObjectBase{pRefCounters, pDevice, Desc, bIsDeviceInternal} + {} + + ~FenceBase() + { + } + + IMPLEMENT_QUERY_INTERFACE_IN_PLACE(IID_Fence, TDeviceObjectBase) +}; + +} // namespace Diligent diff --git a/Graphics/GraphicsEngine/include/PipelineStateBase.h b/Graphics/GraphicsEngine/include/PipelineStateBase.h deleted file mode 100644 index fd3563a0..00000000 --- a/Graphics/GraphicsEngine/include/PipelineStateBase.h +++ /dev/null @@ -1,390 +0,0 @@ -/* - * Copyright 2019-2020 Diligent Graphics LLC - * Copyright 2015-2019 Egor Yusov - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - * - * In no event and under no legal theory, whether in tort (including negligence), - * contract, or otherwise, unless required by applicable law (such as deliberate - * and grossly negligent acts) or agreed to in writing, shall any Contributor be - * liable for any damages, including any direct, indirect, special, incidental, - * or consequential damages of any character arising as a result of this License or - * out of the use or inability to use the software (including but not limited to damages - * for loss of goodwill, work stoppage, computer failure or malfunction, or any and - * all other commercial damages or losses), even if such Contributor has been advised - * of the possibility of such damages. - */ - -#pragma once - -/// \file -/// Implementation of the Diligent::PipelineStateBase template class - -#include -#include - -#include "PipelineState.h" -#include "DeviceObjectBase.h" -#include "STDAllocator.h" -#include "EngineMemory.h" -#include "GraphicsAccessories.hpp" -#include "StringPool.h" - -namespace Diligent -{ - -/// Template class implementing base functionality for a pipeline state object. - -/// \tparam BaseInterface - base interface that this class will inheret -/// (Diligent::IPipelineStateD3D11, Diligent::IPipelineStateD3D12, -/// Diligent::IPipelineStateGL or Diligent::IPipelineStateVk). -/// \tparam RenderDeviceImplType - type of the render device implementation -/// (Diligent::RenderDeviceD3D11Impl, Diligent::RenderDeviceD3D12Impl, -/// Diligent::RenderDeviceGLImpl, or Diligent::RenderDeviceVkImpl) -template -class PipelineStateBase : public DeviceObjectBase -{ -public: - using TDeviceObjectBase = DeviceObjectBase; - - /// \param pRefCounters - reference counters object that controls the lifetime of this PSO - /// \param pDevice - pointer to the device. - /// \param PSODesc - pipeline state description. - /// \param bIsDeviceInternal - flag indicating if the blend state is an internal device object and - /// must not keep a strong reference to the device. - PipelineStateBase(IReferenceCounters* pRefCounters, - RenderDeviceImplType* pDevice, - const PipelineStateDesc& PSODesc, - bool bIsDeviceInternal = false) : - TDeviceObjectBase{pRefCounters, pDevice, PSODesc, bIsDeviceInternal}, - m_NumShaders{0} - { - const auto& SrcLayout = PSODesc.ResourceLayout; - size_t StringPoolSize = 0; - if (SrcLayout.Variables != nullptr) - { - for (Uint32 i = 0; i < SrcLayout.NumVariables; ++i) - StringPoolSize += strlen(SrcLayout.Variables[i].Name) + 1; - } - - if (SrcLayout.StaticSamplers != nullptr) - { - for (Uint32 i = 0; i < SrcLayout.NumStaticSamplers; ++i) - StringPoolSize += strlen(SrcLayout.StaticSamplers[i].SamplerOrTextureName) + 1; - } - - if (!PSODesc.IsComputePipeline) - { - const auto& InputLayout = PSODesc.GraphicsPipeline.InputLayout; - for (Uint32 i = 0; i < InputLayout.NumElements; ++i) - StringPoolSize += strlen(InputLayout.LayoutElements[i].HLSLSemantic) + 1; - } - else - { - DEV_CHECK_ERR(PSODesc.GraphicsPipeline.InputLayout.NumElements == 0, "Compute pipelines must not have input layout elements"); - } - - m_StringPool.Reserve(StringPoolSize, GetRawAllocator()); - - auto& DstLayout = this->m_Desc.ResourceLayout; - if (SrcLayout.Variables != nullptr) - { - ShaderResourceVariableDesc* Variables = - ALLOCATE(GetRawAllocator(), "Memory for ShaderResourceVariableDesc array", ShaderResourceVariableDesc, SrcLayout.NumVariables); - DstLayout.Variables = Variables; - for (Uint32 i = 0; i < SrcLayout.NumVariables; ++i) - { - VERIFY(SrcLayout.Variables[i].Name != nullptr, "Variable name can't be null"); - Variables[i] = SrcLayout.Variables[i]; - Variables[i].Name = m_StringPool.CopyString(SrcLayout.Variables[i].Name); - } - } - - if (SrcLayout.StaticSamplers != nullptr) - { - StaticSamplerDesc* StaticSamplers = - ALLOCATE(GetRawAllocator(), "Memory for StaticSamplerDesc array", StaticSamplerDesc, SrcLayout.NumStaticSamplers); - DstLayout.StaticSamplers = StaticSamplers; - for (Uint32 i = 0; i < SrcLayout.NumStaticSamplers; ++i) - { - VERIFY(SrcLayout.StaticSamplers[i].SamplerOrTextureName != nullptr, "Static sampler or texture name can't be null"); -#ifdef DEVELOPMENT - const auto& BorderColor = SrcLayout.StaticSamplers[i].Desc.BorderColor; - if (!((BorderColor[0] == 0 && BorderColor[1] == 0 && BorderColor[2] == 0 && BorderColor[3] == 0) || - (BorderColor[0] == 0 && BorderColor[1] == 0 && BorderColor[2] == 0 && BorderColor[3] == 1) || - (BorderColor[0] == 1 && BorderColor[1] == 1 && BorderColor[2] == 1 && BorderColor[3] == 1))) - { - LOG_WARNING_MESSAGE("Static sampler for variable \"", SrcLayout.StaticSamplers[i].SamplerOrTextureName, "\" specifies border color (", - BorderColor[0], ", ", BorderColor[1], ", ", BorderColor[2], ", ", BorderColor[3], - "). D3D12 static samplers only allow transparent black (0,0,0,0), opaque black (0,0,0,1) or opaque white (1,1,1,1) as border colors"); - } -#endif - - StaticSamplers[i] = SrcLayout.StaticSamplers[i]; - StaticSamplers[i].SamplerOrTextureName = m_StringPool.CopyString(SrcLayout.StaticSamplers[i].SamplerOrTextureName); - } - } - - - if (this->m_Desc.IsComputePipeline) - { - const auto& ComputePipeline = PSODesc.ComputePipeline; - if (ComputePipeline.pCS == nullptr) - { - LOG_ERROR_AND_THROW("Compute shader is not provided"); - } - -#define VALIDATE_SHADER_TYPE(Shader, ExpectedType, ShaderName) \ - if (Shader && Shader->GetDesc().ShaderType != ExpectedType) \ - { \ - LOG_ERROR_AND_THROW(GetShaderTypeLiteralName(Shader->GetDesc().ShaderType), " is not a valid type for ", ShaderName, " shader"); \ - } - - VALIDATE_SHADER_TYPE(ComputePipeline.pCS, SHADER_TYPE_COMPUTE, "compute") - - m_pCS = ComputePipeline.pCS; - m_ppShaders[0] = ComputePipeline.pCS; - m_NumShaders = 1; - } - else - { - const auto& GraphicsPipeline = PSODesc.GraphicsPipeline; - - VALIDATE_SHADER_TYPE(GraphicsPipeline.pVS, SHADER_TYPE_VERTEX, "vertex") - VALIDATE_SHADER_TYPE(GraphicsPipeline.pPS, SHADER_TYPE_PIXEL, "pixel") - VALIDATE_SHADER_TYPE(GraphicsPipeline.pGS, SHADER_TYPE_GEOMETRY, "geometry") - VALIDATE_SHADER_TYPE(GraphicsPipeline.pHS, SHADER_TYPE_HULL, "hull") - VALIDATE_SHADER_TYPE(GraphicsPipeline.pDS, SHADER_TYPE_DOMAIN, "domain") -#undef VALIDATE_SHADER_TYPE - - m_pVS = GraphicsPipeline.pVS; - m_pPS = GraphicsPipeline.pPS; - m_pGS = GraphicsPipeline.pGS; - m_pDS = GraphicsPipeline.pDS; - m_pHS = GraphicsPipeline.pHS; - - if (GraphicsPipeline.pVS) m_ppShaders[m_NumShaders++] = GraphicsPipeline.pVS; - if (GraphicsPipeline.pPS) m_ppShaders[m_NumShaders++] = GraphicsPipeline.pPS; - if (GraphicsPipeline.pGS) m_ppShaders[m_NumShaders++] = GraphicsPipeline.pGS; - if (GraphicsPipeline.pHS) m_ppShaders[m_NumShaders++] = GraphicsPipeline.pHS; - if (GraphicsPipeline.pDS) m_ppShaders[m_NumShaders++] = GraphicsPipeline.pDS; - - DEV_CHECK_ERR(m_NumShaders > 0, "There must be at least one shader in the Pipeline State"); - - for (Uint32 rt = GraphicsPipeline.NumRenderTargets; rt < _countof(GraphicsPipeline.RTVFormats); ++rt) - { - auto RTVFmt = GraphicsPipeline.RTVFormats[rt]; - if (RTVFmt != TEX_FORMAT_UNKNOWN) - { - LOG_ERROR_MESSAGE("Render target format (", GetTextureFormatAttribs(RTVFmt).Name, ") of unused slot ", rt, - " must be set to TEX_FORMAT_UNKNOWN"); - } - } - - const auto& InputLayout = PSODesc.GraphicsPipeline.InputLayout; - LayoutElement* pLayoutElements = nullptr; - if (InputLayout.NumElements > 0) - { - pLayoutElements = ALLOCATE(GetRawAllocator(), "Raw memory for input layout elements", LayoutElement, InputLayout.NumElements); - } - this->m_Desc.GraphicsPipeline.InputLayout.LayoutElements = pLayoutElements; - for (size_t Elem = 0; Elem < InputLayout.NumElements; ++Elem) - { - pLayoutElements[Elem] = InputLayout.LayoutElements[Elem]; - pLayoutElements[Elem].HLSLSemantic = m_StringPool.CopyString(InputLayout.LayoutElements[Elem].HLSLSemantic); - } - - - // Correct description and compute offsets and tight strides - std::array Strides, TightStrides = {}; - // Set all strides to an invalid value because an application may want to use 0 stride - for (auto& Stride : Strides) - Stride = LAYOUT_ELEMENT_AUTO_STRIDE; - - for (Uint32 i = 0; i < InputLayout.NumElements; ++i) - { - auto& LayoutElem = pLayoutElements[i]; - - if (LayoutElem.ValueType == VT_FLOAT32 || LayoutElem.ValueType == VT_FLOAT16) - LayoutElem.IsNormalized = false; // Floating point values cannot be normalized - - auto BuffSlot = LayoutElem.BufferSlot; - if (BuffSlot >= Strides.size()) - { - UNEXPECTED("Buffer slot (", BuffSlot, ") exceeds maximum allowed value (", Strides.size() - 1, ")"); - continue; - } - m_BufferSlotsUsed = std::max(m_BufferSlotsUsed, BuffSlot + 1); - - auto& CurrAutoStride = TightStrides[BuffSlot]; - // If offset is not explicitly specified, use current auto stride value - if (LayoutElem.RelativeOffset == LAYOUT_ELEMENT_AUTO_OFFSET) - { - LayoutElem.RelativeOffset = CurrAutoStride; - } - - // If stride is explicitly specified, use it for the current buffer slot - if (LayoutElem.Stride != LAYOUT_ELEMENT_AUTO_STRIDE) - { - // Verify that the value is consistent with the previously specified stride, if any - if (Strides[BuffSlot] != LAYOUT_ELEMENT_AUTO_STRIDE && Strides[BuffSlot] != LayoutElem.Stride) - { - LOG_ERROR_MESSAGE("Inconsistent strides are specified for buffer slot ", BuffSlot, - ". Input element at index ", LayoutElem.InputIndex, " explicitly specifies stride ", - LayoutElem.Stride, ", while current value is ", Strides[BuffSlot], - ". Specify consistent strides or use LAYOUT_ELEMENT_AUTO_STRIDE to allow " - "the engine compute strides automatically."); - } - Strides[BuffSlot] = LayoutElem.Stride; - } - - CurrAutoStride = std::max(CurrAutoStride, LayoutElem.RelativeOffset + LayoutElem.NumComponents * GetValueSize(LayoutElem.ValueType)); - } - - for (Uint32 i = 0; i < InputLayout.NumElements; ++i) - { - auto& LayoutElem = pLayoutElements[i]; - - auto BuffSlot = LayoutElem.BufferSlot; - // If no input elements explicitly specified stride for this buffer slot, use automatic stride - if (Strides[BuffSlot] == LAYOUT_ELEMENT_AUTO_STRIDE) - { - Strides[BuffSlot] = TightStrides[BuffSlot]; - } - else - { - if (Strides[BuffSlot] < TightStrides[BuffSlot]) - { - LOG_ERROR_MESSAGE("Stride ", Strides[BuffSlot], " explicitly specified for slot ", BuffSlot, - " is smaller than the minimum stride ", TightStrides[BuffSlot], - " required to accomodate all input elements."); - } - } - if (LayoutElem.Stride == LAYOUT_ELEMENT_AUTO_STRIDE) - LayoutElem.Stride = Strides[BuffSlot]; - } - - if (m_BufferSlotsUsed > 0) - { - m_pStrides = ALLOCATE(GetRawAllocator(), "Raw memory for buffer strides", Uint32, m_BufferSlotsUsed); - - // Set strides for all unused slots to 0 - for (Uint32 i = 0; i < m_BufferSlotsUsed; ++i) - { - auto Stride = Strides[i]; - m_pStrides[i] = Stride != LAYOUT_ELEMENT_AUTO_STRIDE ? Stride : 0; - } - } - } - - VERIFY_EXPR(m_StringPool.GetRemainingSize() == 0); - - Uint64 DeviceQueuesMask = pDevice->GetCommandQueueMask(); - DEV_CHECK_ERR((this->m_Desc.CommandQueueMask & DeviceQueuesMask) != 0, - "No bits in the command queue mask (0x", std::hex, this->m_Desc.CommandQueueMask, - ") correspond to one of ", pDevice->GetCommandQueueCount(), " available device command queues"); - this->m_Desc.CommandQueueMask &= DeviceQueuesMask; - } - - ~PipelineStateBase() - { - /* - /// \note Destructor cannot directly remove the object from the registry as this may cause a - /// deadlock at the point where StateObjectsRegistry::Find() locks the weak pointer: if we - /// are in dtor, the object is locked by Diligent::RefCountedObject::Release() and - /// StateObjectsRegistry::Find() will wait for that lock to be released. - /// A the same time this thread will be waiting for the other thread to unlock the registry.\n - /// Thus destructor only notifies the registry that there is a deleted object. - /// The reference to the object will be removed later. - auto &PipelineStateRegistry = static_cast(this->GetDevice())->GetBSRegistry(); - auto &RasterizerStateRegistry = static_cast(this->GetDevice())->GetRSRegistry(); - auto &DSSRegistry = static_cast(this->GetDevice())->GetDSSRegistry(); - // StateObjectsRegistry::ReportDeletedObject() does not lock the registry, but only - // atomically increments the outstanding deleted objects counter. - PipelineStateRegistry.ReportDeletedObject(); - RasterizerStateRegistry.ReportDeletedObject(); - DSSRegistry.ReportDeletedObject(); - */ - - auto& RawAllocator = GetRawAllocator(); - if (this->m_Desc.ResourceLayout.Variables != nullptr) - RawAllocator.Free(const_cast(this->m_Desc.ResourceLayout.Variables)); - if (this->m_Desc.ResourceLayout.StaticSamplers != nullptr) - RawAllocator.Free(const_cast(this->m_Desc.ResourceLayout.StaticSamplers)); - if (this->m_Desc.GraphicsPipeline.InputLayout.LayoutElements != nullptr) - RawAllocator.Free(const_cast(this->m_Desc.GraphicsPipeline.InputLayout.LayoutElements)); - if (m_pStrides != nullptr) - RawAllocator.Free(m_pStrides); - } - - IMPLEMENT_QUERY_INTERFACE_IN_PLACE(IID_PipelineState, TDeviceObjectBase) - - Uint32 GetBufferStride(Uint32 BufferSlot) const - { - return BufferSlot < m_BufferSlotsUsed ? m_pStrides[BufferSlot] : 0; - } - - Uint32 GetNumBufferSlotsUsed() const - { - return m_BufferSlotsUsed; - } - - IShader* GetVS() { return m_pVS; } - IShader* GetPS() { return m_pPS; } - IShader* GetGS() { return m_pGS; } - IShader* GetDS() { return m_pDS; } - IShader* GetHS() { return m_pHS; } - IShader* GetCS() { return m_pCS; } - - IShader* const* GetShaders() const { return m_ppShaders; } - Uint32 GetNumShaders() const { return m_NumShaders; } - - template - ShaderType* GetShader(Uint32 ShaderInd) - { - VERIFY_EXPR(ShaderInd < m_NumShaders); - return ValidatedCast(m_ppShaders[ShaderInd]); - } - template - ShaderType* GetShader(Uint32 ShaderInd) const - { - VERIFY_EXPR(ShaderInd < m_NumShaders); - return ValidatedCast(m_ppShaders[ShaderInd]); - } - - // This function only compares shader resource layout hashes, so - // it can potentially give false negatives - bool IsIncompatibleWith(const IPipelineState* pPSO) const - { - return m_ShaderResourceLayoutHash != ValidatedCast(pPSO)->m_ShaderResourceLayoutHash; - } - -protected: - Uint32 m_BufferSlotsUsed = 0; - Uint32 m_NumShaders = 0; ///< Number of shaders that this PSO uses - Uint32* m_pStrides = nullptr; - - StringPool m_StringPool; - - RefCntAutoPtr m_pVS; ///< Strong reference to the vertex shader - RefCntAutoPtr m_pPS; ///< Strong reference to the pixel shader - RefCntAutoPtr m_pGS; ///< Strong reference to the geometry shader - RefCntAutoPtr m_pDS; ///< Strong reference to the domain shader - RefCntAutoPtr m_pHS; ///< Strong reference to the hull shader - RefCntAutoPtr m_pCS; ///< Strong reference to the compute shader - - IShader* m_ppShaders[5] = {}; ///< Array of pointers to the shaders used by this PSO - size_t m_ShaderResourceLayoutHash = 0; ///< Hash computed from the shader resource layout -}; - -} // namespace Diligent diff --git a/Graphics/GraphicsEngine/include/PipelineStateBase.hpp b/Graphics/GraphicsEngine/include/PipelineStateBase.hpp new file mode 100644 index 00000000..5dddbaea --- /dev/null +++ b/Graphics/GraphicsEngine/include/PipelineStateBase.hpp @@ -0,0 +1,390 @@ +/* + * Copyright 2019-2020 Diligent Graphics LLC + * Copyright 2015-2019 Egor Yusov + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + * + * In no event and under no legal theory, whether in tort (including negligence), + * contract, or otherwise, unless required by applicable law (such as deliberate + * and grossly negligent acts) or agreed to in writing, shall any Contributor be + * liable for any damages, including any direct, indirect, special, incidental, + * or consequential damages of any character arising as a result of this License or + * out of the use or inability to use the software (including but not limited to damages + * for loss of goodwill, work stoppage, computer failure or malfunction, or any and + * all other commercial damages or losses), even if such Contributor has been advised + * of the possibility of such damages. + */ + +#pragma once + +/// \file +/// Implementation of the Diligent::PipelineStateBase template class + +#include +#include + +#include "PipelineState.h" +#include "DeviceObjectBase.hpp" +#include "STDAllocator.h" +#include "EngineMemory.h" +#include "GraphicsAccessories.hpp" +#include "StringPool.h" + +namespace Diligent +{ + +/// Template class implementing base functionality for a pipeline state object. + +/// \tparam BaseInterface - base interface that this class will inheret +/// (Diligent::IPipelineStateD3D11, Diligent::IPipelineStateD3D12, +/// Diligent::IPipelineStateGL or Diligent::IPipelineStateVk). +/// \tparam RenderDeviceImplType - type of the render device implementation +/// (Diligent::RenderDeviceD3D11Impl, Diligent::RenderDeviceD3D12Impl, +/// Diligent::RenderDeviceGLImpl, or Diligent::RenderDeviceVkImpl) +template +class PipelineStateBase : public DeviceObjectBase +{ +public: + using TDeviceObjectBase = DeviceObjectBase; + + /// \param pRefCounters - reference counters object that controls the lifetime of this PSO + /// \param pDevice - pointer to the device. + /// \param PSODesc - pipeline state description. + /// \param bIsDeviceInternal - flag indicating if the blend state is an internal device object and + /// must not keep a strong reference to the device. + PipelineStateBase(IReferenceCounters* pRefCounters, + RenderDeviceImplType* pDevice, + const PipelineStateDesc& PSODesc, + bool bIsDeviceInternal = false) : + TDeviceObjectBase{pRefCounters, pDevice, PSODesc, bIsDeviceInternal}, + m_NumShaders{0} + { + const auto& SrcLayout = PSODesc.ResourceLayout; + size_t StringPoolSize = 0; + if (SrcLayout.Variables != nullptr) + { + for (Uint32 i = 0; i < SrcLayout.NumVariables; ++i) + StringPoolSize += strlen(SrcLayout.Variables[i].Name) + 1; + } + + if (SrcLayout.StaticSamplers != nullptr) + { + for (Uint32 i = 0; i < SrcLayout.NumStaticSamplers; ++i) + StringPoolSize += strlen(SrcLayout.StaticSamplers[i].SamplerOrTextureName) + 1; + } + + if (!PSODesc.IsComputePipeline) + { + const auto& InputLayout = PSODesc.GraphicsPipeline.InputLayout; + for (Uint32 i = 0; i < InputLayout.NumElements; ++i) + StringPoolSize += strlen(InputLayout.LayoutElements[i].HLSLSemantic) + 1; + } + else + { + DEV_CHECK_ERR(PSODesc.GraphicsPipeline.InputLayout.NumElements == 0, "Compute pipelines must not have input layout elements"); + } + + m_StringPool.Reserve(StringPoolSize, GetRawAllocator()); + + auto& DstLayout = this->m_Desc.ResourceLayout; + if (SrcLayout.Variables != nullptr) + { + ShaderResourceVariableDesc* Variables = + ALLOCATE(GetRawAllocator(), "Memory for ShaderResourceVariableDesc array", ShaderResourceVariableDesc, SrcLayout.NumVariables); + DstLayout.Variables = Variables; + for (Uint32 i = 0; i < SrcLayout.NumVariables; ++i) + { + VERIFY(SrcLayout.Variables[i].Name != nullptr, "Variable name can't be null"); + Variables[i] = SrcLayout.Variables[i]; + Variables[i].Name = m_StringPool.CopyString(SrcLayout.Variables[i].Name); + } + } + + if (SrcLayout.StaticSamplers != nullptr) + { + StaticSamplerDesc* StaticSamplers = + ALLOCATE(GetRawAllocator(), "Memory for StaticSamplerDesc array", StaticSamplerDesc, SrcLayout.NumStaticSamplers); + DstLayout.StaticSamplers = StaticSamplers; + for (Uint32 i = 0; i < SrcLayout.NumStaticSamplers; ++i) + { + VERIFY(SrcLayout.StaticSamplers[i].SamplerOrTextureName != nullptr, "Static sampler or texture name can't be null"); +#ifdef DEVELOPMENT + const auto& BorderColor = SrcLayout.StaticSamplers[i].Desc.BorderColor; + if (!((BorderColor[0] == 0 && BorderColor[1] == 0 && BorderColor[2] == 0 && BorderColor[3] == 0) || + (BorderColor[0] == 0 && BorderColor[1] == 0 && BorderColor[2] == 0 && BorderColor[3] == 1) || + (BorderColor[0] == 1 && BorderColor[1] == 1 && BorderColor[2] == 1 && BorderColor[3] == 1))) + { + LOG_WARNING_MESSAGE("Static sampler for variable \"", SrcLayout.StaticSamplers[i].SamplerOrTextureName, "\" specifies border color (", + BorderColor[0], ", ", BorderColor[1], ", ", BorderColor[2], ", ", BorderColor[3], + "). D3D12 static samplers only allow transparent black (0,0,0,0), opaque black (0,0,0,1) or opaque white (1,1,1,1) as border colors"); + } +#endif + + StaticSamplers[i] = SrcLayout.StaticSamplers[i]; + StaticSamplers[i].SamplerOrTextureName = m_StringPool.CopyString(SrcLayout.StaticSamplers[i].SamplerOrTextureName); + } + } + + + if (this->m_Desc.IsComputePipeline) + { + const auto& ComputePipeline = PSODesc.ComputePipeline; + if (ComputePipeline.pCS == nullptr) + { + LOG_ERROR_AND_THROW("Compute shader is not provided"); + } + +#define VALIDATE_SHADER_TYPE(Shader, ExpectedType, ShaderName) \ + if (Shader && Shader->GetDesc().ShaderType != ExpectedType) \ + { \ + LOG_ERROR_AND_THROW(GetShaderTypeLiteralName(Shader->GetDesc().ShaderType), " is not a valid type for ", ShaderName, " shader"); \ + } + + VALIDATE_SHADER_TYPE(ComputePipeline.pCS, SHADER_TYPE_COMPUTE, "compute") + + m_pCS = ComputePipeline.pCS; + m_ppShaders[0] = ComputePipeline.pCS; + m_NumShaders = 1; + } + else + { + const auto& GraphicsPipeline = PSODesc.GraphicsPipeline; + + VALIDATE_SHADER_TYPE(GraphicsPipeline.pVS, SHADER_TYPE_VERTEX, "vertex") + VALIDATE_SHADER_TYPE(GraphicsPipeline.pPS, SHADER_TYPE_PIXEL, "pixel") + VALIDATE_SHADER_TYPE(GraphicsPipeline.pGS, SHADER_TYPE_GEOMETRY, "geometry") + VALIDATE_SHADER_TYPE(GraphicsPipeline.pHS, SHADER_TYPE_HULL, "hull") + VALIDATE_SHADER_TYPE(GraphicsPipeline.pDS, SHADER_TYPE_DOMAIN, "domain") +#undef VALIDATE_SHADER_TYPE + + m_pVS = GraphicsPipeline.pVS; + m_pPS = GraphicsPipeline.pPS; + m_pGS = GraphicsPipeline.pGS; + m_pDS = GraphicsPipeline.pDS; + m_pHS = GraphicsPipeline.pHS; + + if (GraphicsPipeline.pVS) m_ppShaders[m_NumShaders++] = GraphicsPipeline.pVS; + if (GraphicsPipeline.pPS) m_ppShaders[m_NumShaders++] = GraphicsPipeline.pPS; + if (GraphicsPipeline.pGS) m_ppShaders[m_NumShaders++] = GraphicsPipeline.pGS; + if (GraphicsPipeline.pHS) m_ppShaders[m_NumShaders++] = GraphicsPipeline.pHS; + if (GraphicsPipeline.pDS) m_ppShaders[m_NumShaders++] = GraphicsPipeline.pDS; + + DEV_CHECK_ERR(m_NumShaders > 0, "There must be at least one shader in the Pipeline State"); + + for (Uint32 rt = GraphicsPipeline.NumRenderTargets; rt < _countof(GraphicsPipeline.RTVFormats); ++rt) + { + auto RTVFmt = GraphicsPipeline.RTVFormats[rt]; + if (RTVFmt != TEX_FORMAT_UNKNOWN) + { + LOG_ERROR_MESSAGE("Render target format (", GetTextureFormatAttribs(RTVFmt).Name, ") of unused slot ", rt, + " must be set to TEX_FORMAT_UNKNOWN"); + } + } + + const auto& InputLayout = PSODesc.GraphicsPipeline.InputLayout; + LayoutElement* pLayoutElements = nullptr; + if (InputLayout.NumElements > 0) + { + pLayoutElements = ALLOCATE(GetRawAllocator(), "Raw memory for input layout elements", LayoutElement, InputLayout.NumElements); + } + this->m_Desc.GraphicsPipeline.InputLayout.LayoutElements = pLayoutElements; + for (size_t Elem = 0; Elem < InputLayout.NumElements; ++Elem) + { + pLayoutElements[Elem] = InputLayout.LayoutElements[Elem]; + pLayoutElements[Elem].HLSLSemantic = m_StringPool.CopyString(InputLayout.LayoutElements[Elem].HLSLSemantic); + } + + + // Correct description and compute offsets and tight strides + std::array Strides, TightStrides = {}; + // Set all strides to an invalid value because an application may want to use 0 stride + for (auto& Stride : Strides) + Stride = LAYOUT_ELEMENT_AUTO_STRIDE; + + for (Uint32 i = 0; i < InputLayout.NumElements; ++i) + { + auto& LayoutElem = pLayoutElements[i]; + + if (LayoutElem.ValueType == VT_FLOAT32 || LayoutElem.ValueType == VT_FLOAT16) + LayoutElem.IsNormalized = false; // Floating point values cannot be normalized + + auto BuffSlot = LayoutElem.BufferSlot; + if (BuffSlot >= Strides.size()) + { + UNEXPECTED("Buffer slot (", BuffSlot, ") exceeds maximum allowed value (", Strides.size() - 1, ")"); + continue; + } + m_BufferSlotsUsed = std::max(m_BufferSlotsUsed, BuffSlot + 1); + + auto& CurrAutoStride = TightStrides[BuffSlot]; + // If offset is not explicitly specified, use current auto stride value + if (LayoutElem.RelativeOffset == LAYOUT_ELEMENT_AUTO_OFFSET) + { + LayoutElem.RelativeOffset = CurrAutoStride; + } + + // If stride is explicitly specified, use it for the current buffer slot + if (LayoutElem.Stride != LAYOUT_ELEMENT_AUTO_STRIDE) + { + // Verify that the value is consistent with the previously specified stride, if any + if (Strides[BuffSlot] != LAYOUT_ELEMENT_AUTO_STRIDE && Strides[BuffSlot] != LayoutElem.Stride) + { + LOG_ERROR_MESSAGE("Inconsistent strides are specified for buffer slot ", BuffSlot, + ". Input element at index ", LayoutElem.InputIndex, " explicitly specifies stride ", + LayoutElem.Stride, ", while current value is ", Strides[BuffSlot], + ". Specify consistent strides or use LAYOUT_ELEMENT_AUTO_STRIDE to allow " + "the engine compute strides automatically."); + } + Strides[BuffSlot] = LayoutElem.Stride; + } + + CurrAutoStride = std::max(CurrAutoStride, LayoutElem.RelativeOffset + LayoutElem.NumComponents * GetValueSize(LayoutElem.ValueType)); + } + + for (Uint32 i = 0; i < InputLayout.NumElements; ++i) + { + auto& LayoutElem = pLayoutElements[i]; + + auto BuffSlot = LayoutElem.BufferSlot; + // If no input elements explicitly specified stride for this buffer slot, use automatic stride + if (Strides[BuffSlot] == LAYOUT_ELEMENT_AUTO_STRIDE) + { + Strides[BuffSlot] = TightStrides[BuffSlot]; + } + else + { + if (Strides[BuffSlot] < TightStrides[BuffSlot]) + { + LOG_ERROR_MESSAGE("Stride ", Strides[BuffSlot], " explicitly specified for slot ", BuffSlot, + " is smaller than the minimum stride ", TightStrides[BuffSlot], + " required to accomodate all input elements."); + } + } + if (LayoutElem.Stride == LAYOUT_ELEMENT_AUTO_STRIDE) + LayoutElem.Stride = Strides[BuffSlot]; + } + + if (m_BufferSlotsUsed > 0) + { + m_pStrides = ALLOCATE(GetRawAllocator(), "Raw memory for buffer strides", Uint32, m_BufferSlotsUsed); + + // Set strides for all unused slots to 0 + for (Uint32 i = 0; i < m_BufferSlotsUsed; ++i) + { + auto Stride = Strides[i]; + m_pStrides[i] = Stride != LAYOUT_ELEMENT_AUTO_STRIDE ? Stride : 0; + } + } + } + + VERIFY_EXPR(m_StringPool.GetRemainingSize() == 0); + + Uint64 DeviceQueuesMask = pDevice->GetCommandQueueMask(); + DEV_CHECK_ERR((this->m_Desc.CommandQueueMask & DeviceQueuesMask) != 0, + "No bits in the command queue mask (0x", std::hex, this->m_Desc.CommandQueueMask, + ") correspond to one of ", pDevice->GetCommandQueueCount(), " available device command queues"); + this->m_Desc.CommandQueueMask &= DeviceQueuesMask; + } + + ~PipelineStateBase() + { + /* + /// \note Destructor cannot directly remove the object from the registry as this may cause a + /// deadlock at the point where StateObjectsRegistry::Find() locks the weak pointer: if we + /// are in dtor, the object is locked by Diligent::RefCountedObject::Release() and + /// StateObjectsRegistry::Find() will wait for that lock to be released. + /// A the same time this thread will be waiting for the other thread to unlock the registry.\n + /// Thus destructor only notifies the registry that there is a deleted object. + /// The reference to the object will be removed later. + auto &PipelineStateRegistry = static_cast(this->GetDevice())->GetBSRegistry(); + auto &RasterizerStateRegistry = static_cast(this->GetDevice())->GetRSRegistry(); + auto &DSSRegistry = static_cast(this->GetDevice())->GetDSSRegistry(); + // StateObjectsRegistry::ReportDeletedObject() does not lock the registry, but only + // atomically increments the outstanding deleted objects counter. + PipelineStateRegistry.ReportDeletedObject(); + RasterizerStateRegistry.ReportDeletedObject(); + DSSRegistry.ReportDeletedObject(); + */ + + auto& RawAllocator = GetRawAllocator(); + if (this->m_Desc.ResourceLayout.Variables != nullptr) + RawAllocator.Free(const_cast(this->m_Desc.ResourceLayout.Variables)); + if (this->m_Desc.ResourceLayout.StaticSamplers != nullptr) + RawAllocator.Free(const_cast(this->m_Desc.ResourceLayout.StaticSamplers)); + if (this->m_Desc.GraphicsPipeline.InputLayout.LayoutElements != nullptr) + RawAllocator.Free(const_cast(this->m_Desc.GraphicsPipeline.InputLayout.LayoutElements)); + if (m_pStrides != nullptr) + RawAllocator.Free(m_pStrides); + } + + IMPLEMENT_QUERY_INTERFACE_IN_PLACE(IID_PipelineState, TDeviceObjectBase) + + Uint32 GetBufferStride(Uint32 BufferSlot) const + { + return BufferSlot < m_BufferSlotsUsed ? m_pStrides[BufferSlot] : 0; + } + + Uint32 GetNumBufferSlotsUsed() const + { + return m_BufferSlotsUsed; + } + + IShader* GetVS() { return m_pVS; } + IShader* GetPS() { return m_pPS; } + IShader* GetGS() { return m_pGS; } + IShader* GetDS() { return m_pDS; } + IShader* GetHS() { return m_pHS; } + IShader* GetCS() { return m_pCS; } + + IShader* const* GetShaders() const { return m_ppShaders; } + Uint32 GetNumShaders() const { return m_NumShaders; } + + template + ShaderType* GetShader(Uint32 ShaderInd) + { + VERIFY_EXPR(ShaderInd < m_NumShaders); + return ValidatedCast(m_ppShaders[ShaderInd]); + } + template + ShaderType* GetShader(Uint32 ShaderInd) const + { + VERIFY_EXPR(ShaderInd < m_NumShaders); + return ValidatedCast(m_ppShaders[ShaderInd]); + } + + // This function only compares shader resource layout hashes, so + // it can potentially give false negatives + bool IsIncompatibleWith(const IPipelineState* pPSO) const + { + return m_ShaderResourceLayoutHash != ValidatedCast(pPSO)->m_ShaderResourceLayoutHash; + } + +protected: + Uint32 m_BufferSlotsUsed = 0; + Uint32 m_NumShaders = 0; ///< Number of shaders that this PSO uses + Uint32* m_pStrides = nullptr; + + StringPool m_StringPool; + + RefCntAutoPtr m_pVS; ///< Strong reference to the vertex shader + RefCntAutoPtr m_pPS; ///< Strong reference to the pixel shader + RefCntAutoPtr m_pGS; ///< Strong reference to the geometry shader + RefCntAutoPtr m_pDS; ///< Strong reference to the domain shader + RefCntAutoPtr m_pHS; ///< Strong reference to the hull shader + RefCntAutoPtr m_pCS; ///< Strong reference to the compute shader + + IShader* m_ppShaders[5] = {}; ///< Array of pointers to the shaders used by this PSO + size_t m_ShaderResourceLayoutHash = 0; ///< Hash computed from the shader resource layout +}; + +} // namespace Diligent diff --git a/Graphics/GraphicsEngine/include/QueryBase.h b/Graphics/GraphicsEngine/include/QueryBase.h deleted file mode 100644 index daa0d30e..00000000 --- a/Graphics/GraphicsEngine/include/QueryBase.h +++ /dev/null @@ -1,243 +0,0 @@ -/* - * Copyright 2019-2020 Diligent Graphics LLC - * Copyright 2015-2019 Egor Yusov - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - * - * In no event and under no legal theory, whether in tort (including negligence), - * contract, or otherwise, unless required by applicable law (such as deliberate - * and grossly negligent acts) or agreed to in writing, shall any Contributor be - * liable for any damages, including any direct, indirect, special, incidental, - * or consequential damages of any character arising as a result of this License or - * out of the use or inability to use the software (including but not limited to damages - * for loss of goodwill, work stoppage, computer failure or malfunction, or any and - * all other commercial damages or losses), even if such Contributor has been advised - * of the possibility of such damages. - */ - -#pragma once - -/// \file -/// Implementation of Diligent::QueryBase template class - -#include "Query.h" -#include "DeviceObjectBase.h" -#include "GraphicsTypes.h" -#include "RefCntAutoPtr.h" - -namespace Diligent -{ - -class IDeviceContext; - -/// Template class implementing base functionality for a Query object - -/// \tparam BaseInterface - base interface that this class will inheret -/// (Diligent::IQueryD3D11, Diligent::IQueryD3D12, -/// Diligent::IQueryGL or Diligent::IQueryVk). -/// \tparam RenderDeviceImplType - type of the render device implementation -template -class QueryBase : public DeviceObjectBase -{ -public: - enum class QueryState - { - Inactive, - Querying, - Complete - }; - - using TDeviceObjectBase = DeviceObjectBase; - - /// \param pRefCounters - reference counters object that controls the lifetime of this command list. - /// \param pDevice - pointer to the device. - /// \param Desc - Query description - /// \param bIsDeviceInternal - flag indicating if the Query is an internal device object and - /// must not keep a strong reference to the device. - QueryBase(IReferenceCounters* pRefCounters, - RenderDeviceImplType* pDevice, - const QueryDesc& Desc, - bool bIsDeviceInternal = false) : - TDeviceObjectBase{pRefCounters, pDevice, Desc, bIsDeviceInternal} - { - const auto& deviceFeatures = pDevice->GetDeviceCaps().Features; - switch (Desc.Type) - { - case QUERY_TYPE_OCCLUSION: - if (!deviceFeatures.OcclusionQueries) - LOG_ERROR_AND_THROW("Occlusion queries are not supported by this device"); - break; - - case QUERY_TYPE_BINARY_OCCLUSION: - if (!deviceFeatures.BinaryOcclusionQueries) - LOG_ERROR_AND_THROW("Binary occlusion queries are not supported by this device"); - break; - - case QUERY_TYPE_TIMESTAMP: - if (!deviceFeatures.TimestampQueries) - LOG_ERROR_AND_THROW("Timestamp queries are not supported by this device"); - break; - - case QUERY_TYPE_PIPELINE_STATISTICS: - if (!deviceFeatures.PipelineStatisticsQueries) - LOG_ERROR_AND_THROW("Pipeline statistics queries are not supported by this device"); - break; - - default: - UNEXPECTED("Unexpected device type"); - } - } - - ~QueryBase() - { - if (m_State == QueryState::Querying) - { - LOG_ERROR_MESSAGE("Destroying query '", this->m_Desc.Name, - "' that is in querying state. End the query before releasing it."); - } - } - - IMPLEMENT_QUERY_INTERFACE_IN_PLACE(IID_Query, TDeviceObjectBase) - - virtual void Invalidate() override - { - m_State = QueryState::Inactive; - } - - bool OnBeginQuery(IDeviceContext* pContext) - { - if (this->m_Desc.Type == QUERY_TYPE_TIMESTAMP) - { - LOG_ERROR_MESSAGE("BeginQuery cannot be called on timestamp query '", this->m_Desc.Name, - "'. Call EndQuery to set the timestamp."); - return false; - } - - if (m_State == QueryState::Querying) - { - LOG_ERROR_MESSAGE("Attempting to begin query '", this->m_Desc.Name, - "' twice. A query must be ended before it can be begun again."); - return false; - } - - m_pContext = pContext; - m_State = QueryState::Querying; - return true; - } - - bool OnEndQuery(IDeviceContext* pContext) - { - if (this->m_Desc.Type != QUERY_TYPE_TIMESTAMP) - { - if (m_State != QueryState::Querying) - { - LOG_ERROR_MESSAGE("Attempting to end query '", this->m_Desc.Name, "' that has not been begun"); - return false; - } - } - - if (m_pContext == nullptr) - { - if (this->m_Desc.Type != QUERY_TYPE_TIMESTAMP) - { - LOG_ERROR_MESSAGE("Ending query '", this->m_Desc.Name, "' that has not been begun"); - return false; - } - - m_pContext = pContext; - } - else if (m_pContext != pContext) - { - LOG_ERROR_MESSAGE("Query '", this->m_Desc.Name, "' has been begun by another context"); - return false; - } - - m_State = QueryState::Complete; - return true; - } - - QueryState GetState() const - { - return m_State; - } - - bool CheckQueryDataPtr(void* pData, Uint32 DataSize) - { - if (m_State != QueryState::Complete) - { - LOG_ERROR_MESSAGE("Attempting to get data of query '", this->m_Desc.Name, "' that has not been ended"); - return false; - } - - if (pData != nullptr) - { - if (*reinterpret_cast(pData) != this->m_Desc.Type) - { - LOG_ERROR_MESSAGE("Incorrect query data structure type."); - return false; - } - - switch (this->m_Desc.Type) - { - case QUERY_TYPE_UNDEFINED: - UNEXPECTED("Undefined query type is unexpected"); - return false; - - case QUERY_TYPE_OCCLUSION: - if (DataSize != sizeof(QueryDataOcclusion)) - { - LOG_ERROR_MESSAGE("The size of query data (", DataSize, ") is incorrect: ", sizeof(QueryDataOcclusion), " (aka sizeof(QueryDataOcclusion)) is expected"); - return false; - } - break; - - case QUERY_TYPE_BINARY_OCCLUSION: - if (DataSize != sizeof(QueryDataBinaryOcclusion)) - { - LOG_ERROR_MESSAGE("The size of query data (", DataSize, ") is incorrect: ", sizeof(QueryDataBinaryOcclusion), " (aka sizeof(QueryDataBinaryOcclusion)) is expected"); - return false; - } - break; - - case QUERY_TYPE_TIMESTAMP: - if (DataSize != sizeof(QueryDataTimestamp)) - { - LOG_ERROR_MESSAGE("The size of query data (", DataSize, ") is incorrect: ", sizeof(QueryDataTimestamp), " (aka sizeof(QueryDataTimestamp)) is expected"); - return false; - } - break; - - case QUERY_TYPE_PIPELINE_STATISTICS: - if (DataSize != sizeof(QueryDataPipelineStatistics)) - { - LOG_ERROR_MESSAGE("The size of query data (", DataSize, ") is incorrect: ", sizeof(QueryDataPipelineStatistics), " (aka sizeof(QueryDataPipelineStatistics)) is expected"); - return false; - } - break; - - default: - UNEXPECTED("Unexpected query type"); - return false; - } - } - - return true; - } - -protected: - RefCntAutoPtr m_pContext; - - QueryState m_State = QueryState::Inactive; -}; - -} // namespace Diligent diff --git a/Graphics/GraphicsEngine/include/QueryBase.hpp b/Graphics/GraphicsEngine/include/QueryBase.hpp new file mode 100644 index 00000000..91e5f034 --- /dev/null +++ b/Graphics/GraphicsEngine/include/QueryBase.hpp @@ -0,0 +1,243 @@ +/* + * Copyright 2019-2020 Diligent Graphics LLC + * Copyright 2015-2019 Egor Yusov + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + * + * In no event and under no legal theory, whether in tort (including negligence), + * contract, or otherwise, unless required by applicable law (such as deliberate + * and grossly negligent acts) or agreed to in writing, shall any Contributor be + * liable for any damages, including any direct, indirect, special, incidental, + * or consequential damages of any character arising as a result of this License or + * out of the use or inability to use the software (including but not limited to damages + * for loss of goodwill, work stoppage, computer failure or malfunction, or any and + * all other commercial damages or losses), even if such Contributor has been advised + * of the possibility of such damages. + */ + +#pragma once + +/// \file +/// Implementation of Diligent::QueryBase template class + +#include "Query.h" +#include "DeviceObjectBase.hpp" +#include "GraphicsTypes.h" +#include "RefCntAutoPtr.h" + +namespace Diligent +{ + +class IDeviceContext; + +/// Template class implementing base functionality for a Query object + +/// \tparam BaseInterface - base interface that this class will inheret +/// (Diligent::IQueryD3D11, Diligent::IQueryD3D12, +/// Diligent::IQueryGL or Diligent::IQueryVk). +/// \tparam RenderDeviceImplType - type of the render device implementation +template +class QueryBase : public DeviceObjectBase +{ +public: + enum class QueryState + { + Inactive, + Querying, + Complete + }; + + using TDeviceObjectBase = DeviceObjectBase; + + /// \param pRefCounters - reference counters object that controls the lifetime of this command list. + /// \param pDevice - pointer to the device. + /// \param Desc - Query description + /// \param bIsDeviceInternal - flag indicating if the Query is an internal device object and + /// must not keep a strong reference to the device. + QueryBase(IReferenceCounters* pRefCounters, + RenderDeviceImplType* pDevice, + const QueryDesc& Desc, + bool bIsDeviceInternal = false) : + TDeviceObjectBase{pRefCounters, pDevice, Desc, bIsDeviceInternal} + { + const auto& deviceFeatures = pDevice->GetDeviceCaps().Features; + switch (Desc.Type) + { + case QUERY_TYPE_OCCLUSION: + if (!deviceFeatures.OcclusionQueries) + LOG_ERROR_AND_THROW("Occlusion queries are not supported by this device"); + break; + + case QUERY_TYPE_BINARY_OCCLUSION: + if (!deviceFeatures.BinaryOcclusionQueries) + LOG_ERROR_AND_THROW("Binary occlusion queries are not supported by this device"); + break; + + case QUERY_TYPE_TIMESTAMP: + if (!deviceFeatures.TimestampQueries) + LOG_ERROR_AND_THROW("Timestamp queries are not supported by this device"); + break; + + case QUERY_TYPE_PIPELINE_STATISTICS: + if (!deviceFeatures.PipelineStatisticsQueries) + LOG_ERROR_AND_THROW("Pipeline statistics queries are not supported by this device"); + break; + + default: + UNEXPECTED("Unexpected device type"); + } + } + + ~QueryBase() + { + if (m_State == QueryState::Querying) + { + LOG_ERROR_MESSAGE("Destroying query '", this->m_Desc.Name, + "' that is in querying state. End the query before releasing it."); + } + } + + IMPLEMENT_QUERY_INTERFACE_IN_PLACE(IID_Query, TDeviceObjectBase) + + virtual void Invalidate() override + { + m_State = QueryState::Inactive; + } + + bool OnBeginQuery(IDeviceContext* pContext) + { + if (this->m_Desc.Type == QUERY_TYPE_TIMESTAMP) + { + LOG_ERROR_MESSAGE("BeginQuery cannot be called on timestamp query '", this->m_Desc.Name, + "'. Call EndQuery to set the timestamp."); + return false; + } + + if (m_State == QueryState::Querying) + { + LOG_ERROR_MESSAGE("Attempting to begin query '", this->m_Desc.Name, + "' twice. A query must be ended before it can be begun again."); + return false; + } + + m_pContext = pContext; + m_State = QueryState::Querying; + return true; + } + + bool OnEndQuery(IDeviceContext* pContext) + { + if (this->m_Desc.Type != QUERY_TYPE_TIMESTAMP) + { + if (m_State != QueryState::Querying) + { + LOG_ERROR_MESSAGE("Attempting to end query '", this->m_Desc.Name, "' that has not been begun"); + return false; + } + } + + if (m_pContext == nullptr) + { + if (this->m_Desc.Type != QUERY_TYPE_TIMESTAMP) + { + LOG_ERROR_MESSAGE("Ending query '", this->m_Desc.Name, "' that has not been begun"); + return false; + } + + m_pContext = pContext; + } + else if (m_pContext != pContext) + { + LOG_ERROR_MESSAGE("Query '", this->m_Desc.Name, "' has been begun by another context"); + return false; + } + + m_State = QueryState::Complete; + return true; + } + + QueryState GetState() const + { + return m_State; + } + + bool CheckQueryDataPtr(void* pData, Uint32 DataSize) + { + if (m_State != QueryState::Complete) + { + LOG_ERROR_MESSAGE("Attempting to get data of query '", this->m_Desc.Name, "' that has not been ended"); + return false; + } + + if (pData != nullptr) + { + if (*reinterpret_cast(pData) != this->m_Desc.Type) + { + LOG_ERROR_MESSAGE("Incorrect query data structure type."); + return false; + } + + switch (this->m_Desc.Type) + { + case QUERY_TYPE_UNDEFINED: + UNEXPECTED("Undefined query type is unexpected"); + return false; + + case QUERY_TYPE_OCCLUSION: + if (DataSize != sizeof(QueryDataOcclusion)) + { + LOG_ERROR_MESSAGE("The size of query data (", DataSize, ") is incorrect: ", sizeof(QueryDataOcclusion), " (aka sizeof(QueryDataOcclusion)) is expected"); + return false; + } + break; + + case QUERY_TYPE_BINARY_OCCLUSION: + if (DataSize != sizeof(QueryDataBinaryOcclusion)) + { + LOG_ERROR_MESSAGE("The size of query data (", DataSize, ") is incorrect: ", sizeof(QueryDataBinaryOcclusion), " (aka sizeof(QueryDataBinaryOcclusion)) is expected"); + return false; + } + break; + + case QUERY_TYPE_TIMESTAMP: + if (DataSize != sizeof(QueryDataTimestamp)) + { + LOG_ERROR_MESSAGE("The size of query data (", DataSize, ") is incorrect: ", sizeof(QueryDataTimestamp), " (aka sizeof(QueryDataTimestamp)) is expected"); + return false; + } + break; + + case QUERY_TYPE_PIPELINE_STATISTICS: + if (DataSize != sizeof(QueryDataPipelineStatistics)) + { + LOG_ERROR_MESSAGE("The size of query data (", DataSize, ") is incorrect: ", sizeof(QueryDataPipelineStatistics), " (aka sizeof(QueryDataPipelineStatistics)) is expected"); + return false; + } + break; + + default: + UNEXPECTED("Unexpected query type"); + return false; + } + } + + return true; + } + +protected: + RefCntAutoPtr m_pContext; + + QueryState m_State = QueryState::Inactive; +}; + +} // namespace Diligent diff --git a/Graphics/GraphicsEngine/include/RenderDeviceBase.h b/Graphics/GraphicsEngine/include/RenderDeviceBase.h deleted file mode 100644 index 68ab63a6..00000000 --- a/Graphics/GraphicsEngine/include/RenderDeviceBase.h +++ /dev/null @@ -1,502 +0,0 @@ -/* - * Copyright 2019-2020 Diligent Graphics LLC - * Copyright 2015-2019 Egor Yusov - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - * - * In no event and under no legal theory, whether in tort (including negligence), - * contract, or otherwise, unless required by applicable law (such as deliberate - * and grossly negligent acts) or agreed to in writing, shall any Contributor be - * liable for any damages, including any direct, indirect, special, incidental, - * or consequential damages of any character arising as a result of this License or - * out of the use or inability to use the software (including but not limited to damages - * for loss of goodwill, work stoppage, computer failure or malfunction, or any and - * all other commercial damages or losses), even if such Contributor has been advised - * of the possibility of such damages. - */ - -#pragma once - -/// \file -/// Implementation of the Diligent::RenderDeviceBase template class and related structures - -#include "RenderDevice.h" -#include "DeviceObjectBase.h" -#include "Defines.h" -#include "ResourceMappingImpl.h" -#include "StateObjectsRegistry.h" -#include "HashUtils.h" -#include "ObjectBase.h" -#include "DeviceContext.h" -#include "SwapChain.h" -#include "GraphicsAccessories.hpp" -#include "FixedBlockMemoryAllocator.h" -#include "EngineMemory.h" -#include "STDAllocator.h" - -namespace std -{ -/// Hash function specialization for Diligent::SamplerDesc structure. -template <> -struct hash -{ - size_t operator()(const Diligent::SamplerDesc& SamDesc) const - { - // Sampler name is ignored in comparison operator - // and should not be hashed - return Diligent::ComputeHash( // SamDesc.Name, - static_cast(SamDesc.MinFilter), - static_cast(SamDesc.MagFilter), - static_cast(SamDesc.MipFilter), - static_cast(SamDesc.AddressU), - static_cast(SamDesc.AddressV), - static_cast(SamDesc.AddressW), - SamDesc.MipLODBias, - SamDesc.MaxAnisotropy, - static_cast(SamDesc.ComparisonFunc), - SamDesc.BorderColor[0], - SamDesc.BorderColor[1], - SamDesc.BorderColor[2], - SamDesc.BorderColor[3], - SamDesc.MinLOD, SamDesc.MaxLOD); - } -}; - -/// Hash function specialization for Diligent::StencilOpDesc structure. -template <> -struct hash -{ - size_t operator()(const Diligent::StencilOpDesc& StOpDesc) const - { - return Diligent::ComputeHash(static_cast(StOpDesc.StencilFailOp), - static_cast(StOpDesc.StencilDepthFailOp), - static_cast(StOpDesc.StencilPassOp), - static_cast(StOpDesc.StencilFunc)); - } -}; - -/// Hash function specialization for Diligent::DepthStencilStateDesc structure. -template <> -struct hash -{ - size_t operator()(const Diligent::DepthStencilStateDesc& DepthStencilDesc) const - { - return Diligent::ComputeHash(DepthStencilDesc.DepthEnable, - DepthStencilDesc.DepthWriteEnable, - static_cast(DepthStencilDesc.DepthFunc), - DepthStencilDesc.StencilEnable, - DepthStencilDesc.StencilReadMask, - DepthStencilDesc.StencilWriteMask, - DepthStencilDesc.FrontFace, - DepthStencilDesc.BackFace); - } -}; - -/// Hash function specialization for Diligent::RasterizerStateDesc structure. -template <> -struct hash -{ - size_t operator()(const Diligent::RasterizerStateDesc& RasterizerDesc) const - { - return Diligent::ComputeHash(static_cast(RasterizerDesc.FillMode), - static_cast(RasterizerDesc.CullMode), - RasterizerDesc.FrontCounterClockwise, - RasterizerDesc.DepthBias, - RasterizerDesc.DepthBiasClamp, - RasterizerDesc.SlopeScaledDepthBias, - RasterizerDesc.DepthClipEnable, - RasterizerDesc.ScissorEnable, - RasterizerDesc.AntialiasedLineEnable); - } -}; - -/// Hash function specialization for Diligent::BlendStateDesc structure. -template <> -struct hash -{ - size_t operator()(const Diligent::BlendStateDesc& BSDesc) const - { - std::size_t Seed = 0; - for (int i = 0; i < Diligent::MAX_RENDER_TARGETS; ++i) - { - const auto& rt = BSDesc.RenderTargets[i]; - Diligent::HashCombine(Seed, - rt.BlendEnable, - static_cast(rt.SrcBlend), - static_cast(rt.DestBlend), - static_cast(rt.BlendOp), - static_cast(rt.SrcBlendAlpha), - static_cast(rt.DestBlendAlpha), - static_cast(rt.BlendOpAlpha), - rt.RenderTargetWriteMask); - } - Diligent::HashCombine(Seed, - BSDesc.AlphaToCoverageEnable, - BSDesc.IndependentBlendEnable); - return Seed; - } -}; - - -/// Hash function specialization for Diligent::TextureViewDesc structure. -template <> -struct hash -{ - size_t operator()(const Diligent::TextureViewDesc& TexViewDesc) const - { - std::size_t Seed = 0; - Diligent::HashCombine(Seed, - static_cast(TexViewDesc.ViewType), - static_cast(TexViewDesc.TextureDim), - static_cast(TexViewDesc.Format), - TexViewDesc.MostDetailedMip, - TexViewDesc.NumMipLevels, - TexViewDesc.FirstArraySlice, - TexViewDesc.NumArraySlices, - static_cast(TexViewDesc.AccessFlags), - static_cast(TexViewDesc.Flags)); - return Seed; - } -}; -} // namespace std - -namespace Diligent -{ - -/// Base implementation of a render device - -/// \tparam BaseInterface - base interface that this class will inheret. -/// \warning -/// Render device must *NOT* hold strong references to any -/// object it creates to avoid circular dependencies. -/// Device context, swap chain and all object the device creates -/// keep strong reference to the device. -/// Device only holds weak reference to the immediate context. -template -class RenderDeviceBase : public ObjectBase -{ -public: - using TObjectBase = ObjectBase; - - /// Describes the sizes of device objects - struct DeviceObjectSizes - { - /// Size of the texture object (TextureD3D12Impl, TextureVkImpl, etc.), in bytes - const size_t TextureObjSize; - - /// Size of the texture view object (TextureViewD3D12Impl, TextureViewVkImpl, etc.), in bytes - const size_t TexViewObjSize; - - /// Size of the buffer object (BufferD3D12Impl, BufferVkImpl, etc.), in bytes - const size_t BufferObjSize; - - /// Size of the buffer view object (BufferViewD3D12Impl, BufferViewVkImpl, etc.), in bytes - const size_t BuffViewObjSize; - - /// Size of the shader object (ShaderD3D12Impl, ShaderVkImpl, etc.), in bytes - const size_t ShaderObjSize; - - /// Size of the sampler object (SamplerD3D12Impl, SamplerVkImpl, etc.), in bytes - const size_t SamplerObjSize; - - /// Size of the pipeline state object (PipelineStateD3D12Impl, PipelineStateVkImpl, etc.), in bytes - const size_t PSOSize; - - /// Size of the shader resource binding object (ShaderResourceBindingD3D12Impl, ShaderResourceBindingVkImpl, etc.), in bytes - const size_t SRBSize; - - /// Size of the fence object (FenceD3D12Impl, FenceVkImpl, etc.), in bytes - const size_t FenceSize; - - /// Size of the query object (QueryD3D12Impl, QueryVkImpl, etc.), in bytes - const size_t QuerySize; - }; - - /// \param pRefCounters - reference counters object that controls the lifetime of this render device - /// \param RawMemAllocator - allocator that will be used to allocate memory for all device objects (including render device itself) - /// \param pEngineFactory - engine factory that was used to create this device - /// \param NumDeferredContexts - number of deferred device contexts - /// \param ObjectSizes - device object sizes - /// - /// \remarks Render device uses fixed block allocators (see FixedBlockMemoryAllocator) to allocate memory for - /// device objects. The object sizes provided to constructor are used to initialize the allocators. - RenderDeviceBase(IReferenceCounters* pRefCounters, - IMemoryAllocator& RawMemAllocator, - IEngineFactory* pEngineFactory, - Uint32 NumDeferredContexts, - const DeviceObjectSizes& ObjectSizes) : - // clang-format off - TObjectBase {pRefCounters}, - m_pEngineFactory {pEngineFactory}, - m_SamplersRegistry {RawMemAllocator, "sampler"}, - m_TextureFormatsInfo (TEX_FORMAT_NUM_FORMATS, TextureFormatInfoExt(), STD_ALLOCATOR_RAW_MEM(TextureFormatInfoExt, RawMemAllocator, "Allocator for vector")), - m_TexFmtInfoInitFlags (TEX_FORMAT_NUM_FORMATS, false, STD_ALLOCATOR_RAW_MEM(bool, RawMemAllocator, "Allocator for vector")), - m_wpDeferredContexts (NumDeferredContexts, RefCntWeakPtr(), STD_ALLOCATOR_RAW_MEM(RefCntWeakPtr, RawMemAllocator, "Allocator for vector< RefCntWeakPtr >")), - m_RawMemAllocator {RawMemAllocator}, - m_TexObjAllocator {RawMemAllocator, ObjectSizes.TextureObjSize, 64 }, - m_TexViewObjAllocator {RawMemAllocator, ObjectSizes.TexViewObjSize, 64 }, - m_BufObjAllocator {RawMemAllocator, ObjectSizes.BufferObjSize, 128 }, - m_BuffViewObjAllocator {RawMemAllocator, ObjectSizes.BuffViewObjSize, 128 }, - m_ShaderObjAllocator {RawMemAllocator, ObjectSizes.ShaderObjSize, 32 }, - m_SamplerObjAllocator {RawMemAllocator, ObjectSizes.SamplerObjSize, 32 }, - m_PSOAllocator {RawMemAllocator, ObjectSizes.PSOSize, 128 }, - m_SRBAllocator {RawMemAllocator, ObjectSizes.SRBSize, 1024}, - m_ResMappingAllocator {RawMemAllocator, sizeof(ResourceMappingImpl), 16 }, - m_FenceAllocator {RawMemAllocator, ObjectSizes.FenceSize, 16 }, - m_QueryAllocator {RawMemAllocator, ObjectSizes.QuerySize, 16 } - // clang-format on - { - // Initialize texture format info - for (Uint32 Fmt = TEX_FORMAT_UNKNOWN; Fmt < TEX_FORMAT_NUM_FORMATS; ++Fmt) - static_cast(m_TextureFormatsInfo[Fmt]) = GetTextureFormatAttribs(static_cast(Fmt)); - - // https://msdn.microsoft.com/en-us/library/windows/desktop/ff471325(v=vs.85).aspx - TEXTURE_FORMAT FilterableFormats[] = - { - TEX_FORMAT_RGBA32_FLOAT, // OpenGL ES3.1 does not require this format to be filterable - TEX_FORMAT_RGBA16_FLOAT, - TEX_FORMAT_RGBA16_UNORM, - TEX_FORMAT_RGBA16_SNORM, - TEX_FORMAT_RG32_FLOAT, // OpenGL ES3.1 does not require this format to be filterable - TEX_FORMAT_R32_FLOAT_X8X24_TYPELESS, - //TEX_FORMAT_R10G10B10A2_UNORM, - TEX_FORMAT_R11G11B10_FLOAT, - TEX_FORMAT_RGBA8_UNORM, - TEX_FORMAT_RGBA8_UNORM_SRGB, - TEX_FORMAT_RGBA8_SNORM, - TEX_FORMAT_RG16_FLOAT, - TEX_FORMAT_RG16_UNORM, - TEX_FORMAT_RG16_SNORM, - TEX_FORMAT_R32_FLOAT, // OpenGL ES3.1 does not require this format to be filterable - TEX_FORMAT_R24_UNORM_X8_TYPELESS, - TEX_FORMAT_RG8_UNORM, - TEX_FORMAT_RG8_SNORM, - TEX_FORMAT_R16_FLOAT, - TEX_FORMAT_R16_UNORM, - TEX_FORMAT_R16_SNORM, - TEX_FORMAT_R8_UNORM, - TEX_FORMAT_R8_SNORM, - TEX_FORMAT_A8_UNORM, - TEX_FORMAT_RGB9E5_SHAREDEXP, - TEX_FORMAT_RG8_B8G8_UNORM, - TEX_FORMAT_G8R8_G8B8_UNORM, - TEX_FORMAT_BC1_UNORM, - TEX_FORMAT_BC1_UNORM_SRGB, - TEX_FORMAT_BC2_UNORM, - TEX_FORMAT_BC2_UNORM_SRGB, - TEX_FORMAT_BC3_UNORM, - TEX_FORMAT_BC3_UNORM_SRGB, - TEX_FORMAT_BC4_UNORM, - TEX_FORMAT_BC4_SNORM, - TEX_FORMAT_BC5_UNORM, - TEX_FORMAT_BC5_SNORM, - TEX_FORMAT_B5G6R5_UNORM}; - for (Uint32 fmt = 0; fmt < _countof(FilterableFormats); ++fmt) - m_TextureFormatsInfo[FilterableFormats[fmt]].Filterable = true; - } - - ~RenderDeviceBase() - { - } - - IMPLEMENT_QUERY_INTERFACE_IN_PLACE(IID_RenderDevice, ObjectBase) - - // It is important to have final implementation of Release() method to avoid - // virtual calls - inline virtual ReferenceCounterValueType Release() override final - { - return TObjectBase::Release(); - } - - /// Implementation of IRenderDevice::CreateResourceMapping(). - virtual void CreateResourceMapping(const ResourceMappingDesc& MappingDesc, IResourceMapping** ppMapping) override final; - - /// Implementation of IRenderDevice::GetDeviceCaps(). - virtual const DeviceCaps& GetDeviceCaps() const override final - { - return m_DeviceCaps; - } - - /// Implementation of IRenderDevice::GetTextureFormatInfo(). - virtual const TextureFormatInfo& GetTextureFormatInfo(TEXTURE_FORMAT TexFormat) override final - { - VERIFY(TexFormat >= TEX_FORMAT_UNKNOWN && TexFormat < TEX_FORMAT_NUM_FORMATS, "Texture format out of range"); - const auto& TexFmtInfo = m_TextureFormatsInfo[TexFormat]; - VERIFY(TexFmtInfo.Format == TexFormat, "Sanity check failed"); - return TexFmtInfo; - } - - /// Implementation of IRenderDevice::GetTextureFormatInfoExt(). - virtual const TextureFormatInfoExt& GetTextureFormatInfoExt(TEXTURE_FORMAT TexFormat) override final - { - VERIFY(TexFormat >= TEX_FORMAT_UNKNOWN && TexFormat < TEX_FORMAT_NUM_FORMATS, "Texture format out of range"); - const auto& TexFmtInfo = m_TextureFormatsInfo[TexFormat]; - VERIFY(TexFmtInfo.Format == TexFormat, "Sanity check failed"); - if (!m_TexFmtInfoInitFlags[TexFormat]) - { - if (TexFmtInfo.Supported) - TestTextureFormat(TexFormat); - m_TexFmtInfoInitFlags[TexFormat] = true; - } - return TexFmtInfo; - } - - virtual IEngineFactory* GetEngineFactory() const override final - { - return m_pEngineFactory.RawPtr(); - } - - void OnCreateDeviceObject(IDeviceObject* pNewObject) - { - } - - StateObjectsRegistry& GetSamplerRegistry() { return m_SamplersRegistry; } - - /// Set weak reference to the immediate context - void SetImmediateContext(IDeviceContext* pImmediateContext) - { - VERIFY(m_wpImmediateContext.Lock() == nullptr, "Immediate context has already been set"); - m_wpImmediateContext = pImmediateContext; - } - - /// Set weak reference to the deferred context - void SetDeferredContext(size_t Ctx, IDeviceContext* pDeferredCtx) - { - VERIFY(m_wpDeferredContexts[Ctx].Lock() == nullptr, "Deferred context has already been set"); - m_wpDeferredContexts[Ctx] = pDeferredCtx; - } - - /// Returns number of deferred contexts - size_t GetNumDeferredContexts() const - { - return m_wpDeferredContexts.size(); - } - - RefCntAutoPtr GetImmediateContext() { return m_wpImmediateContext.Lock(); } - RefCntAutoPtr GetDeferredContext(size_t Ctx) { return m_wpDeferredContexts[Ctx].Lock(); } - - FixedBlockMemoryAllocator& GetTexViewObjAllocator() { return m_TexViewObjAllocator; } - FixedBlockMemoryAllocator& GetBuffViewObjAllocator() { return m_BuffViewObjAllocator; } - FixedBlockMemoryAllocator& GetSRBAllocator() { return m_SRBAllocator; } - -protected: - virtual void TestTextureFormat(TEXTURE_FORMAT TexFormat) = 0; - - /// Helper template function to facilitate device object creation - template - void CreateDeviceObject(const Char* ObjectTypeName, const TObjectDescType& Desc, TObjectType** ppObject, TObjectConstructor ConstructObject); - - RefCntAutoPtr m_pEngineFactory; - - DeviceCaps m_DeviceCaps; - - // All state object registries hold raw pointers. - // This is safe because every object unregisters itself - // when it is deleted. - StateObjectsRegistry m_SamplersRegistry; ///< Sampler state registry - std::vector> m_TextureFormatsInfo; - std::vector> m_TexFmtInfoInitFlags; - - /// Weak reference to the immediate context. Immediate context holds strong reference - /// to the device, so we must use weak reference to avoid circular dependencies. - RefCntWeakPtr m_wpImmediateContext; - - /// Weak references to deferred contexts. - std::vector, STDAllocatorRawMem>> m_wpDeferredContexts; - - IMemoryAllocator& m_RawMemAllocator; ///< Raw memory allocator - FixedBlockMemoryAllocator m_TexObjAllocator; ///< Allocator for texture objects - FixedBlockMemoryAllocator m_TexViewObjAllocator; ///< Allocator for texture view objects - FixedBlockMemoryAllocator m_BufObjAllocator; ///< Allocator for buffer objects - FixedBlockMemoryAllocator m_BuffViewObjAllocator; ///< Allocator for buffer view objects - FixedBlockMemoryAllocator m_ShaderObjAllocator; ///< Allocator for shader objects - FixedBlockMemoryAllocator m_SamplerObjAllocator; ///< Allocator for sampler objects - FixedBlockMemoryAllocator m_PSOAllocator; ///< Allocator for pipeline state objects - FixedBlockMemoryAllocator m_SRBAllocator; ///< Allocator for shader resource binding objects - FixedBlockMemoryAllocator m_ResMappingAllocator; ///< Allocator for resource mapping objects - FixedBlockMemoryAllocator m_FenceAllocator; ///< Allocator for fence objects - FixedBlockMemoryAllocator m_QueryAllocator; ///< Allocator for query objects -}; - - -template -void RenderDeviceBase::CreateResourceMapping(const ResourceMappingDesc& MappingDesc, IResourceMapping** ppMapping) -{ - VERIFY(ppMapping != nullptr, "Null pointer provided"); - if (ppMapping == nullptr) - return; - VERIFY(*ppMapping == nullptr, "Overwriting reference to existing object may cause memory leaks"); - - auto* pResourceMapping(NEW_RC_OBJ(m_ResMappingAllocator, "ResourceMappingImpl instance", ResourceMappingImpl)(GetRawAllocator())); - pResourceMapping->QueryInterface(IID_ResourceMapping, reinterpret_cast(ppMapping)); - if (MappingDesc.pEntries) - { - for (auto* pEntry = MappingDesc.pEntries; pEntry->Name && pEntry->pObject; ++pEntry) - { - (*ppMapping)->AddResourceArray(pEntry->Name, pEntry->ArrayIndex, &pEntry->pObject, 1, true); - } - } -} - - -/// \tparam TObjectType - type of the object being created (IBuffer, ITexture, etc.) -/// \tparam TObjectDescType - type of the object description structure (BufferDesc, TextureDesc, etc.) -/// \tparam TObjectConstructor - type of the function that constructs the object -/// \param ObjectTypeName - string name of the object type ("buffer", "texture", etc.) -/// \param Desc - object description -/// \param ppObject - memory address where the pointer to the created object will be stored -/// \param ConstructObject - function that constructs the object -template -template -void RenderDeviceBase::CreateDeviceObject(const Char* ObjectTypeName, const TObjectDescType& Desc, TObjectType** ppObject, TObjectConstructor ConstructObject) -{ - VERIFY(ppObject != nullptr, "Null pointer provided"); - if (!ppObject) - return; - - VERIFY(*ppObject == nullptr, "Overwriting reference to existing object may cause memory leaks"); - // Do not release *ppObject here! - // Should this happen, RefCntAutoPtr<> will take care of this! - //if( *ppObject ) - //{ - // (*ppObject)->Release(); - // *ppObject = nullptr; - //} - - *ppObject = nullptr; - - try - { - ConstructObject(); - } - catch (const std::runtime_error&) - { - VERIFY(*ppObject == nullptr, "Object was created despite error"); - if (*ppObject) - { - (*ppObject)->Release(); - *ppObject = nullptr; - } - auto ObjectDescString = GetObjectDescString(Desc); - if (ObjectDescString.length()) - { - LOG_ERROR("Failed to create ", ObjectTypeName, " object '", Desc.Name ? Desc.Name : "", "'\n", ObjectDescString); - } - else - { - LOG_ERROR("Failed to create ", ObjectTypeName, " object '", Desc.Name ? Desc.Name : "", "'"); - } - } -} - -} // namespace Diligent diff --git a/Graphics/GraphicsEngine/include/RenderDeviceBase.hpp b/Graphics/GraphicsEngine/include/RenderDeviceBase.hpp new file mode 100644 index 00000000..8c16321d --- /dev/null +++ b/Graphics/GraphicsEngine/include/RenderDeviceBase.hpp @@ -0,0 +1,502 @@ +/* + * Copyright 2019-2020 Diligent Graphics LLC + * Copyright 2015-2019 Egor Yusov + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + * + * In no event and under no legal theory, whether in tort (including negligence), + * contract, or otherwise, unless required by applicable law (such as deliberate + * and grossly negligent acts) or agreed to in writing, shall any Contributor be + * liable for any damages, including any direct, indirect, special, incidental, + * or consequential damages of any character arising as a result of this License or + * out of the use or inability to use the software (including but not limited to damages + * for loss of goodwill, work stoppage, computer failure or malfunction, or any and + * all other commercial damages or losses), even if such Contributor has been advised + * of the possibility of such damages. + */ + +#pragma once + +/// \file +/// Implementation of the Diligent::RenderDeviceBase template class and related structures + +#include "RenderDevice.h" +#include "DeviceObjectBase.hpp" +#include "Defines.h" +#include "ResourceMappingImpl.hpp" +#include "StateObjectsRegistry.hpp" +#include "HashUtils.h" +#include "ObjectBase.h" +#include "DeviceContext.h" +#include "SwapChain.h" +#include "GraphicsAccessories.hpp" +#include "FixedBlockMemoryAllocator.h" +#include "EngineMemory.h" +#include "STDAllocator.h" + +namespace std +{ +/// Hash function specialization for Diligent::SamplerDesc structure. +template <> +struct hash +{ + size_t operator()(const Diligent::SamplerDesc& SamDesc) const + { + // Sampler name is ignored in comparison operator + // and should not be hashed + return Diligent::ComputeHash( // SamDesc.Name, + static_cast(SamDesc.MinFilter), + static_cast(SamDesc.MagFilter), + static_cast(SamDesc.MipFilter), + static_cast(SamDesc.AddressU), + static_cast(SamDesc.AddressV), + static_cast(SamDesc.AddressW), + SamDesc.MipLODBias, + SamDesc.MaxAnisotropy, + static_cast(SamDesc.ComparisonFunc), + SamDesc.BorderColor[0], + SamDesc.BorderColor[1], + SamDesc.BorderColor[2], + SamDesc.BorderColor[3], + SamDesc.MinLOD, SamDesc.MaxLOD); + } +}; + +/// Hash function specialization for Diligent::StencilOpDesc structure. +template <> +struct hash +{ + size_t operator()(const Diligent::StencilOpDesc& StOpDesc) const + { + return Diligent::ComputeHash(static_cast(StOpDesc.StencilFailOp), + static_cast(StOpDesc.StencilDepthFailOp), + static_cast(StOpDesc.StencilPassOp), + static_cast(StOpDesc.StencilFunc)); + } +}; + +/// Hash function specialization for Diligent::DepthStencilStateDesc structure. +template <> +struct hash +{ + size_t operator()(const Diligent::DepthStencilStateDesc& DepthStencilDesc) const + { + return Diligent::ComputeHash(DepthStencilDesc.DepthEnable, + DepthStencilDesc.DepthWriteEnable, + static_cast(DepthStencilDesc.DepthFunc), + DepthStencilDesc.StencilEnable, + DepthStencilDesc.StencilReadMask, + DepthStencilDesc.StencilWriteMask, + DepthStencilDesc.FrontFace, + DepthStencilDesc.BackFace); + } +}; + +/// Hash function specialization for Diligent::RasterizerStateDesc structure. +template <> +struct hash +{ + size_t operator()(const Diligent::RasterizerStateDesc& RasterizerDesc) const + { + return Diligent::ComputeHash(static_cast(RasterizerDesc.FillMode), + static_cast(RasterizerDesc.CullMode), + RasterizerDesc.FrontCounterClockwise, + RasterizerDesc.DepthBias, + RasterizerDesc.DepthBiasClamp, + RasterizerDesc.SlopeScaledDepthBias, + RasterizerDesc.DepthClipEnable, + RasterizerDesc.ScissorEnable, + RasterizerDesc.AntialiasedLineEnable); + } +}; + +/// Hash function specialization for Diligent::BlendStateDesc structure. +template <> +struct hash +{ + size_t operator()(const Diligent::BlendStateDesc& BSDesc) const + { + std::size_t Seed = 0; + for (int i = 0; i < Diligent::MAX_RENDER_TARGETS; ++i) + { + const auto& rt = BSDesc.RenderTargets[i]; + Diligent::HashCombine(Seed, + rt.BlendEnable, + static_cast(rt.SrcBlend), + static_cast(rt.DestBlend), + static_cast(rt.BlendOp), + static_cast(rt.SrcBlendAlpha), + static_cast(rt.DestBlendAlpha), + static_cast(rt.BlendOpAlpha), + rt.RenderTargetWriteMask); + } + Diligent::HashCombine(Seed, + BSDesc.AlphaToCoverageEnable, + BSDesc.IndependentBlendEnable); + return Seed; + } +}; + + +/// Hash function specialization for Diligent::TextureViewDesc structure. +template <> +struct hash +{ + size_t operator()(const Diligent::TextureViewDesc& TexViewDesc) const + { + std::size_t Seed = 0; + Diligent::HashCombine(Seed, + static_cast(TexViewDesc.ViewType), + static_cast(TexViewDesc.TextureDim), + static_cast(TexViewDesc.Format), + TexViewDesc.MostDetailedMip, + TexViewDesc.NumMipLevels, + TexViewDesc.FirstArraySlice, + TexViewDesc.NumArraySlices, + static_cast(TexViewDesc.AccessFlags), + static_cast(TexViewDesc.Flags)); + return Seed; + } +}; +} // namespace std + +namespace Diligent +{ + +/// Base implementation of a render device + +/// \tparam BaseInterface - base interface that this class will inheret. +/// \warning +/// Render device must *NOT* hold strong references to any +/// object it creates to avoid circular dependencies. +/// Device context, swap chain and all object the device creates +/// keep strong reference to the device. +/// Device only holds weak reference to the immediate context. +template +class RenderDeviceBase : public ObjectBase +{ +public: + using TObjectBase = ObjectBase; + + /// Describes the sizes of device objects + struct DeviceObjectSizes + { + /// Size of the texture object (TextureD3D12Impl, TextureVkImpl, etc.), in bytes + const size_t TextureObjSize; + + /// Size of the texture view object (TextureViewD3D12Impl, TextureViewVkImpl, etc.), in bytes + const size_t TexViewObjSize; + + /// Size of the buffer object (BufferD3D12Impl, BufferVkImpl, etc.), in bytes + const size_t BufferObjSize; + + /// Size of the buffer view object (BufferViewD3D12Impl, BufferViewVkImpl, etc.), in bytes + const size_t BuffViewObjSize; + + /// Size of the shader object (ShaderD3D12Impl, ShaderVkImpl, etc.), in bytes + const size_t ShaderObjSize; + + /// Size of the sampler object (SamplerD3D12Impl, SamplerVkImpl, etc.), in bytes + const size_t SamplerObjSize; + + /// Size of the pipeline state object (PipelineStateD3D12Impl, PipelineStateVkImpl, etc.), in bytes + const size_t PSOSize; + + /// Size of the shader resource binding object (ShaderResourceBindingD3D12Impl, ShaderResourceBindingVkImpl, etc.), in bytes + const size_t SRBSize; + + /// Size of the fence object (FenceD3D12Impl, FenceVkImpl, etc.), in bytes + const size_t FenceSize; + + /// Size of the query object (QueryD3D12Impl, QueryVkImpl, etc.), in bytes + const size_t QuerySize; + }; + + /// \param pRefCounters - reference counters object that controls the lifetime of this render device + /// \param RawMemAllocator - allocator that will be used to allocate memory for all device objects (including render device itself) + /// \param pEngineFactory - engine factory that was used to create this device + /// \param NumDeferredContexts - number of deferred device contexts + /// \param ObjectSizes - device object sizes + /// + /// \remarks Render device uses fixed block allocators (see FixedBlockMemoryAllocator) to allocate memory for + /// device objects. The object sizes provided to constructor are used to initialize the allocators. + RenderDeviceBase(IReferenceCounters* pRefCounters, + IMemoryAllocator& RawMemAllocator, + IEngineFactory* pEngineFactory, + Uint32 NumDeferredContexts, + const DeviceObjectSizes& ObjectSizes) : + // clang-format off + TObjectBase {pRefCounters}, + m_pEngineFactory {pEngineFactory}, + m_SamplersRegistry {RawMemAllocator, "sampler"}, + m_TextureFormatsInfo (TEX_FORMAT_NUM_FORMATS, TextureFormatInfoExt(), STD_ALLOCATOR_RAW_MEM(TextureFormatInfoExt, RawMemAllocator, "Allocator for vector")), + m_TexFmtInfoInitFlags (TEX_FORMAT_NUM_FORMATS, false, STD_ALLOCATOR_RAW_MEM(bool, RawMemAllocator, "Allocator for vector")), + m_wpDeferredContexts (NumDeferredContexts, RefCntWeakPtr(), STD_ALLOCATOR_RAW_MEM(RefCntWeakPtr, RawMemAllocator, "Allocator for vector< RefCntWeakPtr >")), + m_RawMemAllocator {RawMemAllocator}, + m_TexObjAllocator {RawMemAllocator, ObjectSizes.TextureObjSize, 64 }, + m_TexViewObjAllocator {RawMemAllocator, ObjectSizes.TexViewObjSize, 64 }, + m_BufObjAllocator {RawMemAllocator, ObjectSizes.BufferObjSize, 128 }, + m_BuffViewObjAllocator {RawMemAllocator, ObjectSizes.BuffViewObjSize, 128 }, + m_ShaderObjAllocator {RawMemAllocator, ObjectSizes.ShaderObjSize, 32 }, + m_SamplerObjAllocator {RawMemAllocator, ObjectSizes.SamplerObjSize, 32 }, + m_PSOAllocator {RawMemAllocator, ObjectSizes.PSOSize, 128 }, + m_SRBAllocator {RawMemAllocator, ObjectSizes.SRBSize, 1024}, + m_ResMappingAllocator {RawMemAllocator, sizeof(ResourceMappingImpl), 16 }, + m_FenceAllocator {RawMemAllocator, ObjectSizes.FenceSize, 16 }, + m_QueryAllocator {RawMemAllocator, ObjectSizes.QuerySize, 16 } + // clang-format on + { + // Initialize texture format info + for (Uint32 Fmt = TEX_FORMAT_UNKNOWN; Fmt < TEX_FORMAT_NUM_FORMATS; ++Fmt) + static_cast(m_TextureFormatsInfo[Fmt]) = GetTextureFormatAttribs(static_cast(Fmt)); + + // https://msdn.microsoft.com/en-us/library/windows/desktop/ff471325(v=vs.85).aspx + TEXTURE_FORMAT FilterableFormats[] = + { + TEX_FORMAT_RGBA32_FLOAT, // OpenGL ES3.1 does not require this format to be filterable + TEX_FORMAT_RGBA16_FLOAT, + TEX_FORMAT_RGBA16_UNORM, + TEX_FORMAT_RGBA16_SNORM, + TEX_FORMAT_RG32_FLOAT, // OpenGL ES3.1 does not require this format to be filterable + TEX_FORMAT_R32_FLOAT_X8X24_TYPELESS, + //TEX_FORMAT_R10G10B10A2_UNORM, + TEX_FORMAT_R11G11B10_FLOAT, + TEX_FORMAT_RGBA8_UNORM, + TEX_FORMAT_RGBA8_UNORM_SRGB, + TEX_FORMAT_RGBA8_SNORM, + TEX_FORMAT_RG16_FLOAT, + TEX_FORMAT_RG16_UNORM, + TEX_FORMAT_RG16_SNORM, + TEX_FORMAT_R32_FLOAT, // OpenGL ES3.1 does not require this format to be filterable + TEX_FORMAT_R24_UNORM_X8_TYPELESS, + TEX_FORMAT_RG8_UNORM, + TEX_FORMAT_RG8_SNORM, + TEX_FORMAT_R16_FLOAT, + TEX_FORMAT_R16_UNORM, + TEX_FORMAT_R16_SNORM, + TEX_FORMAT_R8_UNORM, + TEX_FORMAT_R8_SNORM, + TEX_FORMAT_A8_UNORM, + TEX_FORMAT_RGB9E5_SHAREDEXP, + TEX_FORMAT_RG8_B8G8_UNORM, + TEX_FORMAT_G8R8_G8B8_UNORM, + TEX_FORMAT_BC1_UNORM, + TEX_FORMAT_BC1_UNORM_SRGB, + TEX_FORMAT_BC2_UNORM, + TEX_FORMAT_BC2_UNORM_SRGB, + TEX_FORMAT_BC3_UNORM, + TEX_FORMAT_BC3_UNORM_SRGB, + TEX_FORMAT_BC4_UNORM, + TEX_FORMAT_BC4_SNORM, + TEX_FORMAT_BC5_UNORM, + TEX_FORMAT_BC5_SNORM, + TEX_FORMAT_B5G6R5_UNORM}; + for (Uint32 fmt = 0; fmt < _countof(FilterableFormats); ++fmt) + m_TextureFormatsInfo[FilterableFormats[fmt]].Filterable = true; + } + + ~RenderDeviceBase() + { + } + + IMPLEMENT_QUERY_INTERFACE_IN_PLACE(IID_RenderDevice, ObjectBase) + + // It is important to have final implementation of Release() method to avoid + // virtual calls + inline virtual ReferenceCounterValueType Release() override final + { + return TObjectBase::Release(); + } + + /// Implementation of IRenderDevice::CreateResourceMapping(). + virtual void CreateResourceMapping(const ResourceMappingDesc& MappingDesc, IResourceMapping** ppMapping) override final; + + /// Implementation of IRenderDevice::GetDeviceCaps(). + virtual const DeviceCaps& GetDeviceCaps() const override final + { + return m_DeviceCaps; + } + + /// Implementation of IRenderDevice::GetTextureFormatInfo(). + virtual const TextureFormatInfo& GetTextureFormatInfo(TEXTURE_FORMAT TexFormat) override final + { + VERIFY(TexFormat >= TEX_FORMAT_UNKNOWN && TexFormat < TEX_FORMAT_NUM_FORMATS, "Texture format out of range"); + const auto& TexFmtInfo = m_TextureFormatsInfo[TexFormat]; + VERIFY(TexFmtInfo.Format == TexFormat, "Sanity check failed"); + return TexFmtInfo; + } + + /// Implementation of IRenderDevice::GetTextureFormatInfoExt(). + virtual const TextureFormatInfoExt& GetTextureFormatInfoExt(TEXTURE_FORMAT TexFormat) override final + { + VERIFY(TexFormat >= TEX_FORMAT_UNKNOWN && TexFormat < TEX_FORMAT_NUM_FORMATS, "Texture format out of range"); + const auto& TexFmtInfo = m_TextureFormatsInfo[TexFormat]; + VERIFY(TexFmtInfo.Format == TexFormat, "Sanity check failed"); + if (!m_TexFmtInfoInitFlags[TexFormat]) + { + if (TexFmtInfo.Supported) + TestTextureFormat(TexFormat); + m_TexFmtInfoInitFlags[TexFormat] = true; + } + return TexFmtInfo; + } + + virtual IEngineFactory* GetEngineFactory() const override final + { + return m_pEngineFactory.RawPtr(); + } + + void OnCreateDeviceObject(IDeviceObject* pNewObject) + { + } + + StateObjectsRegistry& GetSamplerRegistry() { return m_SamplersRegistry; } + + /// Set weak reference to the immediate context + void SetImmediateContext(IDeviceContext* pImmediateContext) + { + VERIFY(m_wpImmediateContext.Lock() == nullptr, "Immediate context has already been set"); + m_wpImmediateContext = pImmediateContext; + } + + /// Set weak reference to the deferred context + void SetDeferredContext(size_t Ctx, IDeviceContext* pDeferredCtx) + { + VERIFY(m_wpDeferredContexts[Ctx].Lock() == nullptr, "Deferred context has already been set"); + m_wpDeferredContexts[Ctx] = pDeferredCtx; + } + + /// Returns number of deferred contexts + size_t GetNumDeferredContexts() const + { + return m_wpDeferredContexts.size(); + } + + RefCntAutoPtr GetImmediateContext() { return m_wpImmediateContext.Lock(); } + RefCntAutoPtr GetDeferredContext(size_t Ctx) { return m_wpDeferredContexts[Ctx].Lock(); } + + FixedBlockMemoryAllocator& GetTexViewObjAllocator() { return m_TexViewObjAllocator; } + FixedBlockMemoryAllocator& GetBuffViewObjAllocator() { return m_BuffViewObjAllocator; } + FixedBlockMemoryAllocator& GetSRBAllocator() { return m_SRBAllocator; } + +protected: + virtual void TestTextureFormat(TEXTURE_FORMAT TexFormat) = 0; + + /// Helper template function to facilitate device object creation + template + void CreateDeviceObject(const Char* ObjectTypeName, const TObjectDescType& Desc, TObjectType** ppObject, TObjectConstructor ConstructObject); + + RefCntAutoPtr m_pEngineFactory; + + DeviceCaps m_DeviceCaps; + + // All state object registries hold raw pointers. + // This is safe because every object unregisters itself + // when it is deleted. + StateObjectsRegistry m_SamplersRegistry; ///< Sampler state registry + std::vector> m_TextureFormatsInfo; + std::vector> m_TexFmtInfoInitFlags; + + /// Weak reference to the immediate context. Immediate context holds strong reference + /// to the device, so we must use weak reference to avoid circular dependencies. + RefCntWeakPtr m_wpImmediateContext; + + /// Weak references to deferred contexts. + std::vector, STDAllocatorRawMem>> m_wpDeferredContexts; + + IMemoryAllocator& m_RawMemAllocator; ///< Raw memory allocator + FixedBlockMemoryAllocator m_TexObjAllocator; ///< Allocator for texture objects + FixedBlockMemoryAllocator m_TexViewObjAllocator; ///< Allocator for texture view objects + FixedBlockMemoryAllocator m_BufObjAllocator; ///< Allocator for buffer objects + FixedBlockMemoryAllocator m_BuffViewObjAllocator; ///< Allocator for buffer view objects + FixedBlockMemoryAllocator m_ShaderObjAllocator; ///< Allocator for shader objects + FixedBlockMemoryAllocator m_SamplerObjAllocator; ///< Allocator for sampler objects + FixedBlockMemoryAllocator m_PSOAllocator; ///< Allocator for pipeline state objects + FixedBlockMemoryAllocator m_SRBAllocator; ///< Allocator for shader resource binding objects + FixedBlockMemoryAllocator m_ResMappingAllocator; ///< Allocator for resource mapping objects + FixedBlockMemoryAllocator m_FenceAllocator; ///< Allocator for fence objects + FixedBlockMemoryAllocator m_QueryAllocator; ///< Allocator for query objects +}; + + +template +void RenderDeviceBase::CreateResourceMapping(const ResourceMappingDesc& MappingDesc, IResourceMapping** ppMapping) +{ + VERIFY(ppMapping != nullptr, "Null pointer provided"); + if (ppMapping == nullptr) + return; + VERIFY(*ppMapping == nullptr, "Overwriting reference to existing object may cause memory leaks"); + + auto* pResourceMapping(NEW_RC_OBJ(m_ResMappingAllocator, "ResourceMappingImpl instance", ResourceMappingImpl)(GetRawAllocator())); + pResourceMapping->QueryInterface(IID_ResourceMapping, reinterpret_cast(ppMapping)); + if (MappingDesc.pEntries) + { + for (auto* pEntry = MappingDesc.pEntries; pEntry->Name && pEntry->pObject; ++pEntry) + { + (*ppMapping)->AddResourceArray(pEntry->Name, pEntry->ArrayIndex, &pEntry->pObject, 1, true); + } + } +} + + +/// \tparam TObjectType - type of the object being created (IBuffer, ITexture, etc.) +/// \tparam TObjectDescType - type of the object description structure (BufferDesc, TextureDesc, etc.) +/// \tparam TObjectConstructor - type of the function that constructs the object +/// \param ObjectTypeName - string name of the object type ("buffer", "texture", etc.) +/// \param Desc - object description +/// \param ppObject - memory address where the pointer to the created object will be stored +/// \param ConstructObject - function that constructs the object +template +template +void RenderDeviceBase::CreateDeviceObject(const Char* ObjectTypeName, const TObjectDescType& Desc, TObjectType** ppObject, TObjectConstructor ConstructObject) +{ + VERIFY(ppObject != nullptr, "Null pointer provided"); + if (!ppObject) + return; + + VERIFY(*ppObject == nullptr, "Overwriting reference to existing object may cause memory leaks"); + // Do not release *ppObject here! + // Should this happen, RefCntAutoPtr<> will take care of this! + //if( *ppObject ) + //{ + // (*ppObject)->Release(); + // *ppObject = nullptr; + //} + + *ppObject = nullptr; + + try + { + ConstructObject(); + } + catch (const std::runtime_error&) + { + VERIFY(*ppObject == nullptr, "Object was created despite error"); + if (*ppObject) + { + (*ppObject)->Release(); + *ppObject = nullptr; + } + auto ObjectDescString = GetObjectDescString(Desc); + if (ObjectDescString.length()) + { + LOG_ERROR("Failed to create ", ObjectTypeName, " object '", Desc.Name ? Desc.Name : "", "'\n", ObjectDescString); + } + else + { + LOG_ERROR("Failed to create ", ObjectTypeName, " object '", Desc.Name ? Desc.Name : "", "'"); + } + } +} + +} // namespace Diligent diff --git a/Graphics/GraphicsEngine/include/ResourceMappingImpl.h b/Graphics/GraphicsEngine/include/ResourceMappingImpl.h deleted file mode 100644 index 011e4e7a..00000000 --- a/Graphics/GraphicsEngine/include/ResourceMappingImpl.h +++ /dev/null @@ -1,136 +0,0 @@ -/* - * Copyright 2019-2020 Diligent Graphics LLC - * Copyright 2015-2019 Egor Yusov - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - * - * In no event and under no legal theory, whether in tort (including negligence), - * contract, or otherwise, unless required by applicable law (such as deliberate - * and grossly negligent acts) or agreed to in writing, shall any Contributor be - * liable for any damages, including any direct, indirect, special, incidental, - * or consequential damages of any character arising as a result of this License or - * out of the use or inability to use the software (including but not limited to damages - * for loss of goodwill, work stoppage, computer failure or malfunction, or any and - * all other commercial damages or losses), even if such Contributor has been advised - * of the possibility of such damages. - */ - -#pragma once - -/// \file -/// Declaration of the Diligent::ResourceMappingImpl class - -#include "ResourceMapping.h" -#include "ObjectBase.h" -#include -#include "HashUtils.h" -#include "STDAllocator.h" - -namespace Diligent -{ -struct ResMappingHashKey -{ - ResMappingHashKey(const Char* Str, bool bMakeCopy, Uint32 ArrInd) : - StrKey{Str, bMakeCopy}, - ArrayIndex{ArrInd} - { - } - - ResMappingHashKey(ResMappingHashKey&& rhs) : - StrKey{std::move(rhs.StrKey)}, - ArrayIndex{rhs.ArrayIndex} - {} - - bool operator==(const ResMappingHashKey& RHS) const - { - return StrKey == RHS.StrKey && ArrayIndex == RHS.ArrayIndex; - } - - size_t GetHash() const - { - if (Hash == 0) - { - Hash = ComputeHash(StrKey.GetHash(), ArrayIndex); - } - - return Hash; - } - - // clang-format off - ResMappingHashKey ( const ResMappingHashKey& ) = delete; - ResMappingHashKey& operator = ( const ResMappingHashKey& ) = delete; - ResMappingHashKey& operator = ( ResMappingHashKey&& ) = delete; - // clang-format on - - HashMapStringKey StrKey; - Uint32 ArrayIndex; - mutable size_t Hash = 0; -}; -} // namespace Diligent - -namespace std -{ -template <> -struct hash -{ - size_t operator()(const Diligent::ResMappingHashKey& Key) const - { - return Key.GetHash(); - } -}; -} // namespace std - -namespace Diligent -{ -class FixedBlockMemoryAllocator; - -/// Implementation of the resource mapping -class ResourceMappingImpl : public ObjectBase -{ -public: - typedef ObjectBase TObjectBase; - - /// \param pRefCounters - reference counters object that controls the lifetime of this resource mapping - /// \param RawMemAllocator - raw memory allocator that is used by the m_HashTable member - ResourceMappingImpl(IReferenceCounters* pRefCounters, IMemoryAllocator& RawMemAllocator) : - TObjectBase(pRefCounters), - m_HashTable(STD_ALLOCATOR_RAW_MEM(HashTableElem, RawMemAllocator, "Allocator for unordered_map< ResMappingHashKey, RefCntAutoPtr >")) - {} - - ~ResourceMappingImpl(); - - virtual void QueryInterface(const INTERFACE_ID& IID, IObject** ppInterface) override final; - - /// Implementation of IResourceMapping::AddResource() - virtual void AddResource(const Char* Name, IDeviceObject* pObject, bool bIsUnique) override final; - - /// Implementation of IResourceMapping::AddResourceArray() - virtual void AddResourceArray(const Char* Name, Uint32 StartIndex, IDeviceObject* const* ppObjects, Uint32 NumElements, bool bIsUnique) override final; - - /// Implementation of IResourceMapping::RemoveResourceByName() - virtual void RemoveResourceByName(const Char* Name, Uint32 ArrayIndex) override final; - - /// Implementation of IResourceMapping::GetResource() - virtual void GetResource(const Char* Name, IDeviceObject** ppResource, Uint32 ArrayIndex) override final; - - /// Returns number of resources in the resource mapping. - virtual size_t GetSize() override final; - -private: - ThreadingTools::LockHelper Lock(); - - ThreadingTools::LockFlag m_LockFlag; - typedef std::pair> HashTableElem; - std::unordered_map, std::hash, std::equal_to, STDAllocatorRawMem> m_HashTable; -}; -} // namespace Diligent diff --git a/Graphics/GraphicsEngine/include/ResourceMappingImpl.hpp b/Graphics/GraphicsEngine/include/ResourceMappingImpl.hpp new file mode 100644 index 00000000..011e4e7a --- /dev/null +++ b/Graphics/GraphicsEngine/include/ResourceMappingImpl.hpp @@ -0,0 +1,136 @@ +/* + * Copyright 2019-2020 Diligent Graphics LLC + * Copyright 2015-2019 Egor Yusov + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + * + * In no event and under no legal theory, whether in tort (including negligence), + * contract, or otherwise, unless required by applicable law (such as deliberate + * and grossly negligent acts) or agreed to in writing, shall any Contributor be + * liable for any damages, including any direct, indirect, special, incidental, + * or consequential damages of any character arising as a result of this License or + * out of the use or inability to use the software (including but not limited to damages + * for loss of goodwill, work stoppage, computer failure or malfunction, or any and + * all other commercial damages or losses), even if such Contributor has been advised + * of the possibility of such damages. + */ + +#pragma once + +/// \file +/// Declaration of the Diligent::ResourceMappingImpl class + +#include "ResourceMapping.h" +#include "ObjectBase.h" +#include +#include "HashUtils.h" +#include "STDAllocator.h" + +namespace Diligent +{ +struct ResMappingHashKey +{ + ResMappingHashKey(const Char* Str, bool bMakeCopy, Uint32 ArrInd) : + StrKey{Str, bMakeCopy}, + ArrayIndex{ArrInd} + { + } + + ResMappingHashKey(ResMappingHashKey&& rhs) : + StrKey{std::move(rhs.StrKey)}, + ArrayIndex{rhs.ArrayIndex} + {} + + bool operator==(const ResMappingHashKey& RHS) const + { + return StrKey == RHS.StrKey && ArrayIndex == RHS.ArrayIndex; + } + + size_t GetHash() const + { + if (Hash == 0) + { + Hash = ComputeHash(StrKey.GetHash(), ArrayIndex); + } + + return Hash; + } + + // clang-format off + ResMappingHashKey ( const ResMappingHashKey& ) = delete; + ResMappingHashKey& operator = ( const ResMappingHashKey& ) = delete; + ResMappingHashKey& operator = ( ResMappingHashKey&& ) = delete; + // clang-format on + + HashMapStringKey StrKey; + Uint32 ArrayIndex; + mutable size_t Hash = 0; +}; +} // namespace Diligent + +namespace std +{ +template <> +struct hash +{ + size_t operator()(const Diligent::ResMappingHashKey& Key) const + { + return Key.GetHash(); + } +}; +} // namespace std + +namespace Diligent +{ +class FixedBlockMemoryAllocator; + +/// Implementation of the resource mapping +class ResourceMappingImpl : public ObjectBase +{ +public: + typedef ObjectBase TObjectBase; + + /// \param pRefCounters - reference counters object that controls the lifetime of this resource mapping + /// \param RawMemAllocator - raw memory allocator that is used by the m_HashTable member + ResourceMappingImpl(IReferenceCounters* pRefCounters, IMemoryAllocator& RawMemAllocator) : + TObjectBase(pRefCounters), + m_HashTable(STD_ALLOCATOR_RAW_MEM(HashTableElem, RawMemAllocator, "Allocator for unordered_map< ResMappingHashKey, RefCntAutoPtr >")) + {} + + ~ResourceMappingImpl(); + + virtual void QueryInterface(const INTERFACE_ID& IID, IObject** ppInterface) override final; + + /// Implementation of IResourceMapping::AddResource() + virtual void AddResource(const Char* Name, IDeviceObject* pObject, bool bIsUnique) override final; + + /// Implementation of IResourceMapping::AddResourceArray() + virtual void AddResourceArray(const Char* Name, Uint32 StartIndex, IDeviceObject* const* ppObjects, Uint32 NumElements, bool bIsUnique) override final; + + /// Implementation of IResourceMapping::RemoveResourceByName() + virtual void RemoveResourceByName(const Char* Name, Uint32 ArrayIndex) override final; + + /// Implementation of IResourceMapping::GetResource() + virtual void GetResource(const Char* Name, IDeviceObject** ppResource, Uint32 ArrayIndex) override final; + + /// Returns number of resources in the resource mapping. + virtual size_t GetSize() override final; + +private: + ThreadingTools::LockHelper Lock(); + + ThreadingTools::LockFlag m_LockFlag; + typedef std::pair> HashTableElem; + std::unordered_map, std::hash, std::equal_to, STDAllocatorRawMem> m_HashTable; +}; +} // namespace Diligent diff --git a/Graphics/GraphicsEngine/include/SamplerBase.h b/Graphics/GraphicsEngine/include/SamplerBase.h deleted file mode 100644 index aa408556..00000000 --- a/Graphics/GraphicsEngine/include/SamplerBase.h +++ /dev/null @@ -1,74 +0,0 @@ -/* - * Copyright 2019-2020 Diligent Graphics LLC - * Copyright 2015-2019 Egor Yusov - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - * - * In no event and under no legal theory, whether in tort (including negligence), - * contract, or otherwise, unless required by applicable law (such as deliberate - * and grossly negligent acts) or agreed to in writing, shall any Contributor be - * liable for any damages, including any direct, indirect, special, incidental, - * or consequential damages of any character arising as a result of this License or - * out of the use or inability to use the software (including but not limited to damages - * for loss of goodwill, work stoppage, computer failure or malfunction, or any and - * all other commercial damages or losses), even if such Contributor has been advised - * of the possibility of such damages. - */ - -#pragma once - -/// \file -/// Implementation of the Diligent::SamplerBase template class - -#include "Sampler.h" -#include "DeviceObjectBase.h" -#include "RenderDeviceBase.h" - -namespace Diligent -{ - -/// Template class implementing base functionality for a sampler object. - -/// \tparam BaseInterface - base interface that this class will inheret -/// (Diligent::ISamplerD3D11, Diligent::ISamplerD3D12, -/// Diligent::ISamplerGL or Diligent::ISamplerVk). -/// \tparam RenderDeviceImplType - type of the render device implementation -/// (Diligent::RenderDeviceD3D11Impl, Diligent::RenderDeviceD3D12Impl, -/// Diligent::RenderDeviceGLImpl, or Diligent::RenderDeviceVkImpl) -template -class SamplerBase : public DeviceObjectBase -{ -public: - using TDeviceObjectBase = DeviceObjectBase; - - /// \param pRefCounters - reference counters object that controls the lifetime of this sampler. - /// \param pDevice - pointer to the device. - /// \param SamDesc - sampler description. - /// \param bIsDeviceInternal - flag indicating if the sampler is an internal device object and - /// must not keep a strong reference to the device. - SamplerBase(IReferenceCounters* pRefCounters, RenderDeviceImplType* pDevice, const SamplerDesc& SamDesc, bool bIsDeviceInternal = false) : - TDeviceObjectBase{pRefCounters, pDevice, SamDesc, bIsDeviceInternal} - {} - - ~SamplerBase() - { - /// \note Destructor cannot directly remove the object from the registry as this may cause a - /// deadlock. - auto& SamplerRegistry = this->GetDevice()->GetSamplerRegistry(); - SamplerRegistry.ReportDeletedObject(); - } - - IMPLEMENT_QUERY_INTERFACE_IN_PLACE(IID_Sampler, TDeviceObjectBase) -}; - -} // namespace Diligent diff --git a/Graphics/GraphicsEngine/include/SamplerBase.hpp b/Graphics/GraphicsEngine/include/SamplerBase.hpp new file mode 100644 index 00000000..4c9c505d --- /dev/null +++ b/Graphics/GraphicsEngine/include/SamplerBase.hpp @@ -0,0 +1,74 @@ +/* + * Copyright 2019-2020 Diligent Graphics LLC + * Copyright 2015-2019 Egor Yusov + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + * + * In no event and under no legal theory, whether in tort (including negligence), + * contract, or otherwise, unless required by applicable law (such as deliberate + * and grossly negligent acts) or agreed to in writing, shall any Contributor be + * liable for any damages, including any direct, indirect, special, incidental, + * or consequential damages of any character arising as a result of this License or + * out of the use or inability to use the software (including but not limited to damages + * for loss of goodwill, work stoppage, computer failure or malfunction, or any and + * all other commercial damages or losses), even if such Contributor has been advised + * of the possibility of such damages. + */ + +#pragma once + +/// \file +/// Implementation of the Diligent::SamplerBase template class + +#include "Sampler.h" +#include "DeviceObjectBase.hpp" +#include "RenderDeviceBase.hpp" + +namespace Diligent +{ + +/// Template class implementing base functionality for a sampler object. + +/// \tparam BaseInterface - base interface that this class will inheret +/// (Diligent::ISamplerD3D11, Diligent::ISamplerD3D12, +/// Diligent::ISamplerGL or Diligent::ISamplerVk). +/// \tparam RenderDeviceImplType - type of the render device implementation +/// (Diligent::RenderDeviceD3D11Impl, Diligent::RenderDeviceD3D12Impl, +/// Diligent::RenderDeviceGLImpl, or Diligent::RenderDeviceVkImpl) +template +class SamplerBase : public DeviceObjectBase +{ +public: + using TDeviceObjectBase = DeviceObjectBase; + + /// \param pRefCounters - reference counters object that controls the lifetime of this sampler. + /// \param pDevice - pointer to the device. + /// \param SamDesc - sampler description. + /// \param bIsDeviceInternal - flag indicating if the sampler is an internal device object and + /// must not keep a strong reference to the device. + SamplerBase(IReferenceCounters* pRefCounters, RenderDeviceImplType* pDevice, const SamplerDesc& SamDesc, bool bIsDeviceInternal = false) : + TDeviceObjectBase{pRefCounters, pDevice, SamDesc, bIsDeviceInternal} + {} + + ~SamplerBase() + { + /// \note Destructor cannot directly remove the object from the registry as this may cause a + /// deadlock. + auto& SamplerRegistry = this->GetDevice()->GetSamplerRegistry(); + SamplerRegistry.ReportDeletedObject(); + } + + IMPLEMENT_QUERY_INTERFACE_IN_PLACE(IID_Sampler, TDeviceObjectBase) +}; + +} // namespace Diligent diff --git a/Graphics/GraphicsEngine/include/ShaderBase.h b/Graphics/GraphicsEngine/include/ShaderBase.h deleted file mode 100644 index cf99cdc0..00000000 --- a/Graphics/GraphicsEngine/include/ShaderBase.h +++ /dev/null @@ -1,122 +0,0 @@ -/* - * Copyright 2019-2020 Diligent Graphics LLC - * Copyright 2015-2019 Egor Yusov - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - * - * In no event and under no legal theory, whether in tort (including negligence), - * contract, or otherwise, unless required by applicable law (such as deliberate - * and grossly negligent acts) or agreed to in writing, shall any Contributor be - * liable for any damages, including any direct, indirect, special, incidental, - * or consequential damages of any character arising as a result of this License or - * out of the use or inability to use the software (including but not limited to damages - * for loss of goodwill, work stoppage, computer failure or malfunction, or any and - * all other commercial damages or losses), even if such Contributor has been advised - * of the possibility of such damages. - */ - -#pragma once - -/// \file -/// Implementation of the Diligent::ShaderBase template class - -#include - -#include "Shader.h" -#include "DeviceObjectBase.h" -#include "STDAllocator.h" -#include "PlatformMisc.h" -#include "EngineMemory.h" -#include "Align.h" - -namespace Diligent -{ - -inline SHADER_TYPE GetShaderTypeFromIndex(Int32 Index) -{ - return static_cast(1 << Index); -} - -inline Int32 GetShaderTypeIndex(SHADER_TYPE Type) -{ - VERIFY(IsPowerOfTwo(Uint32{Type}), "Only single shader stage should be provided"); - - Int32 ShaderIndex = PlatformMisc::GetLSB(Type); - -#ifdef _DEBUG - switch (Type) - { - // clang-format off - case SHADER_TYPE_UNKNOWN: VERIFY_EXPR(ShaderIndex == -1); break; - case SHADER_TYPE_VERTEX: VERIFY_EXPR(ShaderIndex == 0); break; - case SHADER_TYPE_PIXEL: VERIFY_EXPR(ShaderIndex == 1); break; - case SHADER_TYPE_GEOMETRY:VERIFY_EXPR(ShaderIndex == 2); break; - case SHADER_TYPE_HULL: VERIFY_EXPR(ShaderIndex == 3); break; - case SHADER_TYPE_DOMAIN: VERIFY_EXPR(ShaderIndex == 4); break; - case SHADER_TYPE_COMPUTE: VERIFY_EXPR(ShaderIndex == 5); break; - // clang-format on - default: UNEXPECTED("Unexpected shader type (", Type, ")"); break; - } - VERIFY(Type == GetShaderTypeFromIndex(ShaderIndex), "Incorrect shader type index"); -#endif - return ShaderIndex; -} - -static const int VSInd = GetShaderTypeIndex(SHADER_TYPE_VERTEX); -static const int PSInd = GetShaderTypeIndex(SHADER_TYPE_PIXEL); -static const int GSInd = GetShaderTypeIndex(SHADER_TYPE_GEOMETRY); -static const int HSInd = GetShaderTypeIndex(SHADER_TYPE_HULL); -static const int DSInd = GetShaderTypeIndex(SHADER_TYPE_DOMAIN); -static const int CSInd = GetShaderTypeIndex(SHADER_TYPE_COMPUTE); - - -/// Template class implementing base functionality for a shader object - -/// \tparam BaseInterface - base interface that this class will inheret -/// (Diligent::IShaderD3D11, Diligent::IShaderD3D12, -/// Diligent::IShaderGL or Diligent::IShaderVk). -/// \tparam RenderDeviceImplType - type of the render device implementation -/// (Diligent::RenderDeviceD3D11Impl, Diligent::RenderDeviceD3D12Impl, -/// Diligent::RenderDeviceGLImpl, or Diligent::RenderDeviceVkImpl) -template -class ShaderBase : public DeviceObjectBase -{ -public: - using TDeviceObjectBase = DeviceObjectBase; - - /// \param pRefCounters - reference counters object that controls the lifetime of this shader. - /// \param pDevice - pointer to the device. - /// \param ShdrDesc - shader description. - /// \param bIsDeviceInternal - flag indicating if the shader is an internal device object and - /// must not keep a strong reference to the device. - ShaderBase(IReferenceCounters* pRefCounters, - RenderDeviceImplType* pDevice, - const ShaderDesc& ShdrDesc, - bool bIsDeviceInternal = false) : - TDeviceObjectBase{pRefCounters, pDevice, ShdrDesc, bIsDeviceInternal} - { - const auto& deviceFeatures = pDevice->GetDeviceCaps().Features; - if (ShdrDesc.ShaderType == SHADER_TYPE_GEOMETRY && !deviceFeatures.GeometryShaders) - LOG_ERROR_AND_THROW("Geometry shaders are not supported by this device"); - - if ((ShdrDesc.ShaderType == SHADER_TYPE_DOMAIN || ShdrDesc.ShaderType == SHADER_TYPE_HULL) && !deviceFeatures.Tessellation) - LOG_ERROR_AND_THROW("Tessellation shaders are not supported by this device"); - - if (ShdrDesc.ShaderType == SHADER_TYPE_COMPUTE && !deviceFeatures.ComputeShaders) - LOG_ERROR_AND_THROW("Compute shaders are not supported by this device"); - } - - IMPLEMENT_QUERY_INTERFACE_IN_PLACE(IID_Shader, TDeviceObjectBase) -}; - -} // namespace Diligent diff --git a/Graphics/GraphicsEngine/include/ShaderBase.hpp b/Graphics/GraphicsEngine/include/ShaderBase.hpp new file mode 100644 index 00000000..252f11ac --- /dev/null +++ b/Graphics/GraphicsEngine/include/ShaderBase.hpp @@ -0,0 +1,122 @@ +/* + * Copyright 2019-2020 Diligent Graphics LLC + * Copyright 2015-2019 Egor Yusov + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + * + * In no event and under no legal theory, whether in tort (including negligence), + * contract, or otherwise, unless required by applicable law (such as deliberate + * and grossly negligent acts) or agreed to in writing, shall any Contributor be + * liable for any damages, including any direct, indirect, special, incidental, + * or consequential damages of any character arising as a result of this License or + * out of the use or inability to use the software (including but not limited to damages + * for loss of goodwill, work stoppage, computer failure or malfunction, or any and + * all other commercial damages or losses), even if such Contributor has been advised + * of the possibility of such damages. + */ + +#pragma once + +/// \file +/// Implementation of the Diligent::ShaderBase template class + +#include + +#include "Shader.h" +#include "DeviceObjectBase.hpp" +#include "STDAllocator.h" +#include "PlatformMisc.h" +#include "EngineMemory.h" +#include "Align.h" + +namespace Diligent +{ + +inline SHADER_TYPE GetShaderTypeFromIndex(Int32 Index) +{ + return static_cast(1 << Index); +} + +inline Int32 GetShaderTypeIndex(SHADER_TYPE Type) +{ + VERIFY(IsPowerOfTwo(Uint32{Type}), "Only single shader stage should be provided"); + + Int32 ShaderIndex = PlatformMisc::GetLSB(Type); + +#ifdef _DEBUG + switch (Type) + { + // clang-format off + case SHADER_TYPE_UNKNOWN: VERIFY_EXPR(ShaderIndex == -1); break; + case SHADER_TYPE_VERTEX: VERIFY_EXPR(ShaderIndex == 0); break; + case SHADER_TYPE_PIXEL: VERIFY_EXPR(ShaderIndex == 1); break; + case SHADER_TYPE_GEOMETRY:VERIFY_EXPR(ShaderIndex == 2); break; + case SHADER_TYPE_HULL: VERIFY_EXPR(ShaderIndex == 3); break; + case SHADER_TYPE_DOMAIN: VERIFY_EXPR(ShaderIndex == 4); break; + case SHADER_TYPE_COMPUTE: VERIFY_EXPR(ShaderIndex == 5); break; + // clang-format on + default: UNEXPECTED("Unexpected shader type (", Type, ")"); break; + } + VERIFY(Type == GetShaderTypeFromIndex(ShaderIndex), "Incorrect shader type index"); +#endif + return ShaderIndex; +} + +static const int VSInd = GetShaderTypeIndex(SHADER_TYPE_VERTEX); +static const int PSInd = GetShaderTypeIndex(SHADER_TYPE_PIXEL); +static const int GSInd = GetShaderTypeIndex(SHADER_TYPE_GEOMETRY); +static const int HSInd = GetShaderTypeIndex(SHADER_TYPE_HULL); +static const int DSInd = GetShaderTypeIndex(SHADER_TYPE_DOMAIN); +static const int CSInd = GetShaderTypeIndex(SHADER_TYPE_COMPUTE); + + +/// Template class implementing base functionality for a shader object + +/// \tparam BaseInterface - base interface that this class will inheret +/// (Diligent::IShaderD3D11, Diligent::IShaderD3D12, +/// Diligent::IShaderGL or Diligent::IShaderVk). +/// \tparam RenderDeviceImplType - type of the render device implementation +/// (Diligent::RenderDeviceD3D11Impl, Diligent::RenderDeviceD3D12Impl, +/// Diligent::RenderDeviceGLImpl, or Diligent::RenderDeviceVkImpl) +template +class ShaderBase : public DeviceObjectBase +{ +public: + using TDeviceObjectBase = DeviceObjectBase; + + /// \param pRefCounters - reference counters object that controls the lifetime of this shader. + /// \param pDevice - pointer to the device. + /// \param ShdrDesc - shader description. + /// \param bIsDeviceInternal - flag indicating if the shader is an internal device object and + /// must not keep a strong reference to the device. + ShaderBase(IReferenceCounters* pRefCounters, + RenderDeviceImplType* pDevice, + const ShaderDesc& ShdrDesc, + bool bIsDeviceInternal = false) : + TDeviceObjectBase{pRefCounters, pDevice, ShdrDesc, bIsDeviceInternal} + { + const auto& deviceFeatures = pDevice->GetDeviceCaps().Features; + if (ShdrDesc.ShaderType == SHADER_TYPE_GEOMETRY && !deviceFeatures.GeometryShaders) + LOG_ERROR_AND_THROW("Geometry shaders are not supported by this device"); + + if ((ShdrDesc.ShaderType == SHADER_TYPE_DOMAIN || ShdrDesc.ShaderType == SHADER_TYPE_HULL) && !deviceFeatures.Tessellation) + LOG_ERROR_AND_THROW("Tessellation shaders are not supported by this device"); + + if (ShdrDesc.ShaderType == SHADER_TYPE_COMPUTE && !deviceFeatures.ComputeShaders) + LOG_ERROR_AND_THROW("Compute shaders are not supported by this device"); + } + + IMPLEMENT_QUERY_INTERFACE_IN_PLACE(IID_Shader, TDeviceObjectBase) +}; + +} // namespace Diligent diff --git a/Graphics/GraphicsEngine/include/ShaderResourceBindingBase.h b/Graphics/GraphicsEngine/include/ShaderResourceBindingBase.h deleted file mode 100644 index 9600294c..00000000 --- a/Graphics/GraphicsEngine/include/ShaderResourceBindingBase.h +++ /dev/null @@ -1,90 +0,0 @@ -/* - * Copyright 2019-2020 Diligent Graphics LLC - * Copyright 2015-2019 Egor Yusov - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - * - * In no event and under no legal theory, whether in tort (including negligence), - * contract, or otherwise, unless required by applicable law (such as deliberate - * and grossly negligent acts) or agreed to in writing, shall any Contributor be - * liable for any damages, including any direct, indirect, special, incidental, - * or consequential damages of any character arising as a result of this License or - * out of the use or inability to use the software (including but not limited to damages - * for loss of goodwill, work stoppage, computer failure or malfunction, or any and - * all other commercial damages or losses), even if such Contributor has been advised - * of the possibility of such damages. - */ - -#pragma once - -/// \file -/// Implementation of the Diligent::ShaderResourceBindingBase template class - -#include "ShaderResourceBinding.h" -#include "ObjectBase.h" -#include "GraphicsTypes.h" -#include "RefCntAutoPtr.h" - -namespace Diligent -{ - -/// Template class implementing base functionality for a shader resource binding - -/// \tparam BaseInterface - base interface that this class will inheret -/// (Diligent::IShaderResourceBindingGL, Diligent::IShaderResourceBindingD3D11, -/// Diligent::IShaderResourceBindingD3D12 or Diligent::IShaderResourceBindingVk). -template -class ShaderResourceBindingBase : public ObjectBase -{ -public: - typedef ObjectBase TObjectBase; - - /// \param pRefCounters - reference counters object that controls the lifetime of this SRB. - /// \param pPSO - pipeline state that this SRB belongs to. - /// \param IsInternal - flag indicating if the shader resource binding is an internal PSO object and - /// must not keep a strong reference to the PSO. - ShaderResourceBindingBase(IReferenceCounters* pRefCounters, IPipelineState* pPSO, bool IsInternal = false) : - TObjectBase{pRefCounters}, - m_spPSO{IsInternal ? nullptr : pPSO}, - m_pPSO{pPSO} - {} - - IMPLEMENT_QUERY_INTERFACE_IN_PLACE(IID_ShaderResourceBinding, TObjectBase) - - /// Implementation of IShaderResourceBinding::GetPipelineState(). - virtual IPipelineState* GetPipelineState() override final - { - return m_pPSO; - } - - template - PSOType* GetPipelineState() - { - return ValidatedCast(m_pPSO); - } - - template - PSOType* GetPipelineState() const - { - return ValidatedCast(m_pPSO); - } - -protected: - /// Strong reference to PSO. We must use strong reference, because - /// shader resource binding uses PSO's memory allocator to allocate - /// memory for shader resource cache. - RefCntAutoPtr m_spPSO; - IPipelineState* const m_pPSO; -}; - -} // namespace Diligent diff --git a/Graphics/GraphicsEngine/include/ShaderResourceBindingBase.hpp b/Graphics/GraphicsEngine/include/ShaderResourceBindingBase.hpp new file mode 100644 index 00000000..9600294c --- /dev/null +++ b/Graphics/GraphicsEngine/include/ShaderResourceBindingBase.hpp @@ -0,0 +1,90 @@ +/* + * Copyright 2019-2020 Diligent Graphics LLC + * Copyright 2015-2019 Egor Yusov + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + * + * In no event and under no legal theory, whether in tort (including negligence), + * contract, or otherwise, unless required by applicable law (such as deliberate + * and grossly negligent acts) or agreed to in writing, shall any Contributor be + * liable for any damages, including any direct, indirect, special, incidental, + * or consequential damages of any character arising as a result of this License or + * out of the use or inability to use the software (including but not limited to damages + * for loss of goodwill, work stoppage, computer failure or malfunction, or any and + * all other commercial damages or losses), even if such Contributor has been advised + * of the possibility of such damages. + */ + +#pragma once + +/// \file +/// Implementation of the Diligent::ShaderResourceBindingBase template class + +#include "ShaderResourceBinding.h" +#include "ObjectBase.h" +#include "GraphicsTypes.h" +#include "RefCntAutoPtr.h" + +namespace Diligent +{ + +/// Template class implementing base functionality for a shader resource binding + +/// \tparam BaseInterface - base interface that this class will inheret +/// (Diligent::IShaderResourceBindingGL, Diligent::IShaderResourceBindingD3D11, +/// Diligent::IShaderResourceBindingD3D12 or Diligent::IShaderResourceBindingVk). +template +class ShaderResourceBindingBase : public ObjectBase +{ +public: + typedef ObjectBase TObjectBase; + + /// \param pRefCounters - reference counters object that controls the lifetime of this SRB. + /// \param pPSO - pipeline state that this SRB belongs to. + /// \param IsInternal - flag indicating if the shader resource binding is an internal PSO object and + /// must not keep a strong reference to the PSO. + ShaderResourceBindingBase(IReferenceCounters* pRefCounters, IPipelineState* pPSO, bool IsInternal = false) : + TObjectBase{pRefCounters}, + m_spPSO{IsInternal ? nullptr : pPSO}, + m_pPSO{pPSO} + {} + + IMPLEMENT_QUERY_INTERFACE_IN_PLACE(IID_ShaderResourceBinding, TObjectBase) + + /// Implementation of IShaderResourceBinding::GetPipelineState(). + virtual IPipelineState* GetPipelineState() override final + { + return m_pPSO; + } + + template + PSOType* GetPipelineState() + { + return ValidatedCast(m_pPSO); + } + + template + PSOType* GetPipelineState() const + { + return ValidatedCast(m_pPSO); + } + +protected: + /// Strong reference to PSO. We must use strong reference, because + /// shader resource binding uses PSO's memory allocator to allocate + /// memory for shader resource cache. + RefCntAutoPtr m_spPSO; + IPipelineState* const m_pPSO; +}; + +} // namespace Diligent diff --git a/Graphics/GraphicsEngine/include/ShaderResourceVariableBase.h b/Graphics/GraphicsEngine/include/ShaderResourceVariableBase.h deleted file mode 100644 index 4e8f49e0..00000000 --- a/Graphics/GraphicsEngine/include/ShaderResourceVariableBase.h +++ /dev/null @@ -1,379 +0,0 @@ -/* - * Copyright 2019-2020 Diligent Graphics LLC - * Copyright 2015-2019 Egor Yusov - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - * - * In no event and under no legal theory, whether in tort (including negligence), - * contract, or otherwise, unless required by applicable law (such as deliberate - * and grossly negligent acts) or agreed to in writing, shall any Contributor be - * liable for any damages, including any direct, indirect, special, incidental, - * or consequential damages of any character arising as a result of this License or - * out of the use or inability to use the software (including but not limited to damages - * for loss of goodwill, work stoppage, computer failure or malfunction, or any and - * all other commercial damages or losses), even if such Contributor has been advised - * of the possibility of such damages. - */ - -#pragma once - -/// \file -/// Implementation of the Diligent::ShaderBase template class - -#include - -#include "ShaderResourceVariable.h" -#include "PipelineState.h" -#include "StringTools.h" -#include "GraphicsAccessories.hpp" - -namespace Diligent -{ - -template -SHADER_RESOURCE_VARIABLE_TYPE GetShaderVariableType(SHADER_TYPE ShaderStage, - SHADER_RESOURCE_VARIABLE_TYPE DefaultVariableType, - const ShaderResourceVariableDesc* Variables, - Uint32 NumVars, - TNameCompare NameCompare) -{ - for (Uint32 v = 0; v < NumVars; ++v) - { - const auto& CurrVarDesc = Variables[v]; - if (((CurrVarDesc.ShaderStages & ShaderStage) != 0) && NameCompare(CurrVarDesc.Name)) - { - return CurrVarDesc.Type; - } - } - return DefaultVariableType; -} - -inline SHADER_RESOURCE_VARIABLE_TYPE GetShaderVariableType(SHADER_TYPE ShaderStage, - const Char* Name, - SHADER_RESOURCE_VARIABLE_TYPE DefaultVariableType, - const ShaderResourceVariableDesc* Variables, - Uint32 NumVars) -{ - return GetShaderVariableType(ShaderStage, DefaultVariableType, Variables, NumVars, - [&](const char* VarName) // - { - return strcmp(VarName, Name) == 0; - }); -} - -inline SHADER_RESOURCE_VARIABLE_TYPE GetShaderVariableType(SHADER_TYPE ShaderStage, - const Char* Name, - const PipelineResourceLayoutDesc& LayoutDesc) -{ - return GetShaderVariableType(ShaderStage, Name, LayoutDesc.DefaultVariableType, LayoutDesc.Variables, LayoutDesc.NumVariables); -} - -inline SHADER_RESOURCE_VARIABLE_TYPE GetShaderVariableType(SHADER_TYPE ShaderStage, - const String& Name, - SHADER_RESOURCE_VARIABLE_TYPE DefaultVariableType, - const ShaderResourceVariableDesc* Variables, - Uint32 NumVars) -{ - return GetShaderVariableType(ShaderStage, DefaultVariableType, Variables, NumVars, - [&](const char* VarName) // - { - return Name.compare(VarName) == 0; - }); -} - -inline SHADER_RESOURCE_VARIABLE_TYPE GetShaderVariableType(SHADER_TYPE ShaderStage, - const String& Name, - const PipelineResourceLayoutDesc& LayoutDesc) -{ - return GetShaderVariableType(ShaderStage, Name, LayoutDesc.DefaultVariableType, LayoutDesc.Variables, LayoutDesc.NumVariables); -} - -inline bool IsAllowedType(SHADER_RESOURCE_VARIABLE_TYPE VarType, Uint32 AllowedTypeBits) noexcept -{ - return ((1 << VarType) & AllowedTypeBits) != 0; -} - -inline Uint32 GetAllowedTypeBits(const SHADER_RESOURCE_VARIABLE_TYPE* AllowedVarTypes, Uint32 NumAllowedTypes) noexcept -{ - if (AllowedVarTypes == nullptr) - return 0xFFFFFFFF; - - Uint32 AllowedTypeBits = 0; - for (Uint32 i = 0; i < NumAllowedTypes; ++i) - AllowedTypeBits |= 1 << AllowedVarTypes[i]; - return AllowedTypeBits; -} - -inline Int32 FindStaticSampler(const StaticSamplerDesc* StaticSamplers, - Uint32 NumStaticSamplers, - SHADER_TYPE ShaderType, - const char* ResourceName, - const char* SamplerSuffix) -{ - for (Uint32 s = 0; s < NumStaticSamplers; ++s) - { - const auto& StSam = StaticSamplers[s]; - if (((StSam.ShaderStages & ShaderType) != 0) && StreqSuff(ResourceName, StSam.SamplerOrTextureName, SamplerSuffix)) - return s; - } - - return -1; -} - - - - -template -bool VerifyConstantBufferBinding(const ResourceAttribsType& Attribs, - SHADER_RESOURCE_VARIABLE_TYPE VarType, - Uint32 ArrayIndex, - const IDeviceObject* pBuffer, - const BufferImplType* pBufferImpl, - const IDeviceObject* pCachedBuffer, - const char* ShaderName = nullptr) -{ - if (pBuffer != nullptr && pBufferImpl == nullptr) - { - std::stringstream ss; - ss << "Failed to bind resource '" << pBuffer->GetDesc().Name << "' to variable '" << Attribs.GetPrintName(ArrayIndex) << '\''; - if (ShaderName != nullptr) - { - ss << " in shader '" << ShaderName << '\''; - } - ss << ". Invalid resource type: buffer is expected."; - LOG_ERROR_MESSAGE(ss.str()); - return false; - } - - bool BindingOK = true; - if (pBufferImpl != nullptr && (pBufferImpl->GetDesc().BindFlags & BIND_UNIFORM_BUFFER) == 0) - { - std::stringstream ss; - ss << "Error binding buffer '" << pBufferImpl->GetDesc().Name << "' to variable '" << Attribs.GetPrintName(ArrayIndex) << '\''; - if (ShaderName != nullptr) - { - ss << " in shader '" << ShaderName << '\''; - } - ss << ". The buffer was not created with BIND_UNIFORM_BUFFER flag."; - LOG_ERROR_MESSAGE(ss.str()); - BindingOK = false; - } - - if (VarType != SHADER_RESOURCE_VARIABLE_TYPE_DYNAMIC && pCachedBuffer != nullptr && pCachedBuffer != pBufferImpl) - { - auto VarTypeStr = GetShaderVariableTypeLiteralName(VarType); - - std::stringstream ss; - ss << "Non-null constant (uniform) buffer '" << pCachedBuffer->GetDesc().Name << "' is already bound to " << VarTypeStr - << " shader variable '" << Attribs.GetPrintName(ArrayIndex) << '\''; - if (ShaderName != nullptr) - { - ss << " in shader '" << ShaderName << '\''; - } - ss << ". Attempting to bind "; - if (pBufferImpl) - { - ss << "another resource ('" << pBufferImpl->GetDesc().Name << "')"; - } - else - { - ss << "null"; - } - ss << " is an error and may cause unpredicted behavior. Use another shader resource binding instance or label the variable as dynamic."; - LOG_ERROR_MESSAGE(ss.str()); - - BindingOK = false; - } - - return BindingOK; -} - -template -const char* GetResourceTypeName(); - -template <> -inline const char* GetResourceTypeName() -{ - return "texture view"; -} - -template <> -inline const char* GetResourceTypeName() -{ - return "buffer view"; -} - -template -bool VerifyResourceViewBinding(const ResourceAttribsType& Attribs, - SHADER_RESOURCE_VARIABLE_TYPE VarType, - Uint32 ArrayIndex, - const IDeviceObject* pView, - const ResourceViewImplType* pViewImpl, - std::initializer_list ExpectedViewTypes, - const IDeviceObject* pCachedView, - const char* ShaderName = nullptr) -{ - const char* ExpectedResourceType = GetResourceTypeName(); - - if (pView && !pViewImpl) - { - std::stringstream ss; - ss << "Failed to bind resource '" << pView->GetDesc().Name << "' to variable '" << Attribs.GetPrintName(ArrayIndex) << '\''; - if (ShaderName != nullptr) - { - ss << " in shader '" << ShaderName << '\''; - } - ss << ". Invalid resource type: " << ExpectedResourceType << " is expected."; - LOG_ERROR_MESSAGE(ss.str()); - return false; - } - - bool BindingOK = true; - if (pViewImpl) - { - auto ViewType = pViewImpl->GetDesc().ViewType; - bool IsExpectedViewType = false; - for (auto ExpectedViewType : ExpectedViewTypes) - { - if (ExpectedViewType == ViewType) - IsExpectedViewType = true; - } - - if (!IsExpectedViewType) - { - std::string ExpectedViewTypeName; - - std::stringstream ss; - ss << "Error binding " << ExpectedResourceType << " '" << pViewImpl->GetDesc().Name << "' to variable '" - << Attribs.GetPrintName(ArrayIndex) << '\''; - if (ShaderName != nullptr) - { - ss << " in shader '" << ShaderName << '\''; - } - ss << ". Incorrect view type: "; - bool IsFirstViewType = true; - for (auto ExpectedViewType : ExpectedViewTypes) - { - if (!IsFirstViewType) - { - ss << " or "; - } - ss << GetViewTypeLiteralName(ExpectedViewType); - IsFirstViewType = false; - } - ss << " is expected, " << GetViewTypeLiteralName(ViewType) << " is provided."; - LOG_ERROR_MESSAGE(ss.str()); - - BindingOK = false; - } - } - - if (VarType != SHADER_RESOURCE_VARIABLE_TYPE_DYNAMIC && pCachedView != nullptr && pCachedView != pViewImpl) - { - auto VarTypeStr = GetShaderVariableTypeLiteralName(VarType); - std::stringstream ss; - ss << "Non-null resource '" << pCachedView->GetDesc().Name << "' is already bound to " << VarTypeStr - << " shader variable '" << Attribs.GetPrintName(ArrayIndex) << '\''; - if (ShaderName != nullptr) - { - ss << " in shader '" << ShaderName << '\''; - } - ss << ". Attempting to bind "; - if (pViewImpl) - { - ss << "another resource ('" << pViewImpl->GetDesc().Name << "')"; - } - else - { - ss << "null"; - } - ss << " is an error and may cause unpredicted behavior. Use another shader resource binding instance or label the variable as dynamic."; - LOG_ERROR_MESSAGE(ss.str()); - - BindingOK = false; - } - return BindingOK; -} - -inline void VerifyAndCorrectSetArrayArguments(const char* Name, Uint32 ArraySize, Uint32& FirstElement, Uint32& NumElements) -{ - if (FirstElement >= ArraySize) - { - LOG_ERROR_MESSAGE("SetArray arguments are invalid for '", Name, "' variable: FirstElement (", FirstElement, ") is out of allowed range 0 .. ", ArraySize - 1); - FirstElement = ArraySize - 1; - NumElements = 0; - } - - if (FirstElement + NumElements > ArraySize) - { - LOG_ERROR_MESSAGE("SetArray arguments are invalid for '", Name, "' variable: specified element range (", FirstElement, " .. ", - FirstElement + NumElements - 1, ") is out of array bounds 0 .. ", ArraySize - 1); - NumElements = ArraySize - FirstElement; - } -} - -struct DefaultShaderVariableIDComparator -{ - bool operator()(const INTERFACE_ID& IID) const - { - return IID == IID_ShaderResourceVariable || IID == IID_Unknown; - } -}; - -/// Base implementation of a shader variable -template -struct ShaderVariableBase : public ResourceVariableBaseInterface -{ - ShaderVariableBase(ResourceLayoutType& ParentResLayout) : - m_ParentResLayout(ParentResLayout) - { - } - - void QueryInterface(const INTERFACE_ID& IID, IObject** ppInterface) override final - { - if (ppInterface == nullptr) - return; - - *ppInterface = nullptr; - if (VariableIDComparator{}(IID)) - { - *ppInterface = this; - (*ppInterface)->AddRef(); - } - } - - virtual Atomics::Long AddRef() override final - { - return m_ParentResLayout.GetOwner().AddRef(); - } - - virtual Atomics::Long Release() override final - { - return m_ParentResLayout.GetOwner().Release(); - } - - virtual IReferenceCounters* GetReferenceCounters() const override final - { - return m_ParentResLayout.GetOwner().GetReferenceCounters(); - } - -protected: - ResourceLayoutType& m_ParentResLayout; -}; - -} // namespace Diligent diff --git a/Graphics/GraphicsEngine/include/ShaderResourceVariableBase.hpp b/Graphics/GraphicsEngine/include/ShaderResourceVariableBase.hpp new file mode 100644 index 00000000..4e8f49e0 --- /dev/null +++ b/Graphics/GraphicsEngine/include/ShaderResourceVariableBase.hpp @@ -0,0 +1,379 @@ +/* + * Copyright 2019-2020 Diligent Graphics LLC + * Copyright 2015-2019 Egor Yusov + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + * + * In no event and under no legal theory, whether in tort (including negligence), + * contract, or otherwise, unless required by applicable law (such as deliberate + * and grossly negligent acts) or agreed to in writing, shall any Contributor be + * liable for any damages, including any direct, indirect, special, incidental, + * or consequential damages of any character arising as a result of this License or + * out of the use or inability to use the software (including but not limited to damages + * for loss of goodwill, work stoppage, computer failure or malfunction, or any and + * all other commercial damages or losses), even if such Contributor has been advised + * of the possibility of such damages. + */ + +#pragma once + +/// \file +/// Implementation of the Diligent::ShaderBase template class + +#include + +#include "ShaderResourceVariable.h" +#include "PipelineState.h" +#include "StringTools.h" +#include "GraphicsAccessories.hpp" + +namespace Diligent +{ + +template +SHADER_RESOURCE_VARIABLE_TYPE GetShaderVariableType(SHADER_TYPE ShaderStage, + SHADER_RESOURCE_VARIABLE_TYPE DefaultVariableType, + const ShaderResourceVariableDesc* Variables, + Uint32 NumVars, + TNameCompare NameCompare) +{ + for (Uint32 v = 0; v < NumVars; ++v) + { + const auto& CurrVarDesc = Variables[v]; + if (((CurrVarDesc.ShaderStages & ShaderStage) != 0) && NameCompare(CurrVarDesc.Name)) + { + return CurrVarDesc.Type; + } + } + return DefaultVariableType; +} + +inline SHADER_RESOURCE_VARIABLE_TYPE GetShaderVariableType(SHADER_TYPE ShaderStage, + const Char* Name, + SHADER_RESOURCE_VARIABLE_TYPE DefaultVariableType, + const ShaderResourceVariableDesc* Variables, + Uint32 NumVars) +{ + return GetShaderVariableType(ShaderStage, DefaultVariableType, Variables, NumVars, + [&](const char* VarName) // + { + return strcmp(VarName, Name) == 0; + }); +} + +inline SHADER_RESOURCE_VARIABLE_TYPE GetShaderVariableType(SHADER_TYPE ShaderStage, + const Char* Name, + const PipelineResourceLayoutDesc& LayoutDesc) +{ + return GetShaderVariableType(ShaderStage, Name, LayoutDesc.DefaultVariableType, LayoutDesc.Variables, LayoutDesc.NumVariables); +} + +inline SHADER_RESOURCE_VARIABLE_TYPE GetShaderVariableType(SHADER_TYPE ShaderStage, + const String& Name, + SHADER_RESOURCE_VARIABLE_TYPE DefaultVariableType, + const ShaderResourceVariableDesc* Variables, + Uint32 NumVars) +{ + return GetShaderVariableType(ShaderStage, DefaultVariableType, Variables, NumVars, + [&](const char* VarName) // + { + return Name.compare(VarName) == 0; + }); +} + +inline SHADER_RESOURCE_VARIABLE_TYPE GetShaderVariableType(SHADER_TYPE ShaderStage, + const String& Name, + const PipelineResourceLayoutDesc& LayoutDesc) +{ + return GetShaderVariableType(ShaderStage, Name, LayoutDesc.DefaultVariableType, LayoutDesc.Variables, LayoutDesc.NumVariables); +} + +inline bool IsAllowedType(SHADER_RESOURCE_VARIABLE_TYPE VarType, Uint32 AllowedTypeBits) noexcept +{ + return ((1 << VarType) & AllowedTypeBits) != 0; +} + +inline Uint32 GetAllowedTypeBits(const SHADER_RESOURCE_VARIABLE_TYPE* AllowedVarTypes, Uint32 NumAllowedTypes) noexcept +{ + if (AllowedVarTypes == nullptr) + return 0xFFFFFFFF; + + Uint32 AllowedTypeBits = 0; + for (Uint32 i = 0; i < NumAllowedTypes; ++i) + AllowedTypeBits |= 1 << AllowedVarTypes[i]; + return AllowedTypeBits; +} + +inline Int32 FindStaticSampler(const StaticSamplerDesc* StaticSamplers, + Uint32 NumStaticSamplers, + SHADER_TYPE ShaderType, + const char* ResourceName, + const char* SamplerSuffix) +{ + for (Uint32 s = 0; s < NumStaticSamplers; ++s) + { + const auto& StSam = StaticSamplers[s]; + if (((StSam.ShaderStages & ShaderType) != 0) && StreqSuff(ResourceName, StSam.SamplerOrTextureName, SamplerSuffix)) + return s; + } + + return -1; +} + + + + +template +bool VerifyConstantBufferBinding(const ResourceAttribsType& Attribs, + SHADER_RESOURCE_VARIABLE_TYPE VarType, + Uint32 ArrayIndex, + const IDeviceObject* pBuffer, + const BufferImplType* pBufferImpl, + const IDeviceObject* pCachedBuffer, + const char* ShaderName = nullptr) +{ + if (pBuffer != nullptr && pBufferImpl == nullptr) + { + std::stringstream ss; + ss << "Failed to bind resource '" << pBuffer->GetDesc().Name << "' to variable '" << Attribs.GetPrintName(ArrayIndex) << '\''; + if (ShaderName != nullptr) + { + ss << " in shader '" << ShaderName << '\''; + } + ss << ". Invalid resource type: buffer is expected."; + LOG_ERROR_MESSAGE(ss.str()); + return false; + } + + bool BindingOK = true; + if (pBufferImpl != nullptr && (pBufferImpl->GetDesc().BindFlags & BIND_UNIFORM_BUFFER) == 0) + { + std::stringstream ss; + ss << "Error binding buffer '" << pBufferImpl->GetDesc().Name << "' to variable '" << Attribs.GetPrintName(ArrayIndex) << '\''; + if (ShaderName != nullptr) + { + ss << " in shader '" << ShaderName << '\''; + } + ss << ". The buffer was not created with BIND_UNIFORM_BUFFER flag."; + LOG_ERROR_MESSAGE(ss.str()); + BindingOK = false; + } + + if (VarType != SHADER_RESOURCE_VARIABLE_TYPE_DYNAMIC && pCachedBuffer != nullptr && pCachedBuffer != pBufferImpl) + { + auto VarTypeStr = GetShaderVariableTypeLiteralName(VarType); + + std::stringstream ss; + ss << "Non-null constant (uniform) buffer '" << pCachedBuffer->GetDesc().Name << "' is already bound to " << VarTypeStr + << " shader variable '" << Attribs.GetPrintName(ArrayIndex) << '\''; + if (ShaderName != nullptr) + { + ss << " in shader '" << ShaderName << '\''; + } + ss << ". Attempting to bind "; + if (pBufferImpl) + { + ss << "another resource ('" << pBufferImpl->GetDesc().Name << "')"; + } + else + { + ss << "null"; + } + ss << " is an error and may cause unpredicted behavior. Use another shader resource binding instance or label the variable as dynamic."; + LOG_ERROR_MESSAGE(ss.str()); + + BindingOK = false; + } + + return BindingOK; +} + +template +const char* GetResourceTypeName(); + +template <> +inline const char* GetResourceTypeName() +{ + return "texture view"; +} + +template <> +inline const char* GetResourceTypeName() +{ + return "buffer view"; +} + +template +bool VerifyResourceViewBinding(const ResourceAttribsType& Attribs, + SHADER_RESOURCE_VARIABLE_TYPE VarType, + Uint32 ArrayIndex, + const IDeviceObject* pView, + const ResourceViewImplType* pViewImpl, + std::initializer_list ExpectedViewTypes, + const IDeviceObject* pCachedView, + const char* ShaderName = nullptr) +{ + const char* ExpectedResourceType = GetResourceTypeName(); + + if (pView && !pViewImpl) + { + std::stringstream ss; + ss << "Failed to bind resource '" << pView->GetDesc().Name << "' to variable '" << Attribs.GetPrintName(ArrayIndex) << '\''; + if (ShaderName != nullptr) + { + ss << " in shader '" << ShaderName << '\''; + } + ss << ". Invalid resource type: " << ExpectedResourceType << " is expected."; + LOG_ERROR_MESSAGE(ss.str()); + return false; + } + + bool BindingOK = true; + if (pViewImpl) + { + auto ViewType = pViewImpl->GetDesc().ViewType; + bool IsExpectedViewType = false; + for (auto ExpectedViewType : ExpectedViewTypes) + { + if (ExpectedViewType == ViewType) + IsExpectedViewType = true; + } + + if (!IsExpectedViewType) + { + std::string ExpectedViewTypeName; + + std::stringstream ss; + ss << "Error binding " << ExpectedResourceType << " '" << pViewImpl->GetDesc().Name << "' to variable '" + << Attribs.GetPrintName(ArrayIndex) << '\''; + if (ShaderName != nullptr) + { + ss << " in shader '" << ShaderName << '\''; + } + ss << ". Incorrect view type: "; + bool IsFirstViewType = true; + for (auto ExpectedViewType : ExpectedViewTypes) + { + if (!IsFirstViewType) + { + ss << " or "; + } + ss << GetViewTypeLiteralName(ExpectedViewType); + IsFirstViewType = false; + } + ss << " is expected, " << GetViewTypeLiteralName(ViewType) << " is provided."; + LOG_ERROR_MESSAGE(ss.str()); + + BindingOK = false; + } + } + + if (VarType != SHADER_RESOURCE_VARIABLE_TYPE_DYNAMIC && pCachedView != nullptr && pCachedView != pViewImpl) + { + auto VarTypeStr = GetShaderVariableTypeLiteralName(VarType); + std::stringstream ss; + ss << "Non-null resource '" << pCachedView->GetDesc().Name << "' is already bound to " << VarTypeStr + << " shader variable '" << Attribs.GetPrintName(ArrayIndex) << '\''; + if (ShaderName != nullptr) + { + ss << " in shader '" << ShaderName << '\''; + } + ss << ". Attempting to bind "; + if (pViewImpl) + { + ss << "another resource ('" << pViewImpl->GetDesc().Name << "')"; + } + else + { + ss << "null"; + } + ss << " is an error and may cause unpredicted behavior. Use another shader resource binding instance or label the variable as dynamic."; + LOG_ERROR_MESSAGE(ss.str()); + + BindingOK = false; + } + return BindingOK; +} + +inline void VerifyAndCorrectSetArrayArguments(const char* Name, Uint32 ArraySize, Uint32& FirstElement, Uint32& NumElements) +{ + if (FirstElement >= ArraySize) + { + LOG_ERROR_MESSAGE("SetArray arguments are invalid for '", Name, "' variable: FirstElement (", FirstElement, ") is out of allowed range 0 .. ", ArraySize - 1); + FirstElement = ArraySize - 1; + NumElements = 0; + } + + if (FirstElement + NumElements > ArraySize) + { + LOG_ERROR_MESSAGE("SetArray arguments are invalid for '", Name, "' variable: specified element range (", FirstElement, " .. ", + FirstElement + NumElements - 1, ") is out of array bounds 0 .. ", ArraySize - 1); + NumElements = ArraySize - FirstElement; + } +} + +struct DefaultShaderVariableIDComparator +{ + bool operator()(const INTERFACE_ID& IID) const + { + return IID == IID_ShaderResourceVariable || IID == IID_Unknown; + } +}; + +/// Base implementation of a shader variable +template +struct ShaderVariableBase : public ResourceVariableBaseInterface +{ + ShaderVariableBase(ResourceLayoutType& ParentResLayout) : + m_ParentResLayout(ParentResLayout) + { + } + + void QueryInterface(const INTERFACE_ID& IID, IObject** ppInterface) override final + { + if (ppInterface == nullptr) + return; + + *ppInterface = nullptr; + if (VariableIDComparator{}(IID)) + { + *ppInterface = this; + (*ppInterface)->AddRef(); + } + } + + virtual Atomics::Long AddRef() override final + { + return m_ParentResLayout.GetOwner().AddRef(); + } + + virtual Atomics::Long Release() override final + { + return m_ParentResLayout.GetOwner().Release(); + } + + virtual IReferenceCounters* GetReferenceCounters() const override final + { + return m_ParentResLayout.GetOwner().GetReferenceCounters(); + } + +protected: + ResourceLayoutType& m_ParentResLayout; +}; + +} // namespace Diligent diff --git a/Graphics/GraphicsEngine/include/StateObjectsRegistry.h b/Graphics/GraphicsEngine/include/StateObjectsRegistry.h deleted file mode 100644 index f3484bc1..00000000 --- a/Graphics/GraphicsEngine/include/StateObjectsRegistry.h +++ /dev/null @@ -1,213 +0,0 @@ -/* - * Copyright 2019-2020 Diligent Graphics LLC - * Copyright 2015-2019 Egor Yusov - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - * - * In no event and under no legal theory, whether in tort (including negligence), - * contract, or otherwise, unless required by applicable law (such as deliberate - * and grossly negligent acts) or agreed to in writing, shall any Contributor be - * liable for any damages, including any direct, indirect, special, incidental, - * or consequential damages of any character arising as a result of this License or - * out of the use or inability to use the software (including but not limited to damages - * for loss of goodwill, work stoppage, computer failure or malfunction, or any and - * all other commercial damages or losses), even if such Contributor has been advised - * of the possibility of such damages. - */ - -#pragma once - -/// \file -/// Implementation of the Diligent::StateObjectsRegistry template class - -#include "DeviceObject.h" -#include -#include "STDAllocator.h" - -namespace Diligent -{ -/// Template class implementing state object registry - -/// \tparam ResourceDescType - type of the resource description. The type must have -/// operator== and a hash function defined. -/// -/// \remarks -/// The following strategies do not work: -/// - To store raw pointers and remove the object from the registry in the -/// object's destructor. In this case other thread may obtain a reference -/// to the object while it is being deleted. This scenario is possible if one thread -/// has just entered the destructor, but other is executing Find() and entered -/// the protection section. -/// - Strong pointers will cause circular references and result in memory leaks. -/// \remarks -/// Only weak pointers provide thread-safe solution. The object is either atomically destroyed, -/// so that no other thread can obtain a reference to it through weak pointers. Or it is atomically -/// locked, so that strong reference is obtained. In this case no other thread can destroy the object, -/// because there is at least one strong reference now. Note however that removing the object from the -/// registry in the object's destructor may cause a deadlock at the point where Find() locks the weak pointer: -/// if other thread has started dtor, the object will be locked by Diligent::RefCountedObject::Release(). -/// If after that this thread locks the registry first, it will be waiting for the object to unlock in -/// Diligent::RefCntWeakPtr::Lock(), while the dtor thread will be waiting for the registry to unlock. -template -class StateObjectsRegistry -{ -public: - /// Number of outstanding deleted objects to purge the registry. - static constexpr int DeletedObjectsToPurge = 32; - - StateObjectsRegistry(IMemoryAllocator& RawAllocator, const Char* RegistryName) : - m_DescToObjHashMap(STD_ALLOCATOR_RAW_MEM(HashMapElem, RawAllocator, "Allocator for unordered_map >")), - m_RegistryName{RegistryName} - {} - - ~StateObjectsRegistry() - { - // Object registry is part of the device, and every device - // object holds a strong reference to the device. So device - // is destroyed after all device objects are destroyed, and there - // may only be expired references in the registry. After we - // purge it, the registry must be empty. - Purge(); - VERIFY(m_DescToObjHashMap.empty(), "DescToObjHashMap is not empty"); - } - - /// Adds a new object to the registry - - /// \param [in] ObjectDesc - object description. - /// \param [in] pObject - pointer to the object. - /// - /// Besides adding a new object, the function also checks the number of - /// outstanding deleted objects and calls Purge() if the number has reached - /// the threshold value DeletedObjectsToPurge. Creating a state object is - /// assumed to be an expensive operation and should be performed during - /// the initialization. Occasional purge operations should not add significant - /// cost to it. - void Add(const ResourceDescType& ObjectDesc, IDeviceObject* pObject) - { - ThreadingTools::LockHelper Lock(m_LockFlag); - - // If the number of outstanding deleted objects reached the threshold value, - // purge the registry. Since we have exclusive access now, it is safe - // to do. - if (m_NumDeletedObjects >= DeletedObjectsToPurge) - { - Purge(); - m_NumDeletedObjects = 0; - } - - // Try to construct the new element in place - auto Elems = m_DescToObjHashMap.emplace(std::make_pair(ObjectDesc, Diligent::RefCntWeakPtr(pObject))); - // It is theorertically possible that the same object can be found - // in the registry. This might happen if two threads try to create - // the same object at the same time. They both will not find the - // object and then will create and try to add it. - // - // If the object already exists, we replace the existing reference. - // This is safer as there might be scenarios where existing reference - // might be expired. For instance, two threads try to create the same - // object which is not in the registry. The first thread creates - // the object, adds it to the registry and then releases it. After that - // the second thread creates the same object and tries to add it to - // the registry. It will find an existing expired reference to the - // object. - if (!Elems.second) - { - VERIFY(Elems.first->first == ObjectDesc, "Incorrect object description"); - LOG_WARNING_MESSAGE("Object named '", Elems.first->first.Name, - "' with the same description already exists in the registry." - "Replacing with the new object named '", - ObjectDesc.Name ? ObjectDesc.Name : "", "'."); - Elems.first->second = pObject; - } - } - - /// Finds the object in the registry - void Find(const ResourceDescType& Desc, IDeviceObject** ppObject) - { - VERIFY(*ppObject == nullptr, "Overwriting reference to existing object may cause memory leaks"); - *ppObject = nullptr; - ThreadingTools::LockHelper Lock(m_LockFlag); - - auto It = m_DescToObjHashMap.find(Desc); - if (It != m_DescToObjHashMap.end()) - { - // Try to obtain strong reference to the object. - // This is an atomic operation and we either get - // a new strong reference or object has been destroyed - // and we get null. - auto pObject = It->second.Lock(); - if (pObject) - { - *ppObject = pObject.Detach(); - //LOG_INFO_MESSAGE( "Equivalent of the requested state object named \"", Desc.Name ? Desc.Name : "", "\" found in the ", m_RegistryName, " registry. Reusing existing object."); - } - else - { - // Expired object found: remove it from the map - m_DescToObjHashMap.erase(It); - Atomics::AtomicDecrement(m_NumDeletedObjects); - } - } - } - - /// Purges outstanding deleted objects from the registry - void Purge() - { - Uint32 NumPurgedObjects = 0; - auto It = m_DescToObjHashMap.begin(); - while (It != m_DescToObjHashMap.end()) - { - auto NextIt = It; - ++NextIt; - // Note that IsValid() is not a thread-safe function in the sense that it - // can give false positive results. The only thread-safe way to check if the - // object is alive is to lock the weak pointer, but that requires thread - // synchronization. We will immediately unlock the pointer anyway, so we - // want to detect 100% expired pointers. IsValid() does provide that information - // because once a weak pointer becomes invalid, it will be invalid - // until it is destroyed. It is not a problem if we miss an expired weak - // pointer as it will definitiely be removed next time. - if (!It->second.IsValid()) - { - m_DescToObjHashMap.erase(It); - ++NumPurgedObjects; - } - - It = NextIt; - } - LOG_INFO_MESSAGE("Purged ", NumPurgedObjects, " deleted objects from the ", m_RegistryName, " registry"); - } - - /// Increments the number of outstanding deleted objects. - /// When this number reaches DeletedObjectsToPurge, Purge() will - /// be called. - void ReportDeletedObject() - { - Atomics::AtomicIncrement(m_NumDeletedObjects); - } - -private: - /// Lock flag to protect the m_DescToObjHashMap - ThreadingTools::LockFlag m_LockFlag; - - /// Nmber of outstanding deleted objects that have not been purged - Atomics::AtomicLong m_NumDeletedObjects; - - /// Hash map that stores weak pointers to the referenced objects - typedef std::pair> HashMapElem; - std::unordered_map, std::hash, std::equal_to, STDAllocatorRawMem> m_DescToObjHashMap; - - /// Registry name used for debug output - const String m_RegistryName; -}; -} // namespace Diligent diff --git a/Graphics/GraphicsEngine/include/StateObjectsRegistry.hpp b/Graphics/GraphicsEngine/include/StateObjectsRegistry.hpp new file mode 100644 index 00000000..f3484bc1 --- /dev/null +++ b/Graphics/GraphicsEngine/include/StateObjectsRegistry.hpp @@ -0,0 +1,213 @@ +/* + * Copyright 2019-2020 Diligent Graphics LLC + * Copyright 2015-2019 Egor Yusov + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + * + * In no event and under no legal theory, whether in tort (including negligence), + * contract, or otherwise, unless required by applicable law (such as deliberate + * and grossly negligent acts) or agreed to in writing, shall any Contributor be + * liable for any damages, including any direct, indirect, special, incidental, + * or consequential damages of any character arising as a result of this License or + * out of the use or inability to use the software (including but not limited to damages + * for loss of goodwill, work stoppage, computer failure or malfunction, or any and + * all other commercial damages or losses), even if such Contributor has been advised + * of the possibility of such damages. + */ + +#pragma once + +/// \file +/// Implementation of the Diligent::StateObjectsRegistry template class + +#include "DeviceObject.h" +#include +#include "STDAllocator.h" + +namespace Diligent +{ +/// Template class implementing state object registry + +/// \tparam ResourceDescType - type of the resource description. The type must have +/// operator== and a hash function defined. +/// +/// \remarks +/// The following strategies do not work: +/// - To store raw pointers and remove the object from the registry in the +/// object's destructor. In this case other thread may obtain a reference +/// to the object while it is being deleted. This scenario is possible if one thread +/// has just entered the destructor, but other is executing Find() and entered +/// the protection section. +/// - Strong pointers will cause circular references and result in memory leaks. +/// \remarks +/// Only weak pointers provide thread-safe solution. The object is either atomically destroyed, +/// so that no other thread can obtain a reference to it through weak pointers. Or it is atomically +/// locked, so that strong reference is obtained. In this case no other thread can destroy the object, +/// because there is at least one strong reference now. Note however that removing the object from the +/// registry in the object's destructor may cause a deadlock at the point where Find() locks the weak pointer: +/// if other thread has started dtor, the object will be locked by Diligent::RefCountedObject::Release(). +/// If after that this thread locks the registry first, it will be waiting for the object to unlock in +/// Diligent::RefCntWeakPtr::Lock(), while the dtor thread will be waiting for the registry to unlock. +template +class StateObjectsRegistry +{ +public: + /// Number of outstanding deleted objects to purge the registry. + static constexpr int DeletedObjectsToPurge = 32; + + StateObjectsRegistry(IMemoryAllocator& RawAllocator, const Char* RegistryName) : + m_DescToObjHashMap(STD_ALLOCATOR_RAW_MEM(HashMapElem, RawAllocator, "Allocator for unordered_map >")), + m_RegistryName{RegistryName} + {} + + ~StateObjectsRegistry() + { + // Object registry is part of the device, and every device + // object holds a strong reference to the device. So device + // is destroyed after all device objects are destroyed, and there + // may only be expired references in the registry. After we + // purge it, the registry must be empty. + Purge(); + VERIFY(m_DescToObjHashMap.empty(), "DescToObjHashMap is not empty"); + } + + /// Adds a new object to the registry + + /// \param [in] ObjectDesc - object description. + /// \param [in] pObject - pointer to the object. + /// + /// Besides adding a new object, the function also checks the number of + /// outstanding deleted objects and calls Purge() if the number has reached + /// the threshold value DeletedObjectsToPurge. Creating a state object is + /// assumed to be an expensive operation and should be performed during + /// the initialization. Occasional purge operations should not add significant + /// cost to it. + void Add(const ResourceDescType& ObjectDesc, IDeviceObject* pObject) + { + ThreadingTools::LockHelper Lock(m_LockFlag); + + // If the number of outstanding deleted objects reached the threshold value, + // purge the registry. Since we have exclusive access now, it is safe + // to do. + if (m_NumDeletedObjects >= DeletedObjectsToPurge) + { + Purge(); + m_NumDeletedObjects = 0; + } + + // Try to construct the new element in place + auto Elems = m_DescToObjHashMap.emplace(std::make_pair(ObjectDesc, Diligent::RefCntWeakPtr(pObject))); + // It is theorertically possible that the same object can be found + // in the registry. This might happen if two threads try to create + // the same object at the same time. They both will not find the + // object and then will create and try to add it. + // + // If the object already exists, we replace the existing reference. + // This is safer as there might be scenarios where existing reference + // might be expired. For instance, two threads try to create the same + // object which is not in the registry. The first thread creates + // the object, adds it to the registry and then releases it. After that + // the second thread creates the same object and tries to add it to + // the registry. It will find an existing expired reference to the + // object. + if (!Elems.second) + { + VERIFY(Elems.first->first == ObjectDesc, "Incorrect object description"); + LOG_WARNING_MESSAGE("Object named '", Elems.first->first.Name, + "' with the same description already exists in the registry." + "Replacing with the new object named '", + ObjectDesc.Name ? ObjectDesc.Name : "", "'."); + Elems.first->second = pObject; + } + } + + /// Finds the object in the registry + void Find(const ResourceDescType& Desc, IDeviceObject** ppObject) + { + VERIFY(*ppObject == nullptr, "Overwriting reference to existing object may cause memory leaks"); + *ppObject = nullptr; + ThreadingTools::LockHelper Lock(m_LockFlag); + + auto It = m_DescToObjHashMap.find(Desc); + if (It != m_DescToObjHashMap.end()) + { + // Try to obtain strong reference to the object. + // This is an atomic operation and we either get + // a new strong reference or object has been destroyed + // and we get null. + auto pObject = It->second.Lock(); + if (pObject) + { + *ppObject = pObject.Detach(); + //LOG_INFO_MESSAGE( "Equivalent of the requested state object named \"", Desc.Name ? Desc.Name : "", "\" found in the ", m_RegistryName, " registry. Reusing existing object."); + } + else + { + // Expired object found: remove it from the map + m_DescToObjHashMap.erase(It); + Atomics::AtomicDecrement(m_NumDeletedObjects); + } + } + } + + /// Purges outstanding deleted objects from the registry + void Purge() + { + Uint32 NumPurgedObjects = 0; + auto It = m_DescToObjHashMap.begin(); + while (It != m_DescToObjHashMap.end()) + { + auto NextIt = It; + ++NextIt; + // Note that IsValid() is not a thread-safe function in the sense that it + // can give false positive results. The only thread-safe way to check if the + // object is alive is to lock the weak pointer, but that requires thread + // synchronization. We will immediately unlock the pointer anyway, so we + // want to detect 100% expired pointers. IsValid() does provide that information + // because once a weak pointer becomes invalid, it will be invalid + // until it is destroyed. It is not a problem if we miss an expired weak + // pointer as it will definitiely be removed next time. + if (!It->second.IsValid()) + { + m_DescToObjHashMap.erase(It); + ++NumPurgedObjects; + } + + It = NextIt; + } + LOG_INFO_MESSAGE("Purged ", NumPurgedObjects, " deleted objects from the ", m_RegistryName, " registry"); + } + + /// Increments the number of outstanding deleted objects. + /// When this number reaches DeletedObjectsToPurge, Purge() will + /// be called. + void ReportDeletedObject() + { + Atomics::AtomicIncrement(m_NumDeletedObjects); + } + +private: + /// Lock flag to protect the m_DescToObjHashMap + ThreadingTools::LockFlag m_LockFlag; + + /// Nmber of outstanding deleted objects that have not been purged + Atomics::AtomicLong m_NumDeletedObjects; + + /// Hash map that stores weak pointers to the referenced objects + typedef std::pair> HashMapElem; + std::unordered_map, std::hash, std::equal_to, STDAllocatorRawMem> m_DescToObjHashMap; + + /// Registry name used for debug output + const String m_RegistryName; +}; +} // namespace Diligent diff --git a/Graphics/GraphicsEngine/include/SwapChainBase.h b/Graphics/GraphicsEngine/include/SwapChainBase.h deleted file mode 100644 index 7db31f8d..00000000 --- a/Graphics/GraphicsEngine/include/SwapChainBase.h +++ /dev/null @@ -1,118 +0,0 @@ -/* - * Copyright 2019-2020 Diligent Graphics LLC - * Copyright 2015-2019 Egor Yusov - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - * - * In no event and under no legal theory, whether in tort (including negligence), - * contract, or otherwise, unless required by applicable law (such as deliberate - * and grossly negligent acts) or agreed to in writing, shall any Contributor be - * liable for any damages, including any direct, indirect, special, incidental, - * or consequential damages of any character arising as a result of this License or - * out of the use or inability to use the software (including but not limited to damages - * for loss of goodwill, work stoppage, computer failure or malfunction, or any and - * all other commercial damages or losses), even if such Contributor has been advised - * of the possibility of such damages. - */ - -#pragma once - -/// \file -/// Implementation of the Diligent::SwapChainBase template class - -#include "RenderDevice.h" -#include "DeviceContext.h" -#include "SwapChain.h" -#include "ObjectBase.h" -#include "Errors.h" -#include "RefCntAutoPtr.h" - -namespace Diligent -{ - -/// Base implementation of the swap chain. - -/// \tparam BaseInterface - base interface that this class will inheret -/// (Diligent::ISwapChainGL, Diligent::ISwapChainD3D11, -/// Diligent::ISwapChainD3D12 or Diligent::ISwapChainVk). -/// \remarks Swap chain holds the strong reference to the device and a weak reference to the -/// immediate context. -template -class SwapChainBase : public ObjectBase -{ -public: - using TObjectBase = ObjectBase; - - /// \param pRefCounters - reference counters object that controls the lifetime of this swap chain. - /// \param pDevice - pointer to the device. - /// \param pDeviceContext - pointer to the device context. - /// \param SCDesc - swap chain description - SwapChainBase(IReferenceCounters* pRefCounters, - IRenderDevice* pDevice, - IDeviceContext* pDeviceContext, - const SwapChainDesc& SCDesc) : - // clang-format off - TObjectBase {pRefCounters}, - m_pRenderDevice {pDevice }, - m_wpDeviceContext {pDeviceContext}, - m_SwapChainDesc {SCDesc } - // clang-format on - { - } - - // clang-format off - SwapChainBase (const SwapChainBase&) = delete; - SwapChainBase ( SwapChainBase&&) = delete; - SwapChainBase& operator = (const SwapChainBase&) = delete; - SwapChainBase& operator = ( SwapChainBase&&) = delete; - // clang-format on - - ~SwapChainBase() - { - } - - IMPLEMENT_QUERY_INTERFACE_IN_PLACE(IID_SwapChain, TObjectBase) - - /// Implementation of ISwapChain::GetDesc() - virtual const SwapChainDesc& GetDesc() const override final - { - return m_SwapChainDesc; - } - -protected: - bool Resize(Uint32 NewWidth, Uint32 NewHeight, Int32 Dummy = 0 /*To be different from virtual function*/) - { - if (NewWidth != 0 && NewHeight != 0 && - (m_SwapChainDesc.Width != NewWidth || m_SwapChainDesc.Height != NewHeight)) - { - m_SwapChainDesc.Width = NewWidth; - m_SwapChainDesc.Height = NewHeight; - LOG_INFO_MESSAGE("Resizing the swap chain to ", m_SwapChainDesc.Width, "x", m_SwapChainDesc.Height); - return true; - } - - return false; - } - - /// Strong reference to the render device - RefCntAutoPtr m_pRenderDevice; - - /// Weak references to the immediate device context. The context holds - /// the strong reference to the swap chain. - RefCntWeakPtr m_wpDeviceContext; - - /// Swap chain description - SwapChainDesc m_SwapChainDesc; -}; - -} // namespace Diligent diff --git a/Graphics/GraphicsEngine/include/SwapChainBase.hpp b/Graphics/GraphicsEngine/include/SwapChainBase.hpp new file mode 100644 index 00000000..7db31f8d --- /dev/null +++ b/Graphics/GraphicsEngine/include/SwapChainBase.hpp @@ -0,0 +1,118 @@ +/* + * Copyright 2019-2020 Diligent Graphics LLC + * Copyright 2015-2019 Egor Yusov + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + * + * In no event and under no legal theory, whether in tort (including negligence), + * contract, or otherwise, unless required by applicable law (such as deliberate + * and grossly negligent acts) or agreed to in writing, shall any Contributor be + * liable for any damages, including any direct, indirect, special, incidental, + * or consequential damages of any character arising as a result of this License or + * out of the use or inability to use the software (including but not limited to damages + * for loss of goodwill, work stoppage, computer failure or malfunction, or any and + * all other commercial damages or losses), even if such Contributor has been advised + * of the possibility of such damages. + */ + +#pragma once + +/// \file +/// Implementation of the Diligent::SwapChainBase template class + +#include "RenderDevice.h" +#include "DeviceContext.h" +#include "SwapChain.h" +#include "ObjectBase.h" +#include "Errors.h" +#include "RefCntAutoPtr.h" + +namespace Diligent +{ + +/// Base implementation of the swap chain. + +/// \tparam BaseInterface - base interface that this class will inheret +/// (Diligent::ISwapChainGL, Diligent::ISwapChainD3D11, +/// Diligent::ISwapChainD3D12 or Diligent::ISwapChainVk). +/// \remarks Swap chain holds the strong reference to the device and a weak reference to the +/// immediate context. +template +class SwapChainBase : public ObjectBase +{ +public: + using TObjectBase = ObjectBase; + + /// \param pRefCounters - reference counters object that controls the lifetime of this swap chain. + /// \param pDevice - pointer to the device. + /// \param pDeviceContext - pointer to the device context. + /// \param SCDesc - swap chain description + SwapChainBase(IReferenceCounters* pRefCounters, + IRenderDevice* pDevice, + IDeviceContext* pDeviceContext, + const SwapChainDesc& SCDesc) : + // clang-format off + TObjectBase {pRefCounters}, + m_pRenderDevice {pDevice }, + m_wpDeviceContext {pDeviceContext}, + m_SwapChainDesc {SCDesc } + // clang-format on + { + } + + // clang-format off + SwapChainBase (const SwapChainBase&) = delete; + SwapChainBase ( SwapChainBase&&) = delete; + SwapChainBase& operator = (const SwapChainBase&) = delete; + SwapChainBase& operator = ( SwapChainBase&&) = delete; + // clang-format on + + ~SwapChainBase() + { + } + + IMPLEMENT_QUERY_INTERFACE_IN_PLACE(IID_SwapChain, TObjectBase) + + /// Implementation of ISwapChain::GetDesc() + virtual const SwapChainDesc& GetDesc() const override final + { + return m_SwapChainDesc; + } + +protected: + bool Resize(Uint32 NewWidth, Uint32 NewHeight, Int32 Dummy = 0 /*To be different from virtual function*/) + { + if (NewWidth != 0 && NewHeight != 0 && + (m_SwapChainDesc.Width != NewWidth || m_SwapChainDesc.Height != NewHeight)) + { + m_SwapChainDesc.Width = NewWidth; + m_SwapChainDesc.Height = NewHeight; + LOG_INFO_MESSAGE("Resizing the swap chain to ", m_SwapChainDesc.Width, "x", m_SwapChainDesc.Height); + return true; + } + + return false; + } + + /// Strong reference to the render device + RefCntAutoPtr m_pRenderDevice; + + /// Weak references to the immediate device context. The context holds + /// the strong reference to the swap chain. + RefCntWeakPtr m_wpDeviceContext; + + /// Swap chain description + SwapChainDesc m_SwapChainDesc; +}; + +} // namespace Diligent diff --git a/Graphics/GraphicsEngine/include/TextureBase.h b/Graphics/GraphicsEngine/include/TextureBase.h deleted file mode 100644 index 8a3bc8d3..00000000 --- a/Graphics/GraphicsEngine/include/TextureBase.h +++ /dev/null @@ -1,515 +0,0 @@ -/* - * Copyright 2019-2020 Diligent Graphics LLC - * Copyright 2015-2019 Egor Yusov - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - * - * In no event and under no legal theory, whether in tort (including negligence), - * contract, or otherwise, unless required by applicable law (such as deliberate - * and grossly negligent acts) or agreed to in writing, shall any Contributor be - * liable for any damages, including any direct, indirect, special, incidental, - * or consequential damages of any character arising as a result of this License or - * out of the use or inability to use the software (including but not limited to damages - * for loss of goodwill, work stoppage, computer failure or malfunction, or any and - * all other commercial damages or losses), even if such Contributor has been advised - * of the possibility of such damages. - */ - -#pragma once - -/// \file -/// Implementation of the Diligent::TextureBase template class - -#include "Texture.h" -#include "GraphicsTypes.h" -#include "DeviceObjectBase.h" -#include "GraphicsAccessories.hpp" -#include "STDAllocator.h" -#include "FormatString.h" -#include - -namespace Diligent -{ - -struct CopyTextureAttribs; - -void ValidateTextureDesc(const TextureDesc& TexDesc); -void ValidateUpdateTextureParams(const TextureDesc& TexDesc, Uint32 MipLevel, Uint32 Slice, const Box& DstBox, const TextureSubResData& SubresData); -void ValidateCopyTextureParams(const CopyTextureAttribs& CopyAttribs); -void ValidateMapTextureParams(const TextureDesc& TexDesc, - Uint32 MipLevel, - Uint32 ArraySlice, - MAP_TYPE MapType, - Uint32 MapFlags, - const Box* pMapRegion); - -/// Base implementation of the ITexture interface - -/// \tparam BaseInterface - base interface that this class will inheret -/// (Diligent::ITextureD3D11, Diligent::ITextureD3D12, -/// Diligent::ITextureGL or Diligent::ITextureVk). -/// \tparam TRenderDeviceImpl - type of the render device implementation -/// (Diligent::RenderDeviceD3D11Impl, Diligent::RenderDeviceD3D12Impl, -/// Diligent::RenderDeviceGLImpl, or Diligent::RenderDeviceVkImpl) -/// \tparam TTextureViewImpl - type of the texture view implementation -/// (Diligent::TextureViewD3D11Impl, Diligent::TextureViewD3D12Impl, -/// Diligent::TextureViewGLImpl or Diligent::TextureViewVkImpl). -/// \tparam TTexViewObjAllocator - type of the allocator that is used to allocate memory for the texture view object instances -template -class TextureBase : public DeviceObjectBase -{ -public: - using TDeviceObjectBase = DeviceObjectBase; - - /// \param pRefCounters - reference counters object that controls the lifetime of this texture. - /// \param TexViewObjAllocator - allocator that is used to allocate memory for the instances of the texture view object. - /// This parameter is only used for debug purposes. - /// \param pDevice - pointer to the device - /// \param Desc - texture description - /// \param bIsDeviceInternal - flag indicating if the texture is an internal device object and - /// must not keep a strong reference to the device - TextureBase(IReferenceCounters* pRefCounters, - TTexViewObjAllocator& TexViewObjAllocator, - TRenderDeviceImpl* pDevice, - const TextureDesc& Desc, - bool bIsDeviceInternal = false) : - TDeviceObjectBase(pRefCounters, pDevice, Desc, bIsDeviceInternal), -#ifdef _DEBUG - m_dbgTexViewObjAllocator(TexViewObjAllocator), -#endif - m_pDefaultSRV(nullptr, STDDeleter(TexViewObjAllocator)), - m_pDefaultRTV(nullptr, STDDeleter(TexViewObjAllocator)), - m_pDefaultDSV(nullptr, STDDeleter(TexViewObjAllocator)), - m_pDefaultUAV(nullptr, STDDeleter(TexViewObjAllocator)) - { - if (this->m_Desc.MipLevels == 0) - { - // Compute the number of levels in the full mipmap chain - if (this->m_Desc.Type == RESOURCE_DIM_TEX_1D || - this->m_Desc.Type == RESOURCE_DIM_TEX_1D_ARRAY) - { - this->m_Desc.MipLevels = ComputeMipLevelsCount(this->m_Desc.Width); - } - else if (this->m_Desc.Type == RESOURCE_DIM_TEX_2D || - this->m_Desc.Type == RESOURCE_DIM_TEX_2D_ARRAY || - this->m_Desc.Type == RESOURCE_DIM_TEX_CUBE || - this->m_Desc.Type == RESOURCE_DIM_TEX_CUBE_ARRAY) - { - this->m_Desc.MipLevels = ComputeMipLevelsCount(this->m_Desc.Width, this->m_Desc.Height); - } - else if (this->m_Desc.Type == RESOURCE_DIM_TEX_3D) - { - this->m_Desc.MipLevels = ComputeMipLevelsCount(this->m_Desc.Width, this->m_Desc.Height, this->m_Desc.Depth); - } - else - { - UNEXPECTED("Unknown texture type"); - } - } - - Uint64 DeviceQueuesMask = pDevice->GetCommandQueueMask(); - DEV_CHECK_ERR((this->m_Desc.CommandQueueMask & DeviceQueuesMask) != 0, - "No bits in the command queue mask (0x", std::hex, this->m_Desc.CommandQueueMask, - ") correspond to one of ", pDevice->GetCommandQueueCount(), " available device command queues"); - this->m_Desc.CommandQueueMask &= DeviceQueuesMask; - - // Validate correctness of texture description - ValidateTextureDesc(this->m_Desc); - } - - IMPLEMENT_QUERY_INTERFACE_IN_PLACE(IID_Texture, TDeviceObjectBase) - - /// Implementaiton of ITexture::CreateView(); calls CreateViewInternal() virtual function that - /// creates texture view for the specific engine implementation. - virtual void CreateView(const struct TextureViewDesc& ViewDesc, ITextureView** ppView) override - { - DEV_CHECK_ERR(ViewDesc.ViewType != TEXTURE_VIEW_UNDEFINED, "Texture view type is not specified"); - if (ViewDesc.ViewType == TEXTURE_VIEW_SHADER_RESOURCE) - DEV_CHECK_ERR(this->m_Desc.BindFlags & BIND_SHADER_RESOURCE, "Attempting to create SRV for texture '", this->m_Desc.Name, "' that was not created with BIND_SHADER_RESOURCE flag"); - else if (ViewDesc.ViewType == TEXTURE_VIEW_UNORDERED_ACCESS) - DEV_CHECK_ERR(this->m_Desc.BindFlags & BIND_UNORDERED_ACCESS, "Attempting to create UAV for texture '", this->m_Desc.Name, "' that was not created with BIND_UNORDERED_ACCESS flag"); - else if (ViewDesc.ViewType == TEXTURE_VIEW_RENDER_TARGET) - DEV_CHECK_ERR(this->m_Desc.BindFlags & BIND_RENDER_TARGET, "Attempting to create RTV for texture '", this->m_Desc.Name, "' that was not created with BIND_RENDER_TARGET flag"); - else if (ViewDesc.ViewType == TEXTURE_VIEW_DEPTH_STENCIL) - DEV_CHECK_ERR(this->m_Desc.BindFlags & BIND_DEPTH_STENCIL, "Attempting to create DSV for texture '", this->m_Desc.Name, "' that was not created with BIND_DEPTH_STENCIL flag"); - else - UNEXPECTED("Unexpected texture view type"); - - CreateViewInternal(ViewDesc, ppView, false); - } - - /// Creates default texture views. - - /// - /// - Creates default shader resource view addressing the entire texture if Diligent::BIND_SHADER_RESOURCE flag is set. - /// - Creates default render target view addressing the most detailed mip level if Diligent::BIND_RENDER_TARGET flag is set. - /// - Creates default depth-stencil view addressing the most detailed mip level if Diligent::BIND_DEPTH_STENCIL flag is set. - /// - Creates default unordered access view addressing the entire texture if Diligent::BIND_UNORDERED_ACCESS flag is set. - /// - /// The function calls CreateViewInternal(). - void CreateDefaultViews(); - - virtual void SetState(RESOURCE_STATE State) override final - { - this->m_State = State; - } - - virtual RESOURCE_STATE GetState() const override final - { - return this->m_State; - } - - bool IsInKnownState() const - { - return this->m_State != RESOURCE_STATE_UNKNOWN; - } - - bool CheckState(RESOURCE_STATE State) const - { - VERIFY((State & (State - 1)) == 0, "Single state is expected"); - VERIFY(IsInKnownState(), "Texture state is unknown"); - return (this->m_State & State) == State; - } - - /// Implementation of ITexture::GetDefaultView(). - virtual ITextureView* GetDefaultView(TEXTURE_VIEW_TYPE ViewType) override - { - switch (ViewType) - { - // clang-format off - case TEXTURE_VIEW_SHADER_RESOURCE: return m_pDefaultSRV.get(); - case TEXTURE_VIEW_RENDER_TARGET: return m_pDefaultRTV.get(); - case TEXTURE_VIEW_DEPTH_STENCIL: return m_pDefaultDSV.get(); - case TEXTURE_VIEW_UNORDERED_ACCESS: return m_pDefaultUAV.get(); - // clang-format on - default: UNEXPECTED("Unknown view type"); return nullptr; - } - } - -protected: - /// Pure virtual function that creates texture view for the specific engine implementation. - virtual void CreateViewInternal(const struct TextureViewDesc& ViewDesc, ITextureView** ppView, bool bIsDefaultView) = 0; - -#ifdef _DEBUG - TTexViewObjAllocator& m_dbgTexViewObjAllocator; -#endif - // WARNING! We cannot use ITextureView here, because ITextureView has no virtual dtor! - /// Default SRV addressing the entire texture - std::unique_ptr> m_pDefaultSRV; - /// Default RTV addressing the most detailed mip level - std::unique_ptr> m_pDefaultRTV; - /// Default DSV addressing the most detailed mip level - std::unique_ptr> m_pDefaultDSV; - /// Default UAV addressing the entire texture - std::unique_ptr> m_pDefaultUAV; - - void CorrectTextureViewDesc(struct TextureViewDesc& ViewDesc); - - RESOURCE_STATE m_State = RESOURCE_STATE_UNKNOWN; -}; - - -template -void TextureBase::CorrectTextureViewDesc(struct TextureViewDesc& ViewDesc) -{ -#define TEX_VIEW_VALIDATION_ERROR(...) LOG_ERROR_AND_THROW("\n Failed to create texture view '", (ViewDesc.Name ? ViewDesc.Name : ""), "' for texture '", this->m_Desc.Name, "': ", ##__VA_ARGS__) - - if (!(ViewDesc.ViewType > TEXTURE_VIEW_UNDEFINED && ViewDesc.ViewType < TEXTURE_VIEW_NUM_VIEWS)) - TEX_VIEW_VALIDATION_ERROR("Texture view type is not specified"); - - if (ViewDesc.MostDetailedMip >= this->m_Desc.MipLevels) - TEX_VIEW_VALIDATION_ERROR("Most detailed mip (", ViewDesc.MostDetailedMip, ") is out of range. The texture has only ", this->m_Desc.MipLevels, " mip ", (this->m_Desc.MipLevels > 1 ? "levels." : "level.")); - - if (ViewDesc.NumMipLevels != REMAINING_MIP_LEVELS && ViewDesc.MostDetailedMip + ViewDesc.NumMipLevels > this->m_Desc.MipLevels) - TEX_VIEW_VALIDATION_ERROR("Most detailed mip (", ViewDesc.MostDetailedMip, ") and number of mip levels in the view (", ViewDesc.NumMipLevels, ") is out of range. The texture has only ", this->m_Desc.MipLevels, " mip ", (this->m_Desc.MipLevels > 1 ? "levels." : "level.")); - - if (ViewDesc.Format == TEX_FORMAT_UNKNOWN) - ViewDesc.Format = GetDefaultTextureViewFormat(this->m_Desc.Format, ViewDesc.ViewType, this->m_Desc.BindFlags); - - if (ViewDesc.TextureDim == RESOURCE_DIM_UNDEFINED) - { - if (this->m_Desc.Type == RESOURCE_DIM_TEX_CUBE || this->m_Desc.Type == RESOURCE_DIM_TEX_CUBE_ARRAY) - { - switch (ViewDesc.ViewType) - { - case TEXTURE_VIEW_SHADER_RESOURCE: - ViewDesc.TextureDim = this->m_Desc.Type; - break; - - case TEXTURE_VIEW_RENDER_TARGET: - case TEXTURE_VIEW_DEPTH_STENCIL: - case TEXTURE_VIEW_UNORDERED_ACCESS: - ViewDesc.TextureDim = RESOURCE_DIM_TEX_2D_ARRAY; - break; - - default: UNEXPECTED("Unexpected view type"); - } - } - else - { - ViewDesc.TextureDim = this->m_Desc.Type; - } - } - - switch (this->m_Desc.Type) - { - case RESOURCE_DIM_TEX_1D: - if (ViewDesc.TextureDim != RESOURCE_DIM_TEX_1D) - { - TEX_VIEW_VALIDATION_ERROR("Incorrect texture type for Texture 1D view: only Texture 1D is allowed"); - } - break; - - case RESOURCE_DIM_TEX_1D_ARRAY: - if (ViewDesc.TextureDim != RESOURCE_DIM_TEX_1D_ARRAY && - ViewDesc.TextureDim != RESOURCE_DIM_TEX_1D) - { - TEX_VIEW_VALIDATION_ERROR("Incorrect view type for Texture 1D Array: only Texture 1D or Texture 1D Array are allowed"); - } - break; - - case RESOURCE_DIM_TEX_2D: - if (ViewDesc.TextureDim != RESOURCE_DIM_TEX_2D_ARRAY && - ViewDesc.TextureDim != RESOURCE_DIM_TEX_2D) - { - TEX_VIEW_VALIDATION_ERROR("Incorrect texture type for Texture 2D view: only Texture 2D or Texture 2D Array are allowed"); - } - break; - - case RESOURCE_DIM_TEX_2D_ARRAY: - if (ViewDesc.TextureDim != RESOURCE_DIM_TEX_2D_ARRAY && - ViewDesc.TextureDim != RESOURCE_DIM_TEX_2D) - { - TEX_VIEW_VALIDATION_ERROR("Incorrect texture type for Texture 2D Array view: only Texture 2D or Texture 2D Array are allowed"); - } - break; - - case RESOURCE_DIM_TEX_3D: - if (ViewDesc.TextureDim != RESOURCE_DIM_TEX_3D) - { - TEX_VIEW_VALIDATION_ERROR("Incorrect texture type for Texture 3D view: only Texture 3D is allowed"); - } - break; - - case RESOURCE_DIM_TEX_CUBE: - if (ViewDesc.ViewType == TEXTURE_VIEW_SHADER_RESOURCE) - { - if (ViewDesc.TextureDim != RESOURCE_DIM_TEX_2D && - ViewDesc.TextureDim != RESOURCE_DIM_TEX_2D_ARRAY && - ViewDesc.TextureDim != RESOURCE_DIM_TEX_CUBE) - { - TEX_VIEW_VALIDATION_ERROR("Incorrect texture type for Texture cube SRV: Texture 2D, Texture 2D array or Texture Cube is allowed"); - } - } - else - { - if (ViewDesc.TextureDim != RESOURCE_DIM_TEX_2D && - ViewDesc.TextureDim != RESOURCE_DIM_TEX_2D_ARRAY) - { - TEX_VIEW_VALIDATION_ERROR("Incorrect texture type for Texture cube non-shader resource view: Texture 2D or Texture 2D array is allowed"); - } - } - break; - - case RESOURCE_DIM_TEX_CUBE_ARRAY: - if (ViewDesc.ViewType == TEXTURE_VIEW_SHADER_RESOURCE) - { - if (ViewDesc.TextureDim != RESOURCE_DIM_TEX_2D && - ViewDesc.TextureDim != RESOURCE_DIM_TEX_2D_ARRAY && - ViewDesc.TextureDim != RESOURCE_DIM_TEX_CUBE && - ViewDesc.TextureDim != RESOURCE_DIM_TEX_CUBE_ARRAY) - { - TEX_VIEW_VALIDATION_ERROR("Incorrect texture type for Texture cube array SRV: Texture 2D, Texture 2D array, Texture Cube or Texture Cube Array is allowed"); - } - } - else - { - if (ViewDesc.TextureDim != RESOURCE_DIM_TEX_2D && - ViewDesc.TextureDim != RESOURCE_DIM_TEX_2D_ARRAY) - { - TEX_VIEW_VALIDATION_ERROR("Incorrect texture type for Texture cube array non-shader resource view: Texture 2D or Texture 2D array is allowed"); - } - } - break; - - default: - UNEXPECTED("Unexpected texture type"); - break; - } - - if (ViewDesc.TextureDim == RESOURCE_DIM_TEX_CUBE) - { - if (ViewDesc.ViewType != TEXTURE_VIEW_SHADER_RESOURCE) - TEX_VIEW_VALIDATION_ERROR("Unexpected view type: SRV is expected"); - if (ViewDesc.NumArraySlices != 6 && ViewDesc.NumArraySlices != 0 && ViewDesc.NumArraySlices != REMAINING_ARRAY_SLICES) - TEX_VIEW_VALIDATION_ERROR("Texture cube SRV is expected to have 6 array slices, while ", ViewDesc.NumArraySlices, " is provided"); - if (ViewDesc.FirstArraySlice != 0) - TEX_VIEW_VALIDATION_ERROR("First slice (", ViewDesc.FirstArraySlice, ") must be 0 for non-array texture cube SRV"); - } - if (ViewDesc.TextureDim == RESOURCE_DIM_TEX_CUBE_ARRAY) - { - if (ViewDesc.ViewType != TEXTURE_VIEW_SHADER_RESOURCE) - TEX_VIEW_VALIDATION_ERROR("Unexpected view type: SRV is expected"); - if (ViewDesc.NumArraySlices != REMAINING_ARRAY_SLICES && (ViewDesc.NumArraySlices % 6) != 0) - TEX_VIEW_VALIDATION_ERROR("Number of slices in texture cube array SRV is expected to be multiple of 6. ", ViewDesc.NumArraySlices, " slices is provided."); - } - - if (ViewDesc.TextureDim == RESOURCE_DIM_TEX_1D || - ViewDesc.TextureDim == RESOURCE_DIM_TEX_2D) - { - if (ViewDesc.FirstArraySlice != 0) - TEX_VIEW_VALIDATION_ERROR("First slice (", ViewDesc.FirstArraySlice, ") must be 0 for non-array texture 1D/2D views"); - - if (ViewDesc.NumArraySlices != REMAINING_ARRAY_SLICES && ViewDesc.NumArraySlices > 1) - TEX_VIEW_VALIDATION_ERROR("Number of slices in the view (", ViewDesc.NumArraySlices, ") must be 1 (or 0) for non-array texture 1D/2D views"); - } - else if (ViewDesc.TextureDim == RESOURCE_DIM_TEX_1D_ARRAY || - ViewDesc.TextureDim == RESOURCE_DIM_TEX_2D_ARRAY || - ViewDesc.TextureDim == RESOURCE_DIM_TEX_CUBE || - ViewDesc.TextureDim == RESOURCE_DIM_TEX_CUBE_ARRAY) - { - if (ViewDesc.FirstArraySlice >= this->m_Desc.ArraySize) - TEX_VIEW_VALIDATION_ERROR("First array slice (", ViewDesc.FirstArraySlice, ") exceeds the number of slices in the texture array (", this->m_Desc.ArraySize, ")"); - - if (ViewDesc.NumArraySlices != REMAINING_ARRAY_SLICES && ViewDesc.FirstArraySlice + ViewDesc.NumArraySlices > this->m_Desc.ArraySize) - TEX_VIEW_VALIDATION_ERROR("First slice (", ViewDesc.FirstArraySlice, ") and number of slices in the view (", ViewDesc.NumArraySlices, ") specify more slices than target texture has (", this->m_Desc.ArraySize, ")"); - } - else if (ViewDesc.TextureDim == RESOURCE_DIM_TEX_3D) - { - auto MipDepth = this->m_Desc.Depth >> ViewDesc.MostDetailedMip; - if (ViewDesc.FirstDepthSlice + ViewDesc.NumDepthSlices > MipDepth) - TEX_VIEW_VALIDATION_ERROR("First slice (", ViewDesc.FirstDepthSlice, ") and number of slices in the view (", ViewDesc.NumDepthSlices, ") specify more slices than target 3D texture mip level has (", MipDepth, ")"); - } - else - { - UNEXPECTED("Unexpected texture dimension"); - } - - if (GetTextureFormatAttribs(ViewDesc.Format).IsTypeless) - { - TEX_VIEW_VALIDATION_ERROR("Texture view format (", GetTextureFormatAttribs(ViewDesc.Format).Name, ") cannot be typeless"); - } - - if ((ViewDesc.Flags & TEXTURE_VIEW_FLAG_ALLOW_MIP_MAP_GENERATION) != 0) - { - if ((this->m_Desc.MiscFlags & MISC_TEXTURE_FLAG_GENERATE_MIPS) == 0) - TEX_VIEW_VALIDATION_ERROR("TEXTURE_VIEW_FLAG_ALLOW_MIP_MAP_GENERATION flag can only set if the texture was created with MISC_TEXTURE_FLAG_GENERATE_MIPS flag"); - - if (ViewDesc.ViewType != TEXTURE_VIEW_SHADER_RESOURCE) - TEX_VIEW_VALIDATION_ERROR("TEXTURE_VIEW_FLAG_ALLOW_MIP_MAP_GENERATION flag can only be used with TEXTURE_VIEW_SHADER_RESOURCE view type"); - } - -#undef TEX_VIEW_VALIDATION_ERROR - - if (ViewDesc.NumMipLevels == 0 || ViewDesc.NumMipLevels == REMAINING_MIP_LEVELS) - { - if (ViewDesc.ViewType == TEXTURE_VIEW_SHADER_RESOURCE) - ViewDesc.NumMipLevels = this->m_Desc.MipLevels - ViewDesc.MostDetailedMip; - else - ViewDesc.NumMipLevels = 1; - } - - if (ViewDesc.NumArraySlices == 0 || ViewDesc.NumArraySlices == REMAINING_ARRAY_SLICES) - { - if (ViewDesc.TextureDim == RESOURCE_DIM_TEX_1D_ARRAY || - ViewDesc.TextureDim == RESOURCE_DIM_TEX_2D_ARRAY || - ViewDesc.TextureDim == RESOURCE_DIM_TEX_CUBE || - ViewDesc.TextureDim == RESOURCE_DIM_TEX_CUBE_ARRAY) - ViewDesc.NumArraySlices = this->m_Desc.ArraySize - ViewDesc.FirstArraySlice; - else if (ViewDesc.TextureDim == RESOURCE_DIM_TEX_3D) - { - auto MipDepth = this->m_Desc.Depth >> ViewDesc.MostDetailedMip; - ViewDesc.NumDepthSlices = MipDepth - ViewDesc.FirstDepthSlice; - } - else - ViewDesc.NumArraySlices = 1; - } - - if ((ViewDesc.ViewType == TEXTURE_VIEW_RENDER_TARGET) && - (ViewDesc.Format == TEX_FORMAT_R8_SNORM || ViewDesc.Format == TEX_FORMAT_RG8_SNORM || ViewDesc.Format == TEX_FORMAT_RGBA8_SNORM || - ViewDesc.Format == TEX_FORMAT_R16_SNORM || ViewDesc.Format == TEX_FORMAT_RG16_SNORM || ViewDesc.Format == TEX_FORMAT_RGBA16_SNORM)) - { - const auto* FmtName = GetTextureFormatAttribs(ViewDesc.Format).Name; - LOG_WARNING_MESSAGE(FmtName, " render target view is created.\n" - "There might be an issue in OpenGL driver on NVidia hardware: when rendering to SNORM textures, all negative values are clamped to zero.\n" - "Use UNORM format instead."); - } -} - -template -void TextureBase::CreateDefaultViews() -{ - const auto& TexFmtAttribs = GetTextureFormatAttribs(this->m_Desc.Format); - if (TexFmtAttribs.ComponentType == COMPONENT_TYPE_UNDEFINED) - { - // Cannot create default view for TYPELESS formats - return; - } - - if (this->m_Desc.BindFlags & BIND_SHADER_RESOURCE) - { - TextureViewDesc ViewDesc; - ViewDesc.ViewType = TEXTURE_VIEW_SHADER_RESOURCE; - auto ViewName = FormatString("Default SRV of texture '", this->m_Desc.Name, "'"); - ViewDesc.Name = ViewName.c_str(); - - ITextureView* pSRV = nullptr; - if ((this->m_Desc.MiscFlags & MISC_TEXTURE_FLAG_GENERATE_MIPS) != 0) - ViewDesc.Flags |= TEXTURE_VIEW_FLAG_ALLOW_MIP_MAP_GENERATION; - CreateViewInternal(ViewDesc, &pSRV, true); - m_pDefaultSRV.reset(static_cast(pSRV)); - VERIFY(m_pDefaultSRV->GetDesc().ViewType == TEXTURE_VIEW_SHADER_RESOURCE, "Unexpected view type"); - } - - if (this->m_Desc.BindFlags & BIND_RENDER_TARGET) - { - TextureViewDesc ViewDesc; - ViewDesc.ViewType = TEXTURE_VIEW_RENDER_TARGET; - auto ViewName = FormatString("Default RTV of texture '", this->m_Desc.Name, "'"); - ViewDesc.Name = ViewName.c_str(); - - ITextureView* pRTV = nullptr; - CreateViewInternal(ViewDesc, &pRTV, true); - m_pDefaultRTV.reset(static_cast(pRTV)); - VERIFY(m_pDefaultRTV->GetDesc().ViewType == TEXTURE_VIEW_RENDER_TARGET, "Unexpected view type"); - } - - if (this->m_Desc.BindFlags & BIND_DEPTH_STENCIL) - { - TextureViewDesc ViewDesc; - ViewDesc.ViewType = TEXTURE_VIEW_DEPTH_STENCIL; - auto ViewName = FormatString("Default DSV of texture '", this->m_Desc.Name, "'"); - ViewDesc.Name = ViewName.c_str(); - - ITextureView* pDSV = nullptr; - CreateViewInternal(ViewDesc, &pDSV, true); - m_pDefaultDSV.reset(static_cast(pDSV)); - VERIFY(m_pDefaultDSV->GetDesc().ViewType == TEXTURE_VIEW_DEPTH_STENCIL, "Unexpected view type"); - } - - if (this->m_Desc.BindFlags & BIND_UNORDERED_ACCESS) - { - TextureViewDesc ViewDesc; - ViewDesc.ViewType = TEXTURE_VIEW_UNORDERED_ACCESS; - auto ViewName = FormatString("Default UAV of texture '", this->m_Desc.Name, "'"); - ViewDesc.Name = ViewName.c_str(); - - ViewDesc.AccessFlags = UAV_ACCESS_FLAG_READ_WRITE; - ITextureView* pUAV = nullptr; - CreateViewInternal(ViewDesc, &pUAV, true); - m_pDefaultUAV.reset(static_cast(pUAV)); - VERIFY(m_pDefaultUAV->GetDesc().ViewType == TEXTURE_VIEW_UNORDERED_ACCESS, "Unexpected view type"); - } -} - -} // namespace Diligent diff --git a/Graphics/GraphicsEngine/include/TextureBase.hpp b/Graphics/GraphicsEngine/include/TextureBase.hpp new file mode 100644 index 00000000..9e1a29ed --- /dev/null +++ b/Graphics/GraphicsEngine/include/TextureBase.hpp @@ -0,0 +1,515 @@ +/* + * Copyright 2019-2020 Diligent Graphics LLC + * Copyright 2015-2019 Egor Yusov + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + * + * In no event and under no legal theory, whether in tort (including negligence), + * contract, or otherwise, unless required by applicable law (such as deliberate + * and grossly negligent acts) or agreed to in writing, shall any Contributor be + * liable for any damages, including any direct, indirect, special, incidental, + * or consequential damages of any character arising as a result of this License or + * out of the use or inability to use the software (including but not limited to damages + * for loss of goodwill, work stoppage, computer failure or malfunction, or any and + * all other commercial damages or losses), even if such Contributor has been advised + * of the possibility of such damages. + */ + +#pragma once + +/// \file +/// Implementation of the Diligent::TextureBase template class + +#include "Texture.h" +#include "GraphicsTypes.h" +#include "DeviceObjectBase.hpp" +#include "GraphicsAccessories.hpp" +#include "STDAllocator.h" +#include "FormatString.h" +#include + +namespace Diligent +{ + +struct CopyTextureAttribs; + +void ValidateTextureDesc(const TextureDesc& TexDesc); +void ValidateUpdateTextureParams(const TextureDesc& TexDesc, Uint32 MipLevel, Uint32 Slice, const Box& DstBox, const TextureSubResData& SubresData); +void ValidateCopyTextureParams(const CopyTextureAttribs& CopyAttribs); +void ValidateMapTextureParams(const TextureDesc& TexDesc, + Uint32 MipLevel, + Uint32 ArraySlice, + MAP_TYPE MapType, + Uint32 MapFlags, + const Box* pMapRegion); + +/// Base implementation of the ITexture interface + +/// \tparam BaseInterface - base interface that this class will inheret +/// (Diligent::ITextureD3D11, Diligent::ITextureD3D12, +/// Diligent::ITextureGL or Diligent::ITextureVk). +/// \tparam TRenderDeviceImpl - type of the render device implementation +/// (Diligent::RenderDeviceD3D11Impl, Diligent::RenderDeviceD3D12Impl, +/// Diligent::RenderDeviceGLImpl, or Diligent::RenderDeviceVkImpl) +/// \tparam TTextureViewImpl - type of the texture view implementation +/// (Diligent::TextureViewD3D11Impl, Diligent::TextureViewD3D12Impl, +/// Diligent::TextureViewGLImpl or Diligent::TextureViewVkImpl). +/// \tparam TTexViewObjAllocator - type of the allocator that is used to allocate memory for the texture view object instances +template +class TextureBase : public DeviceObjectBase +{ +public: + using TDeviceObjectBase = DeviceObjectBase; + + /// \param pRefCounters - reference counters object that controls the lifetime of this texture. + /// \param TexViewObjAllocator - allocator that is used to allocate memory for the instances of the texture view object. + /// This parameter is only used for debug purposes. + /// \param pDevice - pointer to the device + /// \param Desc - texture description + /// \param bIsDeviceInternal - flag indicating if the texture is an internal device object and + /// must not keep a strong reference to the device + TextureBase(IReferenceCounters* pRefCounters, + TTexViewObjAllocator& TexViewObjAllocator, + TRenderDeviceImpl* pDevice, + const TextureDesc& Desc, + bool bIsDeviceInternal = false) : + TDeviceObjectBase(pRefCounters, pDevice, Desc, bIsDeviceInternal), +#ifdef _DEBUG + m_dbgTexViewObjAllocator(TexViewObjAllocator), +#endif + m_pDefaultSRV(nullptr, STDDeleter(TexViewObjAllocator)), + m_pDefaultRTV(nullptr, STDDeleter(TexViewObjAllocator)), + m_pDefaultDSV(nullptr, STDDeleter(TexViewObjAllocator)), + m_pDefaultUAV(nullptr, STDDeleter(TexViewObjAllocator)) + { + if (this->m_Desc.MipLevels == 0) + { + // Compute the number of levels in the full mipmap chain + if (this->m_Desc.Type == RESOURCE_DIM_TEX_1D || + this->m_Desc.Type == RESOURCE_DIM_TEX_1D_ARRAY) + { + this->m_Desc.MipLevels = ComputeMipLevelsCount(this->m_Desc.Width); + } + else if (this->m_Desc.Type == RESOURCE_DIM_TEX_2D || + this->m_Desc.Type == RESOURCE_DIM_TEX_2D_ARRAY || + this->m_Desc.Type == RESOURCE_DIM_TEX_CUBE || + this->m_Desc.Type == RESOURCE_DIM_TEX_CUBE_ARRAY) + { + this->m_Desc.MipLevels = ComputeMipLevelsCount(this->m_Desc.Width, this->m_Desc.Height); + } + else if (this->m_Desc.Type == RESOURCE_DIM_TEX_3D) + { + this->m_Desc.MipLevels = ComputeMipLevelsCount(this->m_Desc.Width, this->m_Desc.Height, this->m_Desc.Depth); + } + else + { + UNEXPECTED("Unknown texture type"); + } + } + + Uint64 DeviceQueuesMask = pDevice->GetCommandQueueMask(); + DEV_CHECK_ERR((this->m_Desc.CommandQueueMask & DeviceQueuesMask) != 0, + "No bits in the command queue mask (0x", std::hex, this->m_Desc.CommandQueueMask, + ") correspond to one of ", pDevice->GetCommandQueueCount(), " available device command queues"); + this->m_Desc.CommandQueueMask &= DeviceQueuesMask; + + // Validate correctness of texture description + ValidateTextureDesc(this->m_Desc); + } + + IMPLEMENT_QUERY_INTERFACE_IN_PLACE(IID_Texture, TDeviceObjectBase) + + /// Implementaiton of ITexture::CreateView(); calls CreateViewInternal() virtual function that + /// creates texture view for the specific engine implementation. + virtual void CreateView(const struct TextureViewDesc& ViewDesc, ITextureView** ppView) override + { + DEV_CHECK_ERR(ViewDesc.ViewType != TEXTURE_VIEW_UNDEFINED, "Texture view type is not specified"); + if (ViewDesc.ViewType == TEXTURE_VIEW_SHADER_RESOURCE) + DEV_CHECK_ERR(this->m_Desc.BindFlags & BIND_SHADER_RESOURCE, "Attempting to create SRV for texture '", this->m_Desc.Name, "' that was not created with BIND_SHADER_RESOURCE flag"); + else if (ViewDesc.ViewType == TEXTURE_VIEW_UNORDERED_ACCESS) + DEV_CHECK_ERR(this->m_Desc.BindFlags & BIND_UNORDERED_ACCESS, "Attempting to create UAV for texture '", this->m_Desc.Name, "' that was not created with BIND_UNORDERED_ACCESS flag"); + else if (ViewDesc.ViewType == TEXTURE_VIEW_RENDER_TARGET) + DEV_CHECK_ERR(this->m_Desc.BindFlags & BIND_RENDER_TARGET, "Attempting to create RTV for texture '", this->m_Desc.Name, "' that was not created with BIND_RENDER_TARGET flag"); + else if (ViewDesc.ViewType == TEXTURE_VIEW_DEPTH_STENCIL) + DEV_CHECK_ERR(this->m_Desc.BindFlags & BIND_DEPTH_STENCIL, "Attempting to create DSV for texture '", this->m_Desc.Name, "' that was not created with BIND_DEPTH_STENCIL flag"); + else + UNEXPECTED("Unexpected texture view type"); + + CreateViewInternal(ViewDesc, ppView, false); + } + + /// Creates default texture views. + + /// + /// - Creates default shader resource view addressing the entire texture if Diligent::BIND_SHADER_RESOURCE flag is set. + /// - Creates default render target view addressing the most detailed mip level if Diligent::BIND_RENDER_TARGET flag is set. + /// - Creates default depth-stencil view addressing the most detailed mip level if Diligent::BIND_DEPTH_STENCIL flag is set. + /// - Creates default unordered access view addressing the entire texture if Diligent::BIND_UNORDERED_ACCESS flag is set. + /// + /// The function calls CreateViewInternal(). + void CreateDefaultViews(); + + virtual void SetState(RESOURCE_STATE State) override final + { + this->m_State = State; + } + + virtual RESOURCE_STATE GetState() const override final + { + return this->m_State; + } + + bool IsInKnownState() const + { + return this->m_State != RESOURCE_STATE_UNKNOWN; + } + + bool CheckState(RESOURCE_STATE State) const + { + VERIFY((State & (State - 1)) == 0, "Single state is expected"); + VERIFY(IsInKnownState(), "Texture state is unknown"); + return (this->m_State & State) == State; + } + + /// Implementation of ITexture::GetDefaultView(). + virtual ITextureView* GetDefaultView(TEXTURE_VIEW_TYPE ViewType) override + { + switch (ViewType) + { + // clang-format off + case TEXTURE_VIEW_SHADER_RESOURCE: return m_pDefaultSRV.get(); + case TEXTURE_VIEW_RENDER_TARGET: return m_pDefaultRTV.get(); + case TEXTURE_VIEW_DEPTH_STENCIL: return m_pDefaultDSV.get(); + case TEXTURE_VIEW_UNORDERED_ACCESS: return m_pDefaultUAV.get(); + // clang-format on + default: UNEXPECTED("Unknown view type"); return nullptr; + } + } + +protected: + /// Pure virtual function that creates texture view for the specific engine implementation. + virtual void CreateViewInternal(const struct TextureViewDesc& ViewDesc, ITextureView** ppView, bool bIsDefaultView) = 0; + +#ifdef _DEBUG + TTexViewObjAllocator& m_dbgTexViewObjAllocator; +#endif + // WARNING! We cannot use ITextureView here, because ITextureView has no virtual dtor! + /// Default SRV addressing the entire texture + std::unique_ptr> m_pDefaultSRV; + /// Default RTV addressing the most detailed mip level + std::unique_ptr> m_pDefaultRTV; + /// Default DSV addressing the most detailed mip level + std::unique_ptr> m_pDefaultDSV; + /// Default UAV addressing the entire texture + std::unique_ptr> m_pDefaultUAV; + + void CorrectTextureViewDesc(struct TextureViewDesc& ViewDesc); + + RESOURCE_STATE m_State = RESOURCE_STATE_UNKNOWN; +}; + + +template +void TextureBase::CorrectTextureViewDesc(struct TextureViewDesc& ViewDesc) +{ +#define TEX_VIEW_VALIDATION_ERROR(...) LOG_ERROR_AND_THROW("\n Failed to create texture view '", (ViewDesc.Name ? ViewDesc.Name : ""), "' for texture '", this->m_Desc.Name, "': ", ##__VA_ARGS__) + + if (!(ViewDesc.ViewType > TEXTURE_VIEW_UNDEFINED && ViewDesc.ViewType < TEXTURE_VIEW_NUM_VIEWS)) + TEX_VIEW_VALIDATION_ERROR("Texture view type is not specified"); + + if (ViewDesc.MostDetailedMip >= this->m_Desc.MipLevels) + TEX_VIEW_VALIDATION_ERROR("Most detailed mip (", ViewDesc.MostDetailedMip, ") is out of range. The texture has only ", this->m_Desc.MipLevels, " mip ", (this->m_Desc.MipLevels > 1 ? "levels." : "level.")); + + if (ViewDesc.NumMipLevels != REMAINING_MIP_LEVELS && ViewDesc.MostDetailedMip + ViewDesc.NumMipLevels > this->m_Desc.MipLevels) + TEX_VIEW_VALIDATION_ERROR("Most detailed mip (", ViewDesc.MostDetailedMip, ") and number of mip levels in the view (", ViewDesc.NumMipLevels, ") is out of range. The texture has only ", this->m_Desc.MipLevels, " mip ", (this->m_Desc.MipLevels > 1 ? "levels." : "level.")); + + if (ViewDesc.Format == TEX_FORMAT_UNKNOWN) + ViewDesc.Format = GetDefaultTextureViewFormat(this->m_Desc.Format, ViewDesc.ViewType, this->m_Desc.BindFlags); + + if (ViewDesc.TextureDim == RESOURCE_DIM_UNDEFINED) + { + if (this->m_Desc.Type == RESOURCE_DIM_TEX_CUBE || this->m_Desc.Type == RESOURCE_DIM_TEX_CUBE_ARRAY) + { + switch (ViewDesc.ViewType) + { + case TEXTURE_VIEW_SHADER_RESOURCE: + ViewDesc.TextureDim = this->m_Desc.Type; + break; + + case TEXTURE_VIEW_RENDER_TARGET: + case TEXTURE_VIEW_DEPTH_STENCIL: + case TEXTURE_VIEW_UNORDERED_ACCESS: + ViewDesc.TextureDim = RESOURCE_DIM_TEX_2D_ARRAY; + break; + + default: UNEXPECTED("Unexpected view type"); + } + } + else + { + ViewDesc.TextureDim = this->m_Desc.Type; + } + } + + switch (this->m_Desc.Type) + { + case RESOURCE_DIM_TEX_1D: + if (ViewDesc.TextureDim != RESOURCE_DIM_TEX_1D) + { + TEX_VIEW_VALIDATION_ERROR("Incorrect texture type for Texture 1D view: only Texture 1D is allowed"); + } + break; + + case RESOURCE_DIM_TEX_1D_ARRAY: + if (ViewDesc.TextureDim != RESOURCE_DIM_TEX_1D_ARRAY && + ViewDesc.TextureDim != RESOURCE_DIM_TEX_1D) + { + TEX_VIEW_VALIDATION_ERROR("Incorrect view type for Texture 1D Array: only Texture 1D or Texture 1D Array are allowed"); + } + break; + + case RESOURCE_DIM_TEX_2D: + if (ViewDesc.TextureDim != RESOURCE_DIM_TEX_2D_ARRAY && + ViewDesc.TextureDim != RESOURCE_DIM_TEX_2D) + { + TEX_VIEW_VALIDATION_ERROR("Incorrect texture type for Texture 2D view: only Texture 2D or Texture 2D Array are allowed"); + } + break; + + case RESOURCE_DIM_TEX_2D_ARRAY: + if (ViewDesc.TextureDim != RESOURCE_DIM_TEX_2D_ARRAY && + ViewDesc.TextureDim != RESOURCE_DIM_TEX_2D) + { + TEX_VIEW_VALIDATION_ERROR("Incorrect texture type for Texture 2D Array view: only Texture 2D or Texture 2D Array are allowed"); + } + break; + + case RESOURCE_DIM_TEX_3D: + if (ViewDesc.TextureDim != RESOURCE_DIM_TEX_3D) + { + TEX_VIEW_VALIDATION_ERROR("Incorrect texture type for Texture 3D view: only Texture 3D is allowed"); + } + break; + + case RESOURCE_DIM_TEX_CUBE: + if (ViewDesc.ViewType == TEXTURE_VIEW_SHADER_RESOURCE) + { + if (ViewDesc.TextureDim != RESOURCE_DIM_TEX_2D && + ViewDesc.TextureDim != RESOURCE_DIM_TEX_2D_ARRAY && + ViewDesc.TextureDim != RESOURCE_DIM_TEX_CUBE) + { + TEX_VIEW_VALIDATION_ERROR("Incorrect texture type for Texture cube SRV: Texture 2D, Texture 2D array or Texture Cube is allowed"); + } + } + else + { + if (ViewDesc.TextureDim != RESOURCE_DIM_TEX_2D && + ViewDesc.TextureDim != RESOURCE_DIM_TEX_2D_ARRAY) + { + TEX_VIEW_VALIDATION_ERROR("Incorrect texture type for Texture cube non-shader resource view: Texture 2D or Texture 2D array is allowed"); + } + } + break; + + case RESOURCE_DIM_TEX_CUBE_ARRAY: + if (ViewDesc.ViewType == TEXTURE_VIEW_SHADER_RESOURCE) + { + if (ViewDesc.TextureDim != RESOURCE_DIM_TEX_2D && + ViewDesc.TextureDim != RESOURCE_DIM_TEX_2D_ARRAY && + ViewDesc.TextureDim != RESOURCE_DIM_TEX_CUBE && + ViewDesc.TextureDim != RESOURCE_DIM_TEX_CUBE_ARRAY) + { + TEX_VIEW_VALIDATION_ERROR("Incorrect texture type for Texture cube array SRV: Texture 2D, Texture 2D array, Texture Cube or Texture Cube Array is allowed"); + } + } + else + { + if (ViewDesc.TextureDim != RESOURCE_DIM_TEX_2D && + ViewDesc.TextureDim != RESOURCE_DIM_TEX_2D_ARRAY) + { + TEX_VIEW_VALIDATION_ERROR("Incorrect texture type for Texture cube array non-shader resource view: Texture 2D or Texture 2D array is allowed"); + } + } + break; + + default: + UNEXPECTED("Unexpected texture type"); + break; + } + + if (ViewDesc.TextureDim == RESOURCE_DIM_TEX_CUBE) + { + if (ViewDesc.ViewType != TEXTURE_VIEW_SHADER_RESOURCE) + TEX_VIEW_VALIDATION_ERROR("Unexpected view type: SRV is expected"); + if (ViewDesc.NumArraySlices != 6 && ViewDesc.NumArraySlices != 0 && ViewDesc.NumArraySlices != REMAINING_ARRAY_SLICES) + TEX_VIEW_VALIDATION_ERROR("Texture cube SRV is expected to have 6 array slices, while ", ViewDesc.NumArraySlices, " is provided"); + if (ViewDesc.FirstArraySlice != 0) + TEX_VIEW_VALIDATION_ERROR("First slice (", ViewDesc.FirstArraySlice, ") must be 0 for non-array texture cube SRV"); + } + if (ViewDesc.TextureDim == RESOURCE_DIM_TEX_CUBE_ARRAY) + { + if (ViewDesc.ViewType != TEXTURE_VIEW_SHADER_RESOURCE) + TEX_VIEW_VALIDATION_ERROR("Unexpected view type: SRV is expected"); + if (ViewDesc.NumArraySlices != REMAINING_ARRAY_SLICES && (ViewDesc.NumArraySlices % 6) != 0) + TEX_VIEW_VALIDATION_ERROR("Number of slices in texture cube array SRV is expected to be multiple of 6. ", ViewDesc.NumArraySlices, " slices is provided."); + } + + if (ViewDesc.TextureDim == RESOURCE_DIM_TEX_1D || + ViewDesc.TextureDim == RESOURCE_DIM_TEX_2D) + { + if (ViewDesc.FirstArraySlice != 0) + TEX_VIEW_VALIDATION_ERROR("First slice (", ViewDesc.FirstArraySlice, ") must be 0 for non-array texture 1D/2D views"); + + if (ViewDesc.NumArraySlices != REMAINING_ARRAY_SLICES && ViewDesc.NumArraySlices > 1) + TEX_VIEW_VALIDATION_ERROR("Number of slices in the view (", ViewDesc.NumArraySlices, ") must be 1 (or 0) for non-array texture 1D/2D views"); + } + else if (ViewDesc.TextureDim == RESOURCE_DIM_TEX_1D_ARRAY || + ViewDesc.TextureDim == RESOURCE_DIM_TEX_2D_ARRAY || + ViewDesc.TextureDim == RESOURCE_DIM_TEX_CUBE || + ViewDesc.TextureDim == RESOURCE_DIM_TEX_CUBE_ARRAY) + { + if (ViewDesc.FirstArraySlice >= this->m_Desc.ArraySize) + TEX_VIEW_VALIDATION_ERROR("First array slice (", ViewDesc.FirstArraySlice, ") exceeds the number of slices in the texture array (", this->m_Desc.ArraySize, ")"); + + if (ViewDesc.NumArraySlices != REMAINING_ARRAY_SLICES && ViewDesc.FirstArraySlice + ViewDesc.NumArraySlices > this->m_Desc.ArraySize) + TEX_VIEW_VALIDATION_ERROR("First slice (", ViewDesc.FirstArraySlice, ") and number of slices in the view (", ViewDesc.NumArraySlices, ") specify more slices than target texture has (", this->m_Desc.ArraySize, ")"); + } + else if (ViewDesc.TextureDim == RESOURCE_DIM_TEX_3D) + { + auto MipDepth = this->m_Desc.Depth >> ViewDesc.MostDetailedMip; + if (ViewDesc.FirstDepthSlice + ViewDesc.NumDepthSlices > MipDepth) + TEX_VIEW_VALIDATION_ERROR("First slice (", ViewDesc.FirstDepthSlice, ") and number of slices in the view (", ViewDesc.NumDepthSlices, ") specify more slices than target 3D texture mip level has (", MipDepth, ")"); + } + else + { + UNEXPECTED("Unexpected texture dimension"); + } + + if (GetTextureFormatAttribs(ViewDesc.Format).IsTypeless) + { + TEX_VIEW_VALIDATION_ERROR("Texture view format (", GetTextureFormatAttribs(ViewDesc.Format).Name, ") cannot be typeless"); + } + + if ((ViewDesc.Flags & TEXTURE_VIEW_FLAG_ALLOW_MIP_MAP_GENERATION) != 0) + { + if ((this->m_Desc.MiscFlags & MISC_TEXTURE_FLAG_GENERATE_MIPS) == 0) + TEX_VIEW_VALIDATION_ERROR("TEXTURE_VIEW_FLAG_ALLOW_MIP_MAP_GENERATION flag can only set if the texture was created with MISC_TEXTURE_FLAG_GENERATE_MIPS flag"); + + if (ViewDesc.ViewType != TEXTURE_VIEW_SHADER_RESOURCE) + TEX_VIEW_VALIDATION_ERROR("TEXTURE_VIEW_FLAG_ALLOW_MIP_MAP_GENERATION flag can only be used with TEXTURE_VIEW_SHADER_RESOURCE view type"); + } + +#undef TEX_VIEW_VALIDATION_ERROR + + if (ViewDesc.NumMipLevels == 0 || ViewDesc.NumMipLevels == REMAINING_MIP_LEVELS) + { + if (ViewDesc.ViewType == TEXTURE_VIEW_SHADER_RESOURCE) + ViewDesc.NumMipLevels = this->m_Desc.MipLevels - ViewDesc.MostDetailedMip; + else + ViewDesc.NumMipLevels = 1; + } + + if (ViewDesc.NumArraySlices == 0 || ViewDesc.NumArraySlices == REMAINING_ARRAY_SLICES) + { + if (ViewDesc.TextureDim == RESOURCE_DIM_TEX_1D_ARRAY || + ViewDesc.TextureDim == RESOURCE_DIM_TEX_2D_ARRAY || + ViewDesc.TextureDim == RESOURCE_DIM_TEX_CUBE || + ViewDesc.TextureDim == RESOURCE_DIM_TEX_CUBE_ARRAY) + ViewDesc.NumArraySlices = this->m_Desc.ArraySize - ViewDesc.FirstArraySlice; + else if (ViewDesc.TextureDim == RESOURCE_DIM_TEX_3D) + { + auto MipDepth = this->m_Desc.Depth >> ViewDesc.MostDetailedMip; + ViewDesc.NumDepthSlices = MipDepth - ViewDesc.FirstDepthSlice; + } + else + ViewDesc.NumArraySlices = 1; + } + + if ((ViewDesc.ViewType == TEXTURE_VIEW_RENDER_TARGET) && + (ViewDesc.Format == TEX_FORMAT_R8_SNORM || ViewDesc.Format == TEX_FORMAT_RG8_SNORM || ViewDesc.Format == TEX_FORMAT_RGBA8_SNORM || + ViewDesc.Format == TEX_FORMAT_R16_SNORM || ViewDesc.Format == TEX_FORMAT_RG16_SNORM || ViewDesc.Format == TEX_FORMAT_RGBA16_SNORM)) + { + const auto* FmtName = GetTextureFormatAttribs(ViewDesc.Format).Name; + LOG_WARNING_MESSAGE(FmtName, " render target view is created.\n" + "There might be an issue in OpenGL driver on NVidia hardware: when rendering to SNORM textures, all negative values are clamped to zero.\n" + "Use UNORM format instead."); + } +} + +template +void TextureBase::CreateDefaultViews() +{ + const auto& TexFmtAttribs = GetTextureFormatAttribs(this->m_Desc.Format); + if (TexFmtAttribs.ComponentType == COMPONENT_TYPE_UNDEFINED) + { + // Cannot create default view for TYPELESS formats + return; + } + + if (this->m_Desc.BindFlags & BIND_SHADER_RESOURCE) + { + TextureViewDesc ViewDesc; + ViewDesc.ViewType = TEXTURE_VIEW_SHADER_RESOURCE; + auto ViewName = FormatString("Default SRV of texture '", this->m_Desc.Name, "'"); + ViewDesc.Name = ViewName.c_str(); + + ITextureView* pSRV = nullptr; + if ((this->m_Desc.MiscFlags & MISC_TEXTURE_FLAG_GENERATE_MIPS) != 0) + ViewDesc.Flags |= TEXTURE_VIEW_FLAG_ALLOW_MIP_MAP_GENERATION; + CreateViewInternal(ViewDesc, &pSRV, true); + m_pDefaultSRV.reset(static_cast(pSRV)); + VERIFY(m_pDefaultSRV->GetDesc().ViewType == TEXTURE_VIEW_SHADER_RESOURCE, "Unexpected view type"); + } + + if (this->m_Desc.BindFlags & BIND_RENDER_TARGET) + { + TextureViewDesc ViewDesc; + ViewDesc.ViewType = TEXTURE_VIEW_RENDER_TARGET; + auto ViewName = FormatString("Default RTV of texture '", this->m_Desc.Name, "'"); + ViewDesc.Name = ViewName.c_str(); + + ITextureView* pRTV = nullptr; + CreateViewInternal(ViewDesc, &pRTV, true); + m_pDefaultRTV.reset(static_cast(pRTV)); + VERIFY(m_pDefaultRTV->GetDesc().ViewType == TEXTURE_VIEW_RENDER_TARGET, "Unexpected view type"); + } + + if (this->m_Desc.BindFlags & BIND_DEPTH_STENCIL) + { + TextureViewDesc ViewDesc; + ViewDesc.ViewType = TEXTURE_VIEW_DEPTH_STENCIL; + auto ViewName = FormatString("Default DSV of texture '", this->m_Desc.Name, "'"); + ViewDesc.Name = ViewName.c_str(); + + ITextureView* pDSV = nullptr; + CreateViewInternal(ViewDesc, &pDSV, true); + m_pDefaultDSV.reset(static_cast(pDSV)); + VERIFY(m_pDefaultDSV->GetDesc().ViewType == TEXTURE_VIEW_DEPTH_STENCIL, "Unexpected view type"); + } + + if (this->m_Desc.BindFlags & BIND_UNORDERED_ACCESS) + { + TextureViewDesc ViewDesc; + ViewDesc.ViewType = TEXTURE_VIEW_UNORDERED_ACCESS; + auto ViewName = FormatString("Default UAV of texture '", this->m_Desc.Name, "'"); + ViewDesc.Name = ViewName.c_str(); + + ViewDesc.AccessFlags = UAV_ACCESS_FLAG_READ_WRITE; + ITextureView* pUAV = nullptr; + CreateViewInternal(ViewDesc, &pUAV, true); + m_pDefaultUAV.reset(static_cast(pUAV)); + VERIFY(m_pDefaultUAV->GetDesc().ViewType == TEXTURE_VIEW_UNORDERED_ACCESS, "Unexpected view type"); + } +} + +} // namespace Diligent diff --git a/Graphics/GraphicsEngine/include/TextureViewBase.h b/Graphics/GraphicsEngine/include/TextureViewBase.h deleted file mode 100644 index 3a2c4e90..00000000 --- a/Graphics/GraphicsEngine/include/TextureViewBase.h +++ /dev/null @@ -1,135 +0,0 @@ -/* - * Copyright 2019-2020 Diligent Graphics LLC - * Copyright 2015-2019 Egor Yusov - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - * - * In no event and under no legal theory, whether in tort (including negligence), - * contract, or otherwise, unless required by applicable law (such as deliberate - * and grossly negligent acts) or agreed to in writing, shall any Contributor be - * liable for any damages, including any direct, indirect, special, incidental, - * or consequential damages of any character arising as a result of this License or - * out of the use or inability to use the software (including but not limited to damages - * for loss of goodwill, work stoppage, computer failure or malfunction, or any and - * all other commercial damages or losses), even if such Contributor has been advised - * of the possibility of such damages. - */ - -#pragma once - -/// \file -/// Implementation of the Diligent::TextureViewBase template class - -#include "TextureView.h" -#include "DeviceObjectBase.h" -#include "RefCntAutoPtr.h" -#include "GraphicsAccessories.hpp" - -namespace Diligent -{ - -/// Template class implementing base functionality for a texture view interface - -/// \tparam BaseInterface - base interface that this class will inheret -/// (Diligent::ITextureViewD3D11, Diligent::ITextureViewD3D12, -/// Diligent::ITextureViewGL or Diligent::ITextureViewVk). -/// \tparam RenderDeviceImplType - type of the render device implementation -/// (Diligent::RenderDeviceD3D11Impl, Diligent::RenderDeviceD3D12Impl, -/// Diligent::RenderDeviceGLImpl, or Diligent::RenderDeviceVkImpl) -template -class TextureViewBase : public DeviceObjectBase -{ -public: - using TDeviceObjectBase = DeviceObjectBase; - - - /// \param pRefCounters - reference counters object that controls the lifetime of this texture view. - /// \param pDevice - pointer to the render device. - /// \param ViewDesc - texture view description. - /// \param pTexture - pointer to the texture that the view is to be created for. - /// \param bIsDefaultView - flag indicating if the view is default view, and is thus - /// part of the texture object. In this case the view will attach - /// to the texture's reference counters. - TextureViewBase(IReferenceCounters* pRefCounters, - RenderDeviceImplType* pDevice, - const TextureViewDesc& ViewDesc, - class ITexture* pTexture, - bool bIsDefaultView) : - // Default views are created as part of the texture, so we cannot not keep strong - // reference to the texture to avoid cyclic links. Instead, we will attach to the - // reference counters of the texture. - TDeviceObjectBase(pRefCounters, pDevice, ViewDesc), - m_pTexture(pTexture), - // For non-default view, we will keep strong reference to texture - m_spTexture(bIsDefaultView ? nullptr : pTexture) - {} - - IMPLEMENT_QUERY_INTERFACE_IN_PLACE(IID_TextureView, TDeviceObjectBase) - - /// Implementation of ITextureView::SetSampler() - virtual void SetSampler(class ISampler* pSampler) override final - { -#ifdef DEVELOPMENT - if (this->m_Desc.ViewType != TEXTURE_VIEW_SHADER_RESOURCE) - LOG_ERROR("Texture view \"", this->m_Desc.Name, "\": a sampler can be attached to a shader resource view only. The view type is ", GetTexViewTypeLiteralName(this->m_Desc.ViewType)); -#endif - m_pSampler = pSampler; - } - - /// Implementation of ITextureView::GetSampler() - virtual ISampler* GetSampler() override final - { - return m_pSampler; - } - - const ISampler* GetSampler() const - { - return m_pSampler; - } - - /// Implementation of ITextureView::GetTexture() - virtual ITexture* GetTexture() override final - { - return m_pTexture; - } - - const ITexture* GetTexture() const - { - return m_pTexture; - } - - template - TextureType* GetTexture() - { - return ValidatedCast(m_pTexture); - } - - template - TextureType* GetTexture() const - { - return ValidatedCast(m_pTexture); - } - -protected: - /// Strong reference to the sampler - RefCntAutoPtr m_pSampler; - - /// Raw pointer to the texture - ITexture* const m_pTexture; - - /// Strong reference to the texture. Used for non-default views - /// to keep the texture alive - RefCntAutoPtr m_spTexture; -}; - -} // namespace Diligent diff --git a/Graphics/GraphicsEngine/include/TextureViewBase.hpp b/Graphics/GraphicsEngine/include/TextureViewBase.hpp new file mode 100644 index 00000000..f9879264 --- /dev/null +++ b/Graphics/GraphicsEngine/include/TextureViewBase.hpp @@ -0,0 +1,135 @@ +/* + * Copyright 2019-2020 Diligent Graphics LLC + * Copyright 2015-2019 Egor Yusov + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + * + * In no event and under no legal theory, whether in tort (including negligence), + * contract, or otherwise, unless required by applicable law (such as deliberate + * and grossly negligent acts) or agreed to in writing, shall any Contributor be + * liable for any damages, including any direct, indirect, special, incidental, + * or consequential damages of any character arising as a result of this License or + * out of the use or inability to use the software (including but not limited to damages + * for loss of goodwill, work stoppage, computer failure or malfunction, or any and + * all other commercial damages or losses), even if such Contributor has been advised + * of the possibility of such damages. + */ + +#pragma once + +/// \file +/// Implementation of the Diligent::TextureViewBase template class + +#include "TextureView.h" +#include "DeviceObjectBase.hpp" +#include "RefCntAutoPtr.h" +#include "GraphicsAccessories.hpp" + +namespace Diligent +{ + +/// Template class implementing base functionality for a texture view interface + +/// \tparam BaseInterface - base interface that this class will inheret +/// (Diligent::ITextureViewD3D11, Diligent::ITextureViewD3D12, +/// Diligent::ITextureViewGL or Diligent::ITextureViewVk). +/// \tparam RenderDeviceImplType - type of the render device implementation +/// (Diligent::RenderDeviceD3D11Impl, Diligent::RenderDeviceD3D12Impl, +/// Diligent::RenderDeviceGLImpl, or Diligent::RenderDeviceVkImpl) +template +class TextureViewBase : public DeviceObjectBase +{ +public: + using TDeviceObjectBase = DeviceObjectBase; + + + /// \param pRefCounters - reference counters object that controls the lifetime of this texture view. + /// \param pDevice - pointer to the render device. + /// \param ViewDesc - texture view description. + /// \param pTexture - pointer to the texture that the view is to be created for. + /// \param bIsDefaultView - flag indicating if the view is default view, and is thus + /// part of the texture object. In this case the view will attach + /// to the texture's reference counters. + TextureViewBase(IReferenceCounters* pRefCounters, + RenderDeviceImplType* pDevice, + const TextureViewDesc& ViewDesc, + class ITexture* pTexture, + bool bIsDefaultView) : + // Default views are created as part of the texture, so we cannot not keep strong + // reference to the texture to avoid cyclic links. Instead, we will attach to the + // reference counters of the texture. + TDeviceObjectBase(pRefCounters, pDevice, ViewDesc), + m_pTexture(pTexture), + // For non-default view, we will keep strong reference to texture + m_spTexture(bIsDefaultView ? nullptr : pTexture) + {} + + IMPLEMENT_QUERY_INTERFACE_IN_PLACE(IID_TextureView, TDeviceObjectBase) + + /// Implementation of ITextureView::SetSampler() + virtual void SetSampler(class ISampler* pSampler) override final + { +#ifdef DEVELOPMENT + if (this->m_Desc.ViewType != TEXTURE_VIEW_SHADER_RESOURCE) + LOG_ERROR("Texture view \"", this->m_Desc.Name, "\": a sampler can be attached to a shader resource view only. The view type is ", GetTexViewTypeLiteralName(this->m_Desc.ViewType)); +#endif + m_pSampler = pSampler; + } + + /// Implementation of ITextureView::GetSampler() + virtual ISampler* GetSampler() override final + { + return m_pSampler; + } + + const ISampler* GetSampler() const + { + return m_pSampler; + } + + /// Implementation of ITextureView::GetTexture() + virtual ITexture* GetTexture() override final + { + return m_pTexture; + } + + const ITexture* GetTexture() const + { + return m_pTexture; + } + + template + TextureType* GetTexture() + { + return ValidatedCast(m_pTexture); + } + + template + TextureType* GetTexture() const + { + return ValidatedCast(m_pTexture); + } + +protected: + /// Strong reference to the sampler + RefCntAutoPtr m_pSampler; + + /// Raw pointer to the texture + ITexture* const m_pTexture; + + /// Strong reference to the texture. Used for non-default views + /// to keep the texture alive + RefCntAutoPtr m_spTexture; +}; + +} // namespace Diligent diff --git a/Graphics/GraphicsEngine/include/pch.h b/Graphics/GraphicsEngine/include/pch.h index aa47fb15..1472ead3 100644 --- a/Graphics/GraphicsEngine/include/pch.h +++ b/Graphics/GraphicsEngine/include/pch.h @@ -41,5 +41,5 @@ #include "RefCntAutoPtr.h" #include "Errors.h" #include "DebugUtilities.h" -#include "RenderDeviceBase.h" -#include "DeviceContextBase.h" \ No newline at end of file +#include "RenderDeviceBase.hpp" +#include "DeviceContextBase.hpp" \ No newline at end of file diff --git a/Graphics/GraphicsEngine/src/ResourceMapping.cpp b/Graphics/GraphicsEngine/src/ResourceMapping.cpp index 11acb714..bf34056c 100644 --- a/Graphics/GraphicsEngine/src/ResourceMapping.cpp +++ b/Graphics/GraphicsEngine/src/ResourceMapping.cpp @@ -26,8 +26,8 @@ */ #include "pch.h" -#include "ResourceMappingImpl.h" -#include "DeviceObjectBase.h" +#include "ResourceMappingImpl.hpp" +#include "DeviceObjectBase.hpp" using namespace std; -- cgit v1.2.3