diff --git a/Source/Engine/Graphics/Config.h b/Source/Engine/Graphics/Config.h
index 88fbc780d..54004cdc0 100644
--- a/Source/Engine/Graphics/Config.h
+++ b/Source/Engine/Graphics/Config.h
@@ -14,6 +14,12 @@
// Maximum amount of binded unordered access resources at the same time
#define GPU_MAX_UA_BINDED 2
+// Maximum amount of binded texture sampler resources at the same time
+#define GPU_MAX_SAMPLER_BINDED 16
+
+// Amount of initial slots used for global samplers (static, 4 common samplers + 2 comparision samplers)
+#define GPU_STATIC_SAMPLERS_COUNT 6
+
// Maximum amount of binded vertex buffers at the same time
#define GPU_MAX_VB_BINDED 4
diff --git a/Source/Engine/Graphics/GPUContext.h b/Source/Engine/Graphics/GPUContext.h
index 5e1219fa2..c46e81d46 100644
--- a/Source/Engine/Graphics/GPUContext.h
+++ b/Source/Engine/Graphics/GPUContext.h
@@ -16,6 +16,7 @@ class GPUShaderProgramCS;
class GPUBuffer;
class GPUPipelineState;
class GPUTexture;
+class GPUSampler;
class GPUDevice;
class GPUResource;
class GPUResourceView;
@@ -396,6 +397,13 @@ public:
/// The index buffer.
API_FUNCTION() virtual void BindIB(GPUBuffer* indexBuffer) = 0;
+ ///
+ /// Binds the texture sampler to the pipeline.
+ ///
+ /// The slot index.
+ /// The sampler.
+ API_FUNCTION() virtual void BindSampler(int32 slot, GPUSampler* sampler) = 0;
+
public:
///
diff --git a/Source/Engine/Graphics/GPUDevice.h b/Source/Engine/Graphics/GPUDevice.h
index 8290df1aa..45dd77c06 100644
--- a/Source/Engine/Graphics/GPUDevice.h
+++ b/Source/Engine/Graphics/GPUDevice.h
@@ -18,6 +18,7 @@ class GPUShader;
class GPUTimerQuery;
class GPUTexture;
class GPUBuffer;
+class GPUSampler;
class GPUPipelineState;
class GPUSwapChain;
class Shader;
@@ -360,6 +361,12 @@ public:
/// The buffer.
API_FUNCTION() virtual GPUBuffer* CreateBuffer(const StringView& name = StringView::Empty) = 0;
+ ///
+ /// Creates the texture sampler.
+ ///
+ /// The sampler.
+ API_FUNCTION() virtual GPUSampler* CreateSampler() = 0;
+
///
/// Creates the native window swap chain.
///
diff --git a/Source/Engine/Graphics/GPULimits.h b/Source/Engine/Graphics/GPULimits.h
index 401ce5178..0bf9c1437 100644
--- a/Source/Engine/Graphics/GPULimits.h
+++ b/Source/Engine/Graphics/GPULimits.h
@@ -314,4 +314,9 @@ DECLARE_SCRIPTING_TYPE_MINIMAL(GPULimits);
/// The maximum size of the cube texture (both width and height).
///
API_FIELD() int32 MaximumTextureCubeSize;
+
+ ///
+ /// The maximum degree of anisotropic filtering used for texture sampling.
+ ///
+ API_FIELD() float MaximumSamplerAnisotropy;
};
diff --git a/Source/Engine/Graphics/GPUResource.h b/Source/Engine/Graphics/GPUResource.h
index 5d4eff81c..5f5a01d41 100644
--- a/Source/Engine/Graphics/GPUResource.h
+++ b/Source/Engine/Graphics/GPUResource.h
@@ -22,7 +22,7 @@ public:
///
/// GPU Resources types.
///
- DECLARE_ENUM_9(ResourceType, RenderTarget, Texture, CubeTexture, VolumeTexture, Buffer, Shader, PipelineState, Descriptor, Query);
+ DECLARE_ENUM_10(ResourceType, RenderTarget, Texture, CubeTexture, VolumeTexture, Buffer, Shader, PipelineState, Descriptor, Query, Sampler);
///
/// GPU Resources object types. Used to detect Texture objects from subset of Types: RenderTarget, Texture, CubeTexture, VolumeTexture which use the same API object.
diff --git a/Source/Engine/Graphics/Textures/GPUSamplerDescription.cpp b/Source/Engine/Graphics/Textures/GPUSampler.cpp
similarity index 53%
rename from Source/Engine/Graphics/Textures/GPUSamplerDescription.cpp
rename to Source/Engine/Graphics/Textures/GPUSampler.cpp
index e4b868cc6..87a2e7924 100644
--- a/Source/Engine/Graphics/Textures/GPUSamplerDescription.cpp
+++ b/Source/Engine/Graphics/Textures/GPUSampler.cpp
@@ -1,20 +1,23 @@
// Copyright (c) 2012-2021 Wojciech Figat. All rights reserved.
+#include "GPUSampler.h"
#include "GPUSamplerDescription.h"
#include "Engine/Core/Types/String.h"
+#include "Engine/Graphics/GPUDevice.h"
+
+GPUSamplerDescription GPUSamplerDescription::New(GPUSamplerFilter filter, GPUSamplerAddressMode addressMode)
+{
+ GPUSamplerDescription desc;
+ desc.Clear();
+ desc.Filter = filter;
+ desc.AddressU = desc.AddressV = desc.AddressW = addressMode;
+ return desc;
+}
void GPUSamplerDescription::Clear()
{
- Filter = GPUSamplerFilter::Point;
- AddressU = GPUSamplerAddressMode::Wrap;
- AddressV = GPUSamplerAddressMode::Wrap;
- AddressW = GPUSamplerAddressMode::Wrap;
- MipBias = 0;
- MaxAnisotropy = 0;
- MinMipLevel = 0;
+ Platform::MemoryClear(this, sizeof(GPUSamplerDescription));
MaxMipLevel = MAX_float;
- BorderColor = GPUSamplerBorderColor::TransparentBlack;
- SamplerComparisonFunction = GPUSamplerCompareFunction::Never;
}
bool GPUSamplerDescription::Equals(const GPUSamplerDescription& other) const
@@ -28,12 +31,12 @@ bool GPUSamplerDescription::Equals(const GPUSamplerDescription& other) const
&& MinMipLevel == other.MinMipLevel
&& MaxMipLevel == other.MaxMipLevel
&& BorderColor == other.BorderColor
- && SamplerComparisonFunction == other.SamplerComparisonFunction;
+ && ComparisonFunction == other.ComparisonFunction;
}
String GPUSamplerDescription::ToString() const
{
- return String::Format(TEXT("Filter: {}, Address: {}x{}x{}, MipBias: {}, MaxAnisotropy: {}, MinMipLevel: {}, MaxMipLevel: {}, BorderColor: {}, SamplerComparisonFunction: {}"),
+ return String::Format(TEXT("Filter: {}, Address: {}x{}x{}, MipBias: {}, MaxAnisotropy: {}, MinMipLevel: {}, MaxMipLevel: {}, BorderColor: {}, ComparisonFunction: {}"),
(int32)Filter,
(int32)AddressU,
(int32)AddressV,
@@ -43,7 +46,7 @@ String GPUSamplerDescription::ToString() const
MinMipLevel,
MaxMipLevel,
(int32)BorderColor,
- (int32)SamplerComparisonFunction);
+ (int32)ComparisonFunction);
}
uint32 GetHash(const GPUSamplerDescription& key)
@@ -57,6 +60,57 @@ uint32 GetHash(const GPUSamplerDescription& key)
hashCode = (hashCode * 397) ^ (uint32)key.MinMipLevel;
hashCode = (hashCode * 397) ^ (uint32)key.MaxMipLevel;
hashCode = (hashCode * 397) ^ (uint32)key.BorderColor;
- hashCode = (hashCode * 397) ^ (uint32)key.SamplerComparisonFunction;
+ hashCode = (hashCode * 397) ^ (uint32)key.ComparisonFunction;
return hashCode;
}
+
+GPUSampler* GPUSampler::Spawn(const SpawnParams& params)
+{
+ return GPUDevice::Instance->CreateSampler();
+}
+
+GPUSampler* GPUSampler::New()
+{
+ return GPUDevice::Instance->CreateSampler();
+}
+
+GPUSampler::GPUSampler()
+{
+}
+
+bool GPUSampler::Init(const GPUSamplerDescription& desc)
+{
+ ReleaseGPU();
+ _desc = desc;
+ if (OnInit())
+ {
+ ReleaseGPU();
+ LOG(Warning, "Cannot initialize sampler. Description: {0}", desc.ToString());
+ return true;
+ }
+ return false;
+}
+
+String GPUSampler::ToString() const
+{
+#if GPU_ENABLE_RESOURCE_NAMING
+ return String::Format(TEXT("Sampler {0}, {1}"), GetName(), _desc.ToString());
+#else
+ return TEXT("Sampler");
+#endif
+}
+
+GPUResource::ResourceType GPUSampler::GetResourceType() const
+{
+ return ResourceType::Sampler;
+}
+
+GPUResource::ObjectType GPUSampler::GetObjectType() const
+{
+ return ObjectType::Other;
+}
+
+void GPUSampler::OnReleaseGPU()
+{
+ _desc.Clear();
+}
diff --git a/Source/Engine/Graphics/Textures/GPUSampler.h b/Source/Engine/Graphics/Textures/GPUSampler.h
new file mode 100644
index 000000000..70d2f40ea
--- /dev/null
+++ b/Source/Engine/Graphics/Textures/GPUSampler.h
@@ -0,0 +1,58 @@
+// Copyright (c) 2012-2021 Wojciech Figat. All rights reserved.
+
+#pragma once
+
+#include "GPUSamplerDescription.h"
+#include "../GPUResource.h"
+
+///
+/// GPU texture sampler object.
+///
+///
+API_CLASS(Sealed) class FLAXENGINE_API GPUSampler : public GPUResource
+{
+DECLARE_SCRIPTING_TYPE_NO_SPAWN(GPUSampler);
+ static GPUSampler* Spawn(const SpawnParams& params);
+ static GPUSampler* New();
+
+protected:
+
+ GPUSamplerDescription _desc;
+
+ GPUSampler();
+
+public:
+
+ ///
+ /// Gets sampler description structure.
+ ///
+ API_PROPERTY() const GPUSamplerDescription& GetDescription() const
+ {
+ return _desc;
+ }
+
+public:
+
+ ///
+ /// Creates new sampler.
+ ///
+ /// The sampler description.
+ /// True if cannot create sampler, otherwise false.
+ API_FUNCTION() bool Init(API_PARAM(Ref) const GPUSamplerDescription& desc);
+
+protected:
+
+ virtual bool OnInit() = 0;
+
+public:
+
+ // [GPUResource]
+ String ToString() const override;
+ ResourceType GetResourceType() const final override;
+ ObjectType GetObjectType() const final override;
+
+protected:
+
+ // [GPUResource]
+ void OnReleaseGPU() override;
+};
diff --git a/Source/Engine/Graphics/Textures/GPUSamplerDescription.cs b/Source/Engine/Graphics/Textures/GPUSamplerDescription.cs
new file mode 100644
index 000000000..c20f51c0e
--- /dev/null
+++ b/Source/Engine/Graphics/Textures/GPUSamplerDescription.cs
@@ -0,0 +1,76 @@
+// Copyright (c) 2012-2021 Wojciech Figat. All rights reserved.
+
+using System;
+
+namespace FlaxEngine
+{
+ partial struct GPUSamplerDescription : IEquatable
+ {
+ ///
+ /// Clears description.
+ ///
+ public void Clear()
+ {
+ this = new GPUSamplerDescription();
+ MaxMipLevel = float.MaxValue;
+ }
+
+ ///
+ /// Creates a new with default settings.
+ ///
+ /// The filtering method.
+ /// The addressing mode.
+ /// A new instance of class.
+ public static GPUSamplerDescription New(GPUSamplerFilter filter = GPUSamplerFilter.Point, GPUSamplerAddressMode addressMode = GPUSamplerAddressMode.Wrap)
+ {
+ return new GPUSamplerDescription
+ {
+ Filter = filter,
+ AddressU = addressMode,
+ AddressV = addressMode,
+ AddressW = addressMode,
+ MaxMipLevel = float.MaxValue,
+ };
+ }
+
+ ///
+ public bool Equals(GPUSamplerDescription other)
+ {
+ return Filter == other.Filter &&
+ AddressU == other.AddressU &&
+ AddressV == other.AddressV &&
+ AddressW == other.AddressW &&
+ Mathf.NearEqual(MipBias, other.MipBias) &&
+ Mathf.NearEqual(MinMipLevel, other.MinMipLevel) &&
+ Mathf.NearEqual(MaxMipLevel, other.MaxMipLevel) &&
+ MaxAnisotropy == other.MaxAnisotropy &&
+ BorderColor == other.BorderColor &&
+ ComparisonFunction == other.ComparisonFunction;
+ }
+
+ ///
+ public override bool Equals(object obj)
+ {
+ return obj is GPUSamplerDescription other && Equals(other);
+ }
+
+ ///
+ public override int GetHashCode()
+ {
+ unchecked
+ {
+ var hashCode = (int)Filter;
+ hashCode = (hashCode * 397) ^ (int)AddressU;
+ hashCode = (hashCode * 397) ^ (int)AddressV;
+ hashCode = (hashCode * 397) ^ (int)AddressW;
+ hashCode = (hashCode * 397) ^ (int)MipBias;
+ hashCode = (hashCode * 397) ^ (int)MinMipLevel;
+ hashCode = (hashCode * 397) ^ (int)MaxMipLevel;
+ hashCode = (hashCode * 397) ^ MaxAnisotropy;
+ hashCode = (hashCode * 397) ^ (int)BorderColor;
+ hashCode = (hashCode * 397) ^ (int)ComparisonFunction;
+ return hashCode;
+ }
+ }
+ }
+}
diff --git a/Source/Engine/Graphics/Textures/GPUSamplerDescription.h b/Source/Engine/Graphics/Textures/GPUSamplerDescription.h
index 1b91b419c..2674e88a9 100644
--- a/Source/Engine/Graphics/Textures/GPUSamplerDescription.h
+++ b/Source/Engine/Graphics/Textures/GPUSamplerDescription.h
@@ -4,49 +4,57 @@
#include "Engine/Graphics/Enums.h"
-class String;
-
///
/// GPU sampler filter modes.
///
-enum class GPUSamplerFilter
+API_ENUM() enum class GPUSamplerFilter
{
+ /// Filter using the nearest found pixel. Texture appears pixelated.
Point = 0,
+ /// Filter using the linear average of the nearby pixels. Texture appears blurry.
Bilinear = 1,
+ /// Filter using the linear average of the nearby pixels and nearby mipmaps. Texture appears blurry.
Trilinear = 2,
+ /// Filter using the anisotropic filtering that improves quality when viewing textures at a steep angles. Texture appears sharp at extreme viewing angles.
Anisotropic = 3,
- MAX
+ API_ENUM(Attributes="HideInEditor") MAX
};
///
/// GPU sampler address modes.
///
-enum class GPUSamplerAddressMode
+API_ENUM() enum class GPUSamplerAddressMode
{
+ /// Texture coordinates wrap back to the valid range.
Wrap = 0,
+ /// Texture coordinates are clamped within the valid range.
Clamp = 1,
+ /// Texture coordinates flip every time the size of the valid range is passed.
Mirror = 2,
+ /// Texture coordinates outside of the valid range will return a separately set border color.
Border = 3,
- MAX
+ API_ENUM(Attributes="HideInEditor") MAX
};
///
/// GPU sampler comparision function types.
///
-enum class GPUSamplerCompareFunction
+API_ENUM() enum class GPUSamplerCompareFunction
{
+ /// Never pass the comparison.
Never = 0,
+ /// If the source data is less than the destination data, the comparison passes.
Less = 1,
- MAX
+ API_ENUM(Attributes="HideInEditor") MAX
};
///
/// GPU sampler border color types.
///
-enum class GPUSamplerBorderColor
+API_ENUM() enum class GPUSamplerBorderColor
{
///
/// Indicates black, with the alpha component as fully transparent.
@@ -63,105 +71,93 @@ enum class GPUSamplerBorderColor
///
OpaqueWhite = 2,
- Maximum
+ API_ENUM(Attributes="HideInEditor") MAX
};
///
/// A common description for all samplers.
///
-struct FLAXENGINE_API GPUSamplerDescription
+API_STRUCT() struct FLAXENGINE_API GPUSamplerDescription
{
-public:
+DECLARE_SCRIPTING_TYPE_MINIMAL(GPUSamplerDescription);
///
/// The filtering method to use when sampling a texture.
///
- GPUSamplerFilter Filter;
+ API_FIELD() GPUSamplerFilter Filter;
///
/// The addressing mode for outside [0..1] range for U coordinate.
///
- GPUSamplerAddressMode AddressU;
+ API_FIELD() GPUSamplerAddressMode AddressU;
///
/// The addressing mode for outside [0..1] range for V coordinate.
///
- GPUSamplerAddressMode AddressV;
+ API_FIELD() GPUSamplerAddressMode AddressV;
///
/// The addressing mode for outside [0..1] range for W coordinate.
///
- GPUSamplerAddressMode AddressW;
+ API_FIELD() GPUSamplerAddressMode AddressW;
///
/// The mip bias to be added to mipmap LOD calculation.
///
- float MipBias;
+ API_FIELD() float MipBias;
///
/// The minimum mip map level that will be used, where 0 is the highest resolution mip level.
///
- float MinMipLevel;
+ API_FIELD() float MinMipLevel;
///
/// The maximum mip map level that will be used, where 0 is the highest resolution mip level. To have no upper limit on LOD set this to a large value such as MAX_float.
///
- float MaxMipLevel;
+ API_FIELD() float MaxMipLevel;
///
/// The maximum anisotropy value.
///
- int32 MaxAnisotropy;
+ API_FIELD() int32 MaxAnisotropy;
///
/// The border color to use if Border is specified for AddressU, AddressV, or AddressW.
///
- GPUSamplerBorderColor BorderColor;
+ API_FIELD() GPUSamplerBorderColor BorderColor;
///
/// A function that compares sampled data against existing sampled data.
///
- GPUSamplerCompareFunction SamplerComparisonFunction;
+ API_FIELD() GPUSamplerCompareFunction ComparisonFunction;
public:
///
- /// Clears description to the default values.
+ /// Creates a new with default settings.
///
+ /// The filtering method.
+ /// The addressing mode.
+ /// A new instance of class.
+ static GPUSamplerDescription New(GPUSamplerFilter filter = GPUSamplerFilter::Point, GPUSamplerAddressMode addressMode = GPUSamplerAddressMode::Wrap);
+
+public:
+
void Clear();
+ bool Equals(const GPUSamplerDescription& other) const;
+ String ToString() const;
public:
- ///
- /// Compares with other instance of SamplerDescription
- ///
- /// The other object to compare.
- /// True if objects are the same, otherwise false.
- bool Equals(const GPUSamplerDescription& other) const;
-
- ///
- /// Implements the operator ==.
- ///
- /// The other description.
- /// The result of the operator.
FORCE_INLINE bool operator==(const GPUSamplerDescription& other) const
{
return Equals(other);
}
- ///
- /// Implements the operator !=.
- ///
- /// The other description.
- /// The result of the operator.
FORCE_INLINE bool operator!=(const GPUSamplerDescription& other) const
{
return !Equals(other);
}
-
-public:
-
- String ToString() const;
};
uint32 GetHash(const GPUSamplerDescription& key);
diff --git a/Source/Engine/GraphicsDevice/DirectX/DX11/GPUContextDX11.cpp b/Source/Engine/GraphicsDevice/DirectX/DX11/GPUContextDX11.cpp
index b0cc5cb63..0998ac4d6 100644
--- a/Source/Engine/GraphicsDevice/DirectX/DX11/GPUContextDX11.cpp
+++ b/Source/Engine/GraphicsDevice/DirectX/DX11/GPUContextDX11.cpp
@@ -8,6 +8,7 @@
#include "GPUPipelineStateDX11.h"
#include "GPUTextureDX11.h"
#include "GPUBufferDX11.h"
+#include "GPUSamplerDX11.h"
#include "Engine/GraphicsDevice/DirectX/RenderToolsDX.h"
#include "Engine/Core/Math/Viewport.h"
#include "Engine/Core/Math/Rectangle.h"
@@ -384,6 +385,15 @@ void GPUContextDX11::BindIB(GPUBuffer* indexBuffer)
}
}
+void GPUContextDX11::BindSampler(int32 slot, GPUSampler* sampler)
+{
+ const auto samplerDX11 = sampler ? static_cast(sampler)->SamplerState : nullptr;
+ _context->VSSetSamplers(slot, 1, &samplerDX11);
+ _context->DSSetSamplers(slot, 1, &samplerDX11);
+ _context->PSSetSamplers(slot, 1, &samplerDX11);
+ _context->CSSetSamplers(slot, 1, &samplerDX11);
+}
+
void GPUContextDX11::UpdateCB(GPUConstantBuffer* cb, const void* data)
{
ASSERT(data && cb);
diff --git a/Source/Engine/GraphicsDevice/DirectX/DX11/GPUContextDX11.h b/Source/Engine/GraphicsDevice/DirectX/DX11/GPUContextDX11.h
index dffeae00c..0ec811565 100644
--- a/Source/Engine/GraphicsDevice/DirectX/DX11/GPUContextDX11.h
+++ b/Source/Engine/GraphicsDevice/DirectX/DX11/GPUContextDX11.h
@@ -122,6 +122,7 @@ public:
void BindUA(int32 slot, GPUResourceView* view) override;
void BindVB(const Span& vertexBuffers, const uint32* vertexBuffersOffsets = nullptr) override;
void BindIB(GPUBuffer* indexBuffer) override;
+ void BindSampler(int32 slot, GPUSampler* sampler) override;
void UpdateCB(GPUConstantBuffer* cb, const void* data) override;
void Dispatch(GPUShaderProgramCS* shader, uint32 threadGroupCountX, uint32 threadGroupCountY, uint32 threadGroupCountZ) override;
void DispatchIndirect(GPUShaderProgramCS* shader, GPUBuffer* bufferForArgs, uint32 offsetForArgs) override;
diff --git a/Source/Engine/GraphicsDevice/DirectX/DX11/GPUDeviceDX11.cpp b/Source/Engine/GraphicsDevice/DirectX/DX11/GPUDeviceDX11.cpp
index b0db7da93..935d8a7c0 100644
--- a/Source/Engine/GraphicsDevice/DirectX/DX11/GPUDeviceDX11.cpp
+++ b/Source/Engine/GraphicsDevice/DirectX/DX11/GPUDeviceDX11.cpp
@@ -9,6 +9,7 @@
#include "GPUTextureDX11.h"
#include "GPUTimerQueryDX11.h"
#include "GPUBufferDX11.h"
+#include "GPUSamplerDX11.h"
#include "GPUSwapChainDX11.h"
#include "Engine/Core/Log.h"
#include "Engine/Core/Utilities.h"
@@ -324,6 +325,7 @@ bool GPUDeviceDX11::Init()
limits.MaximumTexture2DArraySize = D3D11_REQ_TEXTURE2D_ARRAY_AXIS_DIMENSION;
limits.MaximumTexture3DSize = D3D11_REQ_TEXTURE3D_U_V_OR_W_DIMENSION;
limits.MaximumTextureCubeSize = D3D11_REQ_TEXTURECUBE_DIMENSION;
+ limits.MaximumSamplerAnisotropy = D3D11_DEFAULT_MAX_ANISOTROPY;
}
else
{
@@ -346,6 +348,7 @@ bool GPUDeviceDX11::Init()
limits.MaximumTexture2DArraySize = D3D10_REQ_TEXTURE2D_ARRAY_AXIS_DIMENSION;
limits.MaximumTexture3DSize = D3D10_REQ_TEXTURE3D_U_V_OR_W_DIMENSION;
limits.MaximumTextureCubeSize = D3D10_REQ_TEXTURECUBE_DIMENSION;
+ limits.MaximumSamplerAnisotropy = D3D10_DEFAULT_MAX_ANISOTROPY;
}
for (int32 i = 0; i < static_cast(PixelFormat::MAX); i++)
@@ -685,6 +688,11 @@ GPUBuffer* GPUDeviceDX11::CreateBuffer(const StringView& name)
return New(this, name);
}
+GPUSampler* GPUDeviceDX11::CreateSampler()
+{
+ return New(this);
+}
+
GPUSwapChain* GPUDeviceDX11::CreateSwapChain(Window* window)
{
return New(this, window);
diff --git a/Source/Engine/GraphicsDevice/DirectX/DX11/GPUDeviceDX11.h b/Source/Engine/GraphicsDevice/DirectX/DX11/GPUDeviceDX11.h
index d75c0b88a..08f770514 100644
--- a/Source/Engine/GraphicsDevice/DirectX/DX11/GPUDeviceDX11.h
+++ b/Source/Engine/GraphicsDevice/DirectX/DX11/GPUDeviceDX11.h
@@ -99,6 +99,7 @@ public:
GPUPipelineState* CreatePipelineState() override;
GPUTimerQuery* CreateTimerQuery() override;
GPUBuffer* CreateBuffer(const StringView& name) override;
+ GPUSampler* CreateSampler() override;
GPUSwapChain* CreateSwapChain(Window* window) override;
};
diff --git a/Source/Engine/GraphicsDevice/DirectX/DX11/GPUSamplerDX11.cpp b/Source/Engine/GraphicsDevice/DirectX/DX11/GPUSamplerDX11.cpp
new file mode 100644
index 000000000..4927523a4
--- /dev/null
+++ b/Source/Engine/GraphicsDevice/DirectX/DX11/GPUSamplerDX11.cpp
@@ -0,0 +1,128 @@
+// Copyright (c) 2012-2021 Wojciech Figat. All rights reserved.
+
+#if GRAPHICS_API_DIRECTX11
+
+#include "GPUSamplerDX11.h"
+
+D3D11_TEXTURE_ADDRESS_MODE ToDX11(GPUSamplerAddressMode value)
+{
+ switch (value)
+ {
+ case GPUSamplerAddressMode::Wrap:
+ return D3D11_TEXTURE_ADDRESS_WRAP;
+ case GPUSamplerAddressMode::Clamp:
+ return D3D11_TEXTURE_ADDRESS_CLAMP;
+ case GPUSamplerAddressMode::Mirror:
+ return D3D11_TEXTURE_ADDRESS_MIRROR;
+ case GPUSamplerAddressMode::Border:
+ return D3D11_TEXTURE_ADDRESS_BORDER;
+ default:
+ return (D3D11_TEXTURE_ADDRESS_MODE)-1;
+ }
+}
+
+bool GPUSamplerDX11::OnInit()
+{
+ D3D11_SAMPLER_DESC samplerDesc;
+ if (_desc.ComparisonFunction == GPUSamplerCompareFunction::Never)
+ {
+ switch (_desc.Filter)
+ {
+ case GPUSamplerFilter::Point:
+ samplerDesc.Filter = D3D11_FILTER_MIN_MAG_MIP_POINT;
+ break;
+ case GPUSamplerFilter::Bilinear:
+ samplerDesc.Filter = D3D11_FILTER_MIN_MAG_LINEAR_MIP_POINT;
+ break;
+ case GPUSamplerFilter::Trilinear:
+ samplerDesc.Filter = D3D11_FILTER_MIN_MAG_MIP_LINEAR;
+ break;
+ case GPUSamplerFilter::Anisotropic:
+ samplerDesc.Filter = D3D11_FILTER_ANISOTROPIC;
+ break;
+ default:
+ return true;
+ }
+ }
+ else
+ {
+ switch (_desc.Filter)
+ {
+ case GPUSamplerFilter::Point:
+ samplerDesc.Filter = D3D11_FILTER_COMPARISON_MIN_MAG_MIP_POINT;
+ break;
+ case GPUSamplerFilter::Bilinear:
+ samplerDesc.Filter = D3D11_FILTER_COMPARISON_MIN_LINEAR_MAG_MIP_POINT;
+ break;
+ case GPUSamplerFilter::Trilinear:
+ samplerDesc.Filter = D3D11_FILTER_COMPARISON_MIN_MAG_MIP_LINEAR;
+ break;
+ case GPUSamplerFilter::Anisotropic:
+ samplerDesc.Filter = D3D11_FILTER_COMPARISON_ANISOTROPIC;
+ break;
+ default:
+ return true;
+ }
+ }
+ samplerDesc.AddressU = ToDX11(_desc.AddressU);
+ samplerDesc.AddressV = ToDX11(_desc.AddressV);
+ samplerDesc.AddressW = ToDX11(_desc.AddressW);
+ samplerDesc.MipLODBias = _desc.MipBias;
+ samplerDesc.MaxAnisotropy = _desc.MaxAnisotropy;
+ switch (_desc.ComparisonFunction)
+ {
+ case GPUSamplerCompareFunction::Never:
+ samplerDesc.ComparisonFunc = D3D11_COMPARISON_NEVER;
+ break;
+ case GPUSamplerCompareFunction::Less:
+ samplerDesc.ComparisonFunc = D3D11_COMPARISON_LESS;
+ break;
+ default:
+ return true;
+ }
+ switch (_desc.BorderColor)
+ {
+ case GPUSamplerBorderColor::TransparentBlack:
+ samplerDesc.BorderColor[0] = 0;
+ samplerDesc.BorderColor[1] = 0;
+ samplerDesc.BorderColor[2] = 0;
+ samplerDesc.BorderColor[3] = 0;
+ break;
+ case GPUSamplerBorderColor::OpaqueBlack:
+ samplerDesc.BorderColor[0] = 0;
+ samplerDesc.BorderColor[1] = 0;
+ samplerDesc.BorderColor[2] = 0;
+ samplerDesc.BorderColor[3] = 1.0f;
+ break;
+ case GPUSamplerBorderColor::OpaqueWhite:
+ samplerDesc.BorderColor[0] = 1.0f;
+ samplerDesc.BorderColor[1] = 1.0f;
+ samplerDesc.BorderColor[2] = 1.0f;
+ samplerDesc.BorderColor[3] = 1.0f;
+ break;
+ default:
+ return true;
+ }
+ samplerDesc.MinLOD = _desc.MinMipLevel;
+ samplerDesc.MaxLOD = _desc.MaxMipLevel;
+ HRESULT result = _device->GetDevice()->CreateSamplerState(&samplerDesc, &SamplerState);
+ LOG_DIRECTX_RESULT_WITH_RETURN(result);
+ ASSERT(SamplerState != nullptr);
+ _memoryUsage = sizeof(D3D11_SAMPLER_DESC);
+
+ return false;
+}
+
+void GPUSamplerDX11::OnReleaseGPU()
+{
+ if (SamplerState)
+ {
+ SamplerState->Release();
+ SamplerState = nullptr;
+ }
+
+ // Base
+ GPUSampler::OnReleaseGPU();
+}
+
+#endif
diff --git a/Source/Engine/GraphicsDevice/DirectX/DX11/GPUSamplerDX11.h b/Source/Engine/GraphicsDevice/DirectX/DX11/GPUSamplerDX11.h
new file mode 100644
index 000000000..b21ab9a61
--- /dev/null
+++ b/Source/Engine/GraphicsDevice/DirectX/DX11/GPUSamplerDX11.h
@@ -0,0 +1,31 @@
+// Copyright (c) 2012-2021 Wojciech Figat. All rights reserved.
+
+#pragma once
+
+#include "Engine/Graphics/Textures/GPUSampler.h"
+#include "GPUDeviceDX11.h"
+
+#if GRAPHICS_API_DIRECTX11
+
+///
+/// Sampler object for DirectX 11 backend.
+///
+class GPUSamplerDX11 : public GPUResourceBase
+{
+public:
+
+ GPUSamplerDX11(GPUDeviceDX11* device)
+ : GPUResourceBase(device, StringView::Empty)
+ {
+ }
+
+ ID3D11SamplerState* SamplerState = nullptr;
+
+protected:
+
+ // [GPUSampler]
+ bool OnInit() override;
+ void OnReleaseGPU() override;
+};
+
+#endif
diff --git a/Source/Engine/GraphicsDevice/DirectX/DX12/GPUContextDX12.cpp b/Source/Engine/GraphicsDevice/DirectX/DX12/GPUContextDX12.cpp
index ffa0a5550..60f90dca0 100644
--- a/Source/Engine/GraphicsDevice/DirectX/DX12/GPUContextDX12.cpp
+++ b/Source/Engine/GraphicsDevice/DirectX/DX12/GPUContextDX12.cpp
@@ -11,6 +11,7 @@
#include "UploadBufferDX12.h"
#include "GPUTextureDX12.h"
#include "GPUBufferDX12.h"
+#include "GPUSamplerDX12.h"
#include "CommandQueueDX12.h"
#include "DescriptorHeapDX12.h"
#include "Engine/Graphics/RenderTask.h"
@@ -74,6 +75,7 @@ GPUContextDX12::GPUContextDX12(GPUDeviceDX12* device, D3D12_COMMAND_LIST_TYPE ty
, _rtDirtyFlag(0)
, _psDirtyFlag(0)
, _cbDirtyFlag(0)
+ , _samplersDirtyFlag(0)
, _rtDepth(nullptr)
, _ibHandle(nullptr)
{
@@ -213,6 +215,7 @@ void GPUContextDX12::Reset()
_currentState = nullptr;
_rtDirtyFlag = false;
_cbDirtyFlag = false;
+ _samplersDirtyFlag = false;
_rtCount = 0;
_rtDepth = nullptr;
_srMaskDirtyGraphics = 0;
@@ -226,8 +229,9 @@ void GPUContextDX12::Reset()
Platform::MemoryClear(_srHandles, sizeof(_srHandles));
Platform::MemoryClear(_uaHandles, sizeof(_uaHandles));
Platform::MemoryClear(_vbHandles, sizeof(_vbHandles));
- Platform::MemoryClear(&_ibHandle, sizeof(_ibHandle));
+ _ibHandle = nullptr;
Platform::MemoryClear(&_cbHandles, sizeof(_cbHandles));
+ Platform::MemoryClear(&_samplers, sizeof(_samplers));
_swapChainsUsed = 0;
// Bind Root Signature
@@ -235,7 +239,7 @@ void GPUContextDX12::Reset()
_commandList->SetComputeRootSignature(_device->GetRootSignature());
// Bind heaps
- ID3D12DescriptorHeap* ppHeaps[] = { _device->RingHeap_CBV_SRV_UAV.GetHeap() };
+ ID3D12DescriptorHeap* ppHeaps[] = { _device->RingHeap_CBV_SRV_UAV.GetHeap(), _device->RingHeap_Sampler.GetHeap() };
_commandList->SetDescriptorHeaps(ARRAY_COUNT(ppHeaps), ppHeaps);
}
@@ -449,28 +453,58 @@ void GPUContextDX12::flushUAVs()
void GPUContextDX12::flushCBs()
{
- // Check if need to flush constant buffers
- if (_cbDirtyFlag)
+ if (!_cbDirtyFlag)
+ return;
+ _cbDirtyFlag = false;
+ for (uint32 slotIndex = 0; slotIndex < ARRAY_COUNT(_cbHandles); slotIndex++)
{
- // Clear flag
- _cbDirtyFlag = false;
-
- // Flush with the driver
- for (uint32 slotIndex = 0; slotIndex < ARRAY_COUNT(_cbHandles); slotIndex++)
+ auto cb = _cbHandles[slotIndex];
+ if (cb)
{
- auto cb = _cbHandles[slotIndex];
- if (cb)
- {
- ASSERT(cb->GPUAddress != 0);
- if (_isCompute)
- _commandList->SetComputeRootConstantBufferView(slotIndex, cb->GPUAddress);
- else
- _commandList->SetGraphicsRootConstantBufferView(slotIndex, cb->GPUAddress);
- }
+ ASSERT(cb->GPUAddress != 0);
+ if (_isCompute)
+ _commandList->SetComputeRootConstantBufferView(slotIndex, cb->GPUAddress);
+ else
+ _commandList->SetGraphicsRootConstantBufferView(slotIndex, cb->GPUAddress);
}
}
}
+void GPUContextDX12::flushSamplers()
+{
+ if (!_samplersDirtyFlag)
+ return;
+ _samplersDirtyFlag = false;
+ int32 lastSlot = -1;
+ for (int32 slotIndex = ARRAY_COUNT(_samplers) - 1; slotIndex >= 0; slotIndex--)
+ {
+ auto sampler = _samplers[slotIndex];
+ if (sampler)
+ {
+ lastSlot = slotIndex;
+ break;
+ }
+ }
+ if (lastSlot < 0)
+ return;
+ const uint32 samplersCount = lastSlot + 1;
+ D3D12_CPU_DESCRIPTOR_HANDLE srcDescriptorRangeStarts[ARRAY_COUNT(_samplers)];
+ for (uint32 i = 0; i < samplersCount; i++)
+ {
+ const auto handle = _samplers[i];
+ if (handle != nullptr)
+ {
+ srcDescriptorRangeStarts[i] = handle->HandleCPU;
+ }
+ }
+ auto allocation = _device->RingHeap_Sampler.AllocateTable(samplersCount);
+ _device->GetDevice()->CopyDescriptors(1, &allocation.CPU, &samplersCount, samplersCount, srcDescriptorRangeStarts, nullptr, D3D12_DESCRIPTOR_HEAP_TYPE_SAMPLER);
+ if (_isCompute)
+ _commandList->SetComputeRootDescriptorTable(4, allocation.GPU);
+ else
+ _commandList->SetGraphicsRootDescriptorTable(4, allocation.GPU);
+}
+
void GPUContextDX12::flushRBs()
{
#if DX12_ENABLE_RESOURCE_BARRIERS_BATCHING
@@ -563,6 +597,7 @@ void GPUContextDX12::OnDrawCall()
flushRBs();
flushPS();
flushCBs();
+ flushSamplers();
#if BUILD_DEBUG
// Additional verification of the state
@@ -835,8 +870,8 @@ void GPUContextDX12::BindUA(int32 slot, GPUResourceView* view)
{
ASSERT(slot >= 0 && slot < GPU_MAX_UA_BINDED);
_uaHandles[slot] = view ? (IShaderResourceDX12*)view->GetNativePtr() : nullptr;
- if (view)
- *view->LastRenderTime = _lastRenderTime;
+ if (view)
+ *view->LastRenderTime = _lastRenderTime;
}
void GPUContextDX12::BindVB(const Span& vertexBuffers, const uint32* vertexBuffersOffsets)
@@ -890,6 +925,17 @@ void GPUContextDX12::BindIB(GPUBuffer* indexBuffer)
}
}
+void GPUContextDX12::BindSampler(int32 slot, GPUSampler* sampler)
+{
+ ASSERT(slot >= GPU_STATIC_SAMPLERS_COUNT && slot < GPU_MAX_SAMPLER_BINDED);
+ const auto handle = sampler ? static_cast(sampler) : nullptr;
+ if (_samplers[slot - GPU_STATIC_SAMPLERS_COUNT] != handle)
+ {
+ _samplersDirtyFlag = true;
+ _samplers[slot - GPU_STATIC_SAMPLERS_COUNT] = handle;
+ }
+}
+
void GPUContextDX12::UpdateCB(GPUConstantBuffer* cb, const void* data)
{
ASSERT(data && cb);
@@ -927,6 +973,7 @@ void GPUContextDX12::Dispatch(GPUShaderProgramCS* shader, uint32 threadGroupCoun
flushUAVs();
flushRBs();
flushCBs();
+ flushSamplers();
auto shaderDX12 = (GPUShaderProgramCSDX12*)shader;
auto computeState = shaderDX12->GetOrCreateState();
@@ -959,6 +1006,7 @@ void GPUContextDX12::DispatchIndirect(GPUShaderProgramCS* shader, GPUBuffer* buf
flushUAVs();
flushRBs();
flushCBs();
+ flushSamplers();
auto shaderDX12 = (GPUShaderProgramCSDX12*)shader;
auto computeState = shaderDX12->GetOrCreateState();
diff --git a/Source/Engine/GraphicsDevice/DirectX/DX12/GPUContextDX12.h b/Source/Engine/GraphicsDevice/DirectX/DX12/GPUContextDX12.h
index b102cfa6b..1d5fa2f42 100644
--- a/Source/Engine/GraphicsDevice/DirectX/DX12/GPUContextDX12.h
+++ b/Source/Engine/GraphicsDevice/DirectX/DX12/GPUContextDX12.h
@@ -12,6 +12,7 @@
class GPUDeviceDX12;
class GPUPipelineStateDX12;
class GPUBufferDX12;
+class GPUSamplerDX12;
class GPUConstantBufferDX12;
class GPUTextureViewDX12;
@@ -51,6 +52,7 @@ private:
int32 _rtDirtyFlag : 1;
int32 _psDirtyFlag : 1;
int32 _cbDirtyFlag : 1;
+ int32 _samplersDirtyFlag : 1;
GPUTextureViewDX12* _rtDepth;
GPUTextureViewDX12* _rtHandles[GPU_MAX_RT_BINDED];
@@ -62,6 +64,7 @@ private:
D3D12_VERTEX_BUFFER_VIEW _vbViews[GPU_MAX_VB_BINDED];
D3D12_RESOURCE_BARRIER _rbBuffer[DX12_RB_BUFFER_SIZE];
GPUConstantBufferDX12* _cbHandles[GPU_MAX_CB_BINDED];
+ GPUSamplerDX12* _samplers[GPU_MAX_SAMPLER_BINDED - GPU_STATIC_SAMPLERS_COUNT];
public:
@@ -136,6 +139,7 @@ private:
void flushRTVs();
void flushUAVs();
void flushCBs();
+ void flushSamplers();
void flushRBs();
void flushPS();
void OnDrawCall();
@@ -167,6 +171,7 @@ public:
void BindUA(int32 slot, GPUResourceView* view) override;
void BindVB(const Span& vertexBuffers, const uint32* vertexBuffersOffsets = nullptr) override;
void BindIB(GPUBuffer* indexBuffer) override;
+ void BindSampler(int32 slot, GPUSampler* sampler) override;
void UpdateCB(GPUConstantBuffer* cb, const void* data) override;
void Dispatch(GPUShaderProgramCS* shader, uint32 threadGroupCountX, uint32 threadGroupCountY, uint32 threadGroupCountZ) override;
void DispatchIndirect(GPUShaderProgramCS* shader, GPUBuffer* bufferForArgs, uint32 offsetForArgs) override;
diff --git a/Source/Engine/GraphicsDevice/DirectX/DX12/GPUDeviceDX12.cpp b/Source/Engine/GraphicsDevice/DirectX/DX12/GPUDeviceDX12.cpp
index fa5e698d5..67f6dae5c 100644
--- a/Source/Engine/GraphicsDevice/DirectX/DX12/GPUDeviceDX12.cpp
+++ b/Source/Engine/GraphicsDevice/DirectX/DX12/GPUDeviceDX12.cpp
@@ -9,6 +9,7 @@
#include "GPUTextureDX12.h"
#include "GPUTimerQueryDX12.h"
#include "GPUBufferDX12.h"
+#include "GPUSamplerDX12.h"
#include "GPUSwapChainDX12.h"
#include "Engine/Engine/Engine.h"
#include "Engine/Engine/CommandLine.h"
@@ -191,7 +192,9 @@ GPUDeviceDX12::GPUDeviceDX12(IDXGIFactory4* dxgiFactory, GPUAdapterDX* adapter)
, Heap_CBV_SRV_UAV(this, D3D12_DESCRIPTOR_HEAP_TYPE_CBV_SRV_UAV, 4 * 1024, false)
, Heap_RTV(this, D3D12_DESCRIPTOR_HEAP_TYPE_RTV, 1 * 1024, false)
, Heap_DSV(this, D3D12_DESCRIPTOR_HEAP_TYPE_DSV, 64, false)
+ , Heap_Sampler(this, D3D12_DESCRIPTOR_HEAP_TYPE_SAMPLER, 128, false)
, RingHeap_CBV_SRV_UAV(this, D3D12_DESCRIPTOR_HEAP_TYPE_CBV_SRV_UAV, 512 * 1024, true)
+ , RingHeap_Sampler(this, D3D12_DESCRIPTOR_HEAP_TYPE_SAMPLER, 1 * 1024, true)
{
}
@@ -350,6 +353,7 @@ bool GPUDeviceDX12::Init()
limits.MaximumTexture2DArraySize = D3D12_REQ_TEXTURE2D_ARRAY_AXIS_DIMENSION;
limits.MaximumTexture3DSize = D3D12_REQ_TEXTURE3D_U_V_OR_W_DIMENSION;
limits.MaximumTextureCubeSize = D3D12_REQ_TEXTURECUBE_DIMENSION;
+ limits.MaximumSamplerAnisotropy = D3D12_DEFAULT_MAX_ANISOTROPY;
for (int32 i = 0; i < static_cast(PixelFormat::MAX); i++)
{
@@ -379,6 +383,8 @@ bool GPUDeviceDX12::Init()
_mainContext = New(this, D3D12_COMMAND_LIST_TYPE_DIRECT);
if (RingHeap_CBV_SRV_UAV.Init())
return true;
+ if (RingHeap_Sampler.Init())
+ return true;
// Create empty views
D3D12_SHADER_RESOURCE_VIEW_DESC srvDesc;
@@ -458,7 +464,7 @@ bool GPUDeviceDX12::Init()
// TODO: maybe create set of different root signatures? for UAVs, for compute, for simple drawing, for post fx?
{
// Descriptor tables
- D3D12_DESCRIPTOR_RANGE r[2];
+ D3D12_DESCRIPTOR_RANGE r[3];
// TODO: separate ranges for pixel/vertex visibility and one shared for all?
{
D3D12_DESCRIPTOR_RANGE& range = r[0];
@@ -476,9 +482,17 @@ bool GPUDeviceDX12::Init()
range.RegisterSpace = 0;
range.OffsetInDescriptorsFromTableStart = D3D12_DESCRIPTOR_RANGE_OFFSET_APPEND;
}
+ {
+ D3D12_DESCRIPTOR_RANGE& range = r[2];
+ range.RangeType = D3D12_DESCRIPTOR_RANGE_TYPE_SAMPLER;
+ range.NumDescriptors = GPU_MAX_SAMPLER_BINDED - GPU_STATIC_SAMPLERS_COUNT;
+ range.BaseShaderRegister = GPU_STATIC_SAMPLERS_COUNT;
+ range.RegisterSpace = 0;
+ range.OffsetInDescriptorsFromTableStart = D3D12_DESCRIPTOR_RANGE_OFFSET_APPEND;
+ }
// Root parameters
- D3D12_ROOT_PARAMETER rootParameters[4];
+ D3D12_ROOT_PARAMETER rootParameters[5];
{
D3D12_ROOT_PARAMETER& rootParam = rootParameters[0];
rootParam.ParameterType = D3D12_ROOT_PARAMETER_TYPE_CBV;
@@ -507,11 +521,18 @@ bool GPUDeviceDX12::Init()
rootParam.DescriptorTable.NumDescriptorRanges = 1;
rootParam.DescriptorTable.pDescriptorRanges = &r[1];
}
-
- // TODO: describe visibilities for the static samples, maybe use all pixel? or again pixel + all combo?
+ {
+ D3D12_ROOT_PARAMETER& rootParam = rootParameters[4];
+ rootParam.ParameterType = D3D12_ROOT_PARAMETER_TYPE_DESCRIPTOR_TABLE;
+ rootParam.ShaderVisibility = D3D12_SHADER_VISIBILITY_ALL;
+ rootParam.DescriptorTable.NumDescriptorRanges = 1;
+ rootParam.DescriptorTable.pDescriptorRanges = &r[2];
+ }
// Static samplers
D3D12_STATIC_SAMPLER_DESC staticSamplers[6];
+ static_assert(GPU_STATIC_SAMPLERS_COUNT == ARRAY_COUNT(staticSamplers), "Update static samplers setup.");
+ // TODO: describe visibilities for the static samples, maybe use all pixel? or again pixel + all combo?
// Linear Clamp
staticSamplers[0].Filter = D3D12_FILTER_MIN_MAG_MIP_LINEAR;
staticSamplers[0].AddressU = D3D12_TEXTURE_ADDRESS_MODE_CLAMP;
@@ -718,7 +739,9 @@ void GPUDeviceDX12::Dispose()
Heap_CBV_SRV_UAV.ReleaseGPU();
Heap_RTV.ReleaseGPU();
Heap_DSV.ReleaseGPU();
+ Heap_Sampler.ReleaseGPU();
RingHeap_CBV_SRV_UAV.ReleaseGPU();
+ RingHeap_Sampler.ReleaseGPU();
SAFE_DELETE(UploadBuffer);
SAFE_DELETE(DrawIndirectCommandSignature);
SAFE_DELETE(_mainContext);
@@ -766,6 +789,11 @@ GPUBuffer* GPUDeviceDX12::CreateBuffer(const StringView& name)
return New(this, name);
}
+GPUSampler* GPUDeviceDX12::CreateSampler()
+{
+ return New(this);
+}
+
GPUSwapChain* GPUDeviceDX12::CreateSwapChain(Window* window)
{
return New(this, window);
diff --git a/Source/Engine/GraphicsDevice/DirectX/DX12/GPUDeviceDX12.h b/Source/Engine/GraphicsDevice/DirectX/DX12/GPUDeviceDX12.h
index f14305ea7..bf0641e21 100644
--- a/Source/Engine/GraphicsDevice/DirectX/DX12/GPUDeviceDX12.h
+++ b/Source/Engine/GraphicsDevice/DirectX/DX12/GPUDeviceDX12.h
@@ -141,7 +141,9 @@ public:
DescriptorHeapPoolDX12 Heap_CBV_SRV_UAV;
DescriptorHeapPoolDX12 Heap_RTV;
DescriptorHeapPoolDX12 Heap_DSV;
+ DescriptorHeapPoolDX12 Heap_Sampler;
DescriptorHeapRingBufferDX12 RingHeap_CBV_SRV_UAV;
+ DescriptorHeapRingBufferDX12 RingHeap_Sampler;
public:
@@ -188,6 +190,7 @@ public:
GPUPipelineState* CreatePipelineState() override;
GPUTimerQuery* CreateTimerQuery() override;
GPUBuffer* CreateBuffer(const StringView& name) override;
+ GPUSampler* CreateSampler() override;
GPUSwapChain* CreateSwapChain(Window* window) override;
};
diff --git a/Source/Engine/GraphicsDevice/DirectX/DX12/GPUSamplerDX12.cpp b/Source/Engine/GraphicsDevice/DirectX/DX12/GPUSamplerDX12.cpp
new file mode 100644
index 000000000..eb4e701d8
--- /dev/null
+++ b/Source/Engine/GraphicsDevice/DirectX/DX12/GPUSamplerDX12.cpp
@@ -0,0 +1,124 @@
+// Copyright (c) 2012-2021 Wojciech Figat. All rights reserved.
+
+#if GRAPHICS_API_DIRECTX12
+
+#include "GPUSamplerDX12.h"
+
+D3D12_TEXTURE_ADDRESS_MODE ToDX12(GPUSamplerAddressMode value)
+{
+ switch (value)
+ {
+ case GPUSamplerAddressMode::Wrap:
+ return D3D12_TEXTURE_ADDRESS_MODE_WRAP;
+ case GPUSamplerAddressMode::Clamp:
+ return D3D12_TEXTURE_ADDRESS_MODE_CLAMP;
+ case GPUSamplerAddressMode::Mirror:
+ return D3D12_TEXTURE_ADDRESS_MODE_MIRROR;
+ case GPUSamplerAddressMode::Border:
+ return D3D12_TEXTURE_ADDRESS_MODE_BORDER;
+ default:
+ return (D3D12_TEXTURE_ADDRESS_MODE)-1;
+ }
+}
+
+bool GPUSamplerDX12::OnInit()
+{
+ D3D12_SAMPLER_DESC samplerDesc;
+ if (_desc.ComparisonFunction == GPUSamplerCompareFunction::Never)
+ {
+ switch (_desc.Filter)
+ {
+ case GPUSamplerFilter::Point:
+ samplerDesc.Filter = D3D12_FILTER_MIN_MAG_MIP_POINT;
+ break;
+ case GPUSamplerFilter::Bilinear:
+ samplerDesc.Filter = D3D12_FILTER_MIN_MAG_LINEAR_MIP_POINT;
+ break;
+ case GPUSamplerFilter::Trilinear:
+ samplerDesc.Filter = D3D12_FILTER_MIN_MAG_MIP_LINEAR;
+ break;
+ case GPUSamplerFilter::Anisotropic:
+ samplerDesc.Filter = D3D12_FILTER_ANISOTROPIC;
+ break;
+ default:
+ return true;
+ }
+ }
+ else
+ {
+ switch (_desc.Filter)
+ {
+ case GPUSamplerFilter::Point:
+ samplerDesc.Filter = D3D12_FILTER_COMPARISON_MIN_MAG_MIP_POINT;
+ break;
+ case GPUSamplerFilter::Bilinear:
+ samplerDesc.Filter = D3D12_FILTER_COMPARISON_MIN_LINEAR_MAG_MIP_POINT;
+ break;
+ case GPUSamplerFilter::Trilinear:
+ samplerDesc.Filter = D3D12_FILTER_COMPARISON_MIN_MAG_MIP_LINEAR;
+ break;
+ case GPUSamplerFilter::Anisotropic:
+ samplerDesc.Filter = D3D12_FILTER_COMPARISON_ANISOTROPIC;
+ break;
+ default:
+ return true;
+ }
+ }
+ samplerDesc.AddressU = ToDX12(_desc.AddressU);
+ samplerDesc.AddressV = ToDX12(_desc.AddressV);
+ samplerDesc.AddressW = ToDX12(_desc.AddressW);
+ samplerDesc.MipLODBias = _desc.MipBias;
+ samplerDesc.MaxAnisotropy = _desc.MaxAnisotropy;
+ switch (_desc.ComparisonFunction)
+ {
+ case GPUSamplerCompareFunction::Never:
+ samplerDesc.ComparisonFunc = D3D12_COMPARISON_FUNC_NEVER;
+ break;
+ case GPUSamplerCompareFunction::Less:
+ samplerDesc.ComparisonFunc = D3D12_COMPARISON_FUNC_LESS;
+ break;
+ default:
+ return true;
+ }
+ switch (_desc.BorderColor)
+ {
+ case GPUSamplerBorderColor::TransparentBlack:
+ samplerDesc.BorderColor[0] = 0;
+ samplerDesc.BorderColor[1] = 0;
+ samplerDesc.BorderColor[2] = 0;
+ samplerDesc.BorderColor[3] = 0;
+ break;
+ case GPUSamplerBorderColor::OpaqueBlack:
+ samplerDesc.BorderColor[0] = 0;
+ samplerDesc.BorderColor[1] = 0;
+ samplerDesc.BorderColor[2] = 0;
+ samplerDesc.BorderColor[3] = 1.0f;
+ break;
+ case GPUSamplerBorderColor::OpaqueWhite:
+ samplerDesc.BorderColor[0] = 1.0f;
+ samplerDesc.BorderColor[1] = 1.0f;
+ samplerDesc.BorderColor[2] = 1.0f;
+ samplerDesc.BorderColor[3] = 1.0f;
+ break;
+ default:
+ return true;
+ }
+ samplerDesc.MinLOD = _desc.MinMipLevel;
+ samplerDesc.MaxLOD = _desc.MaxMipLevel;
+ _device->Heap_Sampler.AllocateSlot(Slot.Heap, Slot.Index);
+ HandleCPU = Slot.CPU();
+ _device->GetDevice()->CreateSampler(&samplerDesc, HandleCPU);
+ _memoryUsage = sizeof(D3D12_SAMPLER_DESC);
+
+ return false;
+}
+
+void GPUSamplerDX12::OnReleaseGPU()
+{
+ Slot.Release();
+
+ // Base
+ GPUSampler::OnReleaseGPU();
+}
+
+#endif
diff --git a/Source/Engine/GraphicsDevice/DirectX/DX12/GPUSamplerDX12.h b/Source/Engine/GraphicsDevice/DirectX/DX12/GPUSamplerDX12.h
new file mode 100644
index 000000000..5b565669a
--- /dev/null
+++ b/Source/Engine/GraphicsDevice/DirectX/DX12/GPUSamplerDX12.h
@@ -0,0 +1,32 @@
+// Copyright (c) 2012-2021 Wojciech Figat. All rights reserved.
+
+#pragma once
+
+#include "Engine/Graphics/Textures/GPUSampler.h"
+#include "GPUDeviceDX12.h"
+
+#if GRAPHICS_API_DIRECTX12
+
+///
+/// Sampler object for DirectX 12 backend.
+///
+class GPUSamplerDX12 : public GPUResourceDX12
+{
+public:
+
+ GPUSamplerDX12(GPUDeviceDX12* device)
+ : GPUResourceDX12(device, StringView::Empty)
+ {
+ }
+
+ DescriptorHeapWithSlotsDX12::Slot Slot;
+ D3D12_CPU_DESCRIPTOR_HANDLE HandleCPU;
+
+protected:
+
+ // [GPUSampler]
+ bool OnInit() override;
+ void OnReleaseGPU() override;
+};
+
+#endif
diff --git a/Source/Engine/GraphicsDevice/Null/GPUContextNull.h b/Source/Engine/GraphicsDevice/Null/GPUContextNull.h
index f45417976..96f2e9485 100644
--- a/Source/Engine/GraphicsDevice/Null/GPUContextNull.h
+++ b/Source/Engine/GraphicsDevice/Null/GPUContextNull.h
@@ -108,6 +108,10 @@ public:
{
}
+ void BindSampler(int32 slot, GPUSampler* sampler) override
+ {
+ }
+
void UpdateCB(GPUConstantBuffer* cb, const void* data) override
{
}
diff --git a/Source/Engine/GraphicsDevice/Null/GPUDeviceNull.cpp b/Source/Engine/GraphicsDevice/Null/GPUDeviceNull.cpp
index fa266a34a..4a5cd23fc 100644
--- a/Source/Engine/GraphicsDevice/Null/GPUDeviceNull.cpp
+++ b/Source/Engine/GraphicsDevice/Null/GPUDeviceNull.cpp
@@ -10,6 +10,7 @@
#include "GPUPipelineStateNull.h"
#include "GPUTimerQueryNull.h"
#include "GPUBufferNull.h"
+#include "GPUSamplerNull.h"
#include "GPUSwapChainNull.h"
GPUDeviceNull::GPUDeviceNull()
@@ -66,6 +67,7 @@ bool GPUDeviceNull::Init()
limits.MaximumTexture2DArraySize = 512;
limits.MaximumTexture3DSize = 2048;
limits.MaximumTextureCubeSize = 16384;
+ limits.MaximumSamplerAnisotropy = 0;
for (int32 i = 0; i < static_cast(PixelFormat::MAX); i++)
{
@@ -177,6 +179,11 @@ GPUBuffer* GPUDeviceNull::CreateBuffer(const StringView& name)
return New();
}
+GPUSampler* GPUDeviceNull::CreateSampler()
+{
+ return New();
+}
+
GPUSwapChain* GPUDeviceNull::CreateSwapChain(Window* window)
{
return New(window);
diff --git a/Source/Engine/GraphicsDevice/Null/GPUDeviceNull.h b/Source/Engine/GraphicsDevice/Null/GPUDeviceNull.h
index 2562a2744..7c3894fa4 100644
--- a/Source/Engine/GraphicsDevice/Null/GPUDeviceNull.h
+++ b/Source/Engine/GraphicsDevice/Null/GPUDeviceNull.h
@@ -46,6 +46,7 @@ public:
GPUPipelineState* CreatePipelineState() override;
GPUTimerQuery* CreateTimerQuery() override;
GPUBuffer* CreateBuffer(const StringView& name) override;
+ GPUSampler* CreateSampler() override;
GPUSwapChain* CreateSwapChain(Window* window) override;
};
diff --git a/Source/Engine/GraphicsDevice/Null/GPUSamplerNull.h b/Source/Engine/GraphicsDevice/Null/GPUSamplerNull.h
new file mode 100644
index 000000000..c54852a90
--- /dev/null
+++ b/Source/Engine/GraphicsDevice/Null/GPUSamplerNull.h
@@ -0,0 +1,23 @@
+// Copyright (c) 2012-2021 Wojciech Figat. All rights reserved.
+
+#pragma once
+
+#if GRAPHICS_API_NULL
+
+#include "Engine/Graphics/Textures/GPUSampler.h"
+
+///
+/// Sampler object for Null backend.
+///
+class GPUSamplerNull : public GPUSampler
+{
+protected:
+
+ // [GPUSampler]
+ bool OnInit() override
+ {
+ return false;
+ }
+};
+
+#endif
diff --git a/Source/Engine/GraphicsDevice/Vulkan/GPUContextVulkan.cpp b/Source/Engine/GraphicsDevice/Vulkan/GPUContextVulkan.cpp
index 6e73ec157..8ee07b70e 100644
--- a/Source/Engine/GraphicsDevice/Vulkan/GPUContextVulkan.cpp
+++ b/Source/Engine/GraphicsDevice/Vulkan/GPUContextVulkan.cpp
@@ -8,6 +8,7 @@
#include "Engine/Core/Math/Color.h"
#include "GPUBufferVulkan.h"
#include "GPUShaderVulkan.h"
+#include "GPUSamplerVulkan.h"
#include "GPUPipelineStateVulkan.h"
#include "Engine/Profiler/RenderStats.h"
#include "GPUShaderProgramVulkan.h"
@@ -447,7 +448,8 @@ void GPUContextVulkan::UpdateDescriptorSets(const SpirvShaderDescriptorInfo& des
case VK_DESCRIPTOR_TYPE_SAMPLER:
{
// Sampler
- const VkSampler sampler = _device->HelperResources.GetStaticSampler((HelperResourcesVulkan::StaticSamplers)descriptor.Slot);
+ const VkSampler sampler = _samplerHandles[descriptor.Slot];
+ ASSERT(sampler);
needsWrite |= dsWriter.WriteSampler(descriptorIndex, sampler);
break;
}
@@ -715,8 +717,6 @@ void GPUContextVulkan::FrameBegin()
_psDirtyFlag = 0;
_rtDirtyFlag = 0;
_cbDirtyFlag = 0;
- _srDirtyFlag = 0;
- _uaDirtyFlag = 0;
_rtCount = 0;
_vbCount = 0;
_renderPass = nullptr;
@@ -726,6 +726,8 @@ void GPUContextVulkan::FrameBegin()
Platform::MemoryClear(_cbHandles, sizeof(_cbHandles));
Platform::MemoryClear(_srHandles, sizeof(_srHandles));
Platform::MemoryClear(_uaHandles, sizeof(_uaHandles));
+ Platform::MemoryCopy(_samplerHandles, _device->HelperResources.GetStaticSamplers(), sizeof(VkSampler) * GPU_STATIC_SAMPLERS_COUNT);
+ Platform::MemoryClear(_samplerHandles + GPU_STATIC_SAMPLERS_COUNT, sizeof(_samplerHandles) - sizeof(VkSampler) * GPU_STATIC_SAMPLERS_COUNT);
#if VULKAN_RESET_QUERY_POOLS
// Reset pending queries
@@ -916,13 +918,11 @@ void GPUContextVulkan::SetRenderTarget(GPUTextureView* rt, GPUBuffer* uaOutput)
void GPUContextVulkan::ResetSR()
{
- _srDirtyFlag = false;
Platform::MemoryClear(_srHandles, sizeof(_srHandles));
}
void GPUContextVulkan::ResetUA()
{
- _uaDirtyFlag = false;
Platform::MemoryClear(_uaHandles, sizeof(_uaHandles));
}
@@ -951,7 +951,6 @@ void GPUContextVulkan::BindSR(int32 slot, GPUResourceView* view)
const auto handle = view ? (DescriptorOwnerResourceVulkan*)view->GetNativePtr() : nullptr;
if (_srHandles[slot] != handle)
{
- _srDirtyFlag = true;
_srHandles[slot] = handle;
if (view)
*view->LastRenderTime = _lastRenderTime;
@@ -964,7 +963,6 @@ void GPUContextVulkan::BindUA(int32 slot, GPUResourceView* view)
const auto handle = view ? (DescriptorOwnerResourceVulkan*)view->GetNativePtr() : nullptr;
if (_uaHandles[slot] != handle)
{
- _uaDirtyFlag = true;
_uaHandles[slot] = handle;
if (view)
*view->LastRenderTime = _lastRenderTime;
@@ -997,6 +995,16 @@ void GPUContextVulkan::BindIB(GPUBuffer* indexBuffer)
vkCmdBindIndexBuffer(cmdBuffer->GetHandle(), ibVulkan, 0, indexBuffer->GetFormat() == PixelFormat::R32_UInt ? VK_INDEX_TYPE_UINT32 : VK_INDEX_TYPE_UINT16);
}
+void GPUContextVulkan::BindSampler(int32 slot, GPUSampler* sampler)
+{
+ ASSERT(slot >= 0 && slot < GPU_MAX_SR_BINDED);
+ const auto handle = sampler ? ((GPUSamplerVulkan*)sampler)->Sampler : nullptr;
+ if (_samplerHandles[slot] != handle)
+ {
+ _samplerHandles[slot] = handle;
+ }
+}
+
void GPUContextVulkan::UpdateCB(GPUConstantBuffer* cb, const void* data)
{
ASSERT(data && cb);
diff --git a/Source/Engine/GraphicsDevice/Vulkan/GPUContextVulkan.h b/Source/Engine/GraphicsDevice/Vulkan/GPUContextVulkan.h
index da2054ab3..f4ba1deeb 100644
--- a/Source/Engine/GraphicsDevice/Vulkan/GPUContextVulkan.h
+++ b/Source/Engine/GraphicsDevice/Vulkan/GPUContextVulkan.h
@@ -94,8 +94,6 @@ private:
int32 _psDirtyFlag : 1;
int32 _rtDirtyFlag : 1;
int32 _cbDirtyFlag : 1;
- int32 _srDirtyFlag : 1;
- int32 _uaDirtyFlag : 1;
int32 _rtCount;
int32 _vbCount;
@@ -107,6 +105,7 @@ private:
DescriptorOwnerResourceVulkan* _cbHandles[GPU_MAX_CB_BINDED];
DescriptorOwnerResourceVulkan* _srHandles[GPU_MAX_SR_BINDED];
DescriptorOwnerResourceVulkan* _uaHandles[GPU_MAX_UA_BINDED];
+ VkSampler _samplerHandles[GPU_MAX_SAMPLER_BINDED];
DescriptorOwnerResourceVulkan** _handles[(int32)SpirvShaderResourceBindingType::MAX];
typedef Array DescriptorPoolArray;
@@ -188,6 +187,7 @@ public:
void BindUA(int32 slot, GPUResourceView* view) override;
void BindVB(const Span& vertexBuffers, const uint32* vertexBuffersOffsets = nullptr) override;
void BindIB(GPUBuffer* indexBuffer) override;
+ void BindSampler(int32 slot, GPUSampler* sampler) override;
void UpdateCB(GPUConstantBuffer* cb, const void* data) override;
void Dispatch(GPUShaderProgramCS* shader, uint32 threadGroupCountX, uint32 threadGroupCountY, uint32 threadGroupCountZ) override;
void DispatchIndirect(GPUShaderProgramCS* shader, GPUBuffer* bufferForArgs, uint32 offsetForArgs) override;
diff --git a/Source/Engine/GraphicsDevice/Vulkan/GPUDeviceVulkan.cpp b/Source/Engine/GraphicsDevice/Vulkan/GPUDeviceVulkan.cpp
index 2ad04d2cb..c2582ad95 100644
--- a/Source/Engine/GraphicsDevice/Vulkan/GPUDeviceVulkan.cpp
+++ b/Source/Engine/GraphicsDevice/Vulkan/GPUDeviceVulkan.cpp
@@ -13,6 +13,7 @@
#include "GPUTextureVulkan.h"
#include "GPUTimerQueryVulkan.h"
#include "GPUBufferVulkan.h"
+#include "GPUSamplerVulkan.h"
#include "GPUSwapChainVulkan.h"
#include "RenderToolsVulkan.h"
#include "QueueVulkan.h"
@@ -369,7 +370,7 @@ void DeferredDeletionQueueVulkan::ReleaseResources(bool deleteImmediately)
#undef SWITCH_CASE
default:
#if !BUILD_RELEASE
- CRASH;
+ CRASH;
#endif
break;
}
@@ -730,8 +731,9 @@ void InitSampler(VkSamplerCreateInfo& createInfo, bool supportsMirrorClampToEdge
createInfo.compareOp = RenderToolsVulkan::ToVulkanSamplerCompareFunction(compareFunction);
}
-VkSampler HelperResourcesVulkan::GetStaticSampler(StaticSamplers type)
+VkSampler* HelperResourcesVulkan::GetStaticSamplers()
{
+ static_assert(GPU_STATIC_SAMPLERS_COUNT == 6, "Update static samplers setup.");
if (!_staticSamplers[0])
{
const bool supportsMirrorClampToEdge = GPUDeviceVulkan::OptionalDeviceExtensions.HasMirrorClampToEdge;
@@ -769,8 +771,7 @@ VkSampler HelperResourcesVulkan::GetStaticSampler(StaticSamplers type)
InitSampler(createInfo, supportsMirrorClampToEdge, GPUSamplerFilter::Trilinear, GPUSamplerAddressMode::Clamp, GPUSamplerAddressMode::Clamp, GPUSamplerAddressMode::Clamp, GPUSamplerCompareFunction::Less);
VALIDATE_VULKAN_RESULT(vkCreateSampler(_device->Device, &createInfo, nullptr, &_staticSamplers[5]));
}
-
- return _staticSamplers[static_cast(type)];
+ return _staticSamplers;
}
GPUTextureVulkan* HelperResourcesVulkan::GetDummyTexture(SpirvShaderResourceType type)
@@ -797,7 +798,7 @@ GPUTextureVulkan* HelperResourcesVulkan::GetDummyTexture(SpirvShaderResourceType
index = 5;
break;
default:
- CRASH;
+ CRASH;
return nullptr;
}
@@ -1172,7 +1173,7 @@ GPUDevice* GPUDeviceVulkan::Create()
volkLoadInstance(Instance);
#endif
-// Setup debug layer
+ // Setup debug layer
#if VULKAN_USE_DEBUG_LAYER
SetupDebugLayerCallback();
#endif
@@ -1692,6 +1693,7 @@ bool GPUDeviceVulkan::Init()
limits.MaximumTexture2DArraySize = PhysicalDeviceLimits.maxImageArrayLayers;
limits.MaximumTexture3DSize = PhysicalDeviceLimits.maxImageDimension3D;
limits.MaximumTextureCubeSize = PhysicalDeviceLimits.maxImageDimensionCube;
+ limits.MaximumSamplerAnisotropy = PhysicalDeviceLimits.maxSamplerAnisotropy;
for (int32 i = 0; i < static_cast(PixelFormat::MAX); i++)
{
@@ -1957,6 +1959,11 @@ GPUBuffer* GPUDeviceVulkan::CreateBuffer(const StringView& name)
return New(this, name);
}
+GPUSampler* GPUDeviceVulkan::CreateSampler()
+{
+ return New(this);
+}
+
GPUSwapChain* GPUDeviceVulkan::CreateSwapChain(Window* window)
{
return New(this, window);
diff --git a/Source/Engine/GraphicsDevice/Vulkan/GPUDeviceVulkan.h b/Source/Engine/GraphicsDevice/Vulkan/GPUDeviceVulkan.h
index 882c803b5..4b3bf7093 100644
--- a/Source/Engine/GraphicsDevice/Vulkan/GPUDeviceVulkan.h
+++ b/Source/Engine/GraphicsDevice/Vulkan/GPUDeviceVulkan.h
@@ -385,46 +385,24 @@ public:
///
class HelperResourcesVulkan
{
-public:
-
- enum class StaticSamplers
- {
- SamplerLinearClamp =0,
- SamplerPointClamp = 1,
- SamplerLinearWrap = 2,
- SamplerPointWrap = 3,
- ShadowSampler = 4,
- ShadowSamplerPCF = 5,
-
- MAX
- };
-
private:
GPUDeviceVulkan* _device;
GPUTextureVulkan* _dummyTextures[6];
GPUBufferVulkan* _dummyBuffer;
GPUBufferVulkan* _dummyVB;
- VkSampler _staticSamplers[static_cast(StaticSamplers::MAX)];
+ VkSampler _staticSamplers[GPU_STATIC_SAMPLERS_COUNT];
public:
- ///
- /// Initializes a new instance of the class.
- ///
- /// The graphics device.
HelperResourcesVulkan(GPUDeviceVulkan* device);
public:
- VkSampler GetStaticSampler(StaticSamplers type);
-
+ VkSampler* GetStaticSamplers();
GPUTextureVulkan* GetDummyTexture(SpirvShaderResourceType type);
-
GPUBufferVulkan* GetDummyBuffer();
-
GPUBufferVulkan* GetDummyVertexBuffer();
-
void Dispose();
};
@@ -739,6 +717,7 @@ public:
GPUPipelineState* CreatePipelineState() override;
GPUTimerQuery* CreateTimerQuery() override;
GPUBuffer* CreateBuffer(const StringView& name) override;
+ GPUSampler* CreateSampler() override;
GPUSwapChain* CreateSwapChain(Window* window) override;
};
diff --git a/Source/Engine/GraphicsDevice/Vulkan/GPUSamplerVulkan.cpp b/Source/Engine/GraphicsDevice/Vulkan/GPUSamplerVulkan.cpp
new file mode 100644
index 000000000..17571f1ed
--- /dev/null
+++ b/Source/Engine/GraphicsDevice/Vulkan/GPUSamplerVulkan.cpp
@@ -0,0 +1,56 @@
+// Copyright (c) 2012-2021 Wojciech Figat. All rights reserved.
+
+#if GRAPHICS_API_VULKAN
+
+#include "GPUSamplerVulkan.h"
+#include "RenderToolsVulkan.h"
+
+bool GPUSamplerVulkan::OnInit()
+{
+ VkSamplerCreateInfo createInfo;
+ RenderToolsVulkan::ZeroStruct(createInfo, VK_STRUCTURE_TYPE_SAMPLER_CREATE_INFO);
+ createInfo.magFilter = RenderToolsVulkan::ToVulkanMagFilterMode(_desc.Filter);
+ createInfo.minFilter = RenderToolsVulkan::ToVulkanMinFilterMode(_desc.Filter);
+ createInfo.mipmapMode = RenderToolsVulkan::ToVulkanMipFilterMode(_desc.Filter);
+ const bool supportsMirrorClampToEdge = GPUDeviceVulkan::OptionalDeviceExtensions.HasMirrorClampToEdge;
+ createInfo.addressModeU = RenderToolsVulkan::ToVulkanWrapMode(_desc.AddressU, supportsMirrorClampToEdge);
+ createInfo.addressModeV = RenderToolsVulkan::ToVulkanWrapMode(_desc.AddressV, supportsMirrorClampToEdge);
+ createInfo.addressModeW = RenderToolsVulkan::ToVulkanWrapMode(_desc.AddressW, supportsMirrorClampToEdge);
+ createInfo.mipLodBias = _desc.MipBias;
+ createInfo.anisotropyEnable = _desc.Filter == GPUSamplerFilter::Anisotropic ? VK_TRUE : VK_FALSE;
+ createInfo.maxAnisotropy = (float)_desc.MaxAnisotropy;
+ createInfo.compareEnable = _desc.ComparisonFunction != GPUSamplerCompareFunction::Never ? VK_TRUE : VK_FALSE;
+ createInfo.compareOp = RenderToolsVulkan::ToVulkanSamplerCompareFunction(_desc.ComparisonFunction);
+ createInfo.minLod = _desc.MinMipLevel;
+ createInfo.maxLod = _desc.MaxMipLevel;
+ switch (_desc.BorderColor)
+ {
+ case GPUSamplerBorderColor::TransparentBlack:
+ createInfo.borderColor = VK_BORDER_COLOR_FLOAT_TRANSPARENT_BLACK;
+ break;
+ case GPUSamplerBorderColor::OpaqueBlack:
+ createInfo.borderColor = VK_BORDER_COLOR_FLOAT_OPAQUE_BLACK;
+ break;
+ case GPUSamplerBorderColor::OpaqueWhite:
+ createInfo.borderColor = VK_BORDER_COLOR_FLOAT_OPAQUE_WHITE;
+ break;
+ default:
+ return true;
+ }
+ VALIDATE_VULKAN_RESULT(vkCreateSampler(_device->Device, &createInfo, nullptr, &Sampler));
+ return false;
+}
+
+void GPUSamplerVulkan::OnReleaseGPU()
+{
+ if (Sampler != VK_NULL_HANDLE)
+ {
+ _device->DeferredDeletionQueue.EnqueueResource(DeferredDeletionQueueVulkan::Buffer, Sampler);
+ Sampler = VK_NULL_HANDLE;
+ }
+
+ // Base
+ GPUSampler::OnReleaseGPU();
+}
+
+#endif
diff --git a/Source/Engine/GraphicsDevice/Vulkan/GPUSamplerVulkan.h b/Source/Engine/GraphicsDevice/Vulkan/GPUSamplerVulkan.h
new file mode 100644
index 000000000..6473cc62e
--- /dev/null
+++ b/Source/Engine/GraphicsDevice/Vulkan/GPUSamplerVulkan.h
@@ -0,0 +1,31 @@
+// Copyright (c) 2012-2021 Wojciech Figat. All rights reserved.
+
+#pragma once
+
+#if GRAPHICS_API_VULKAN
+
+#include "Engine/Graphics/Textures/GPUSampler.h"
+#include "GPUDeviceVulkan.h"
+
+///
+/// Sampler object for Vulkan backend.
+///
+class GPUSamplerVulkan : public GPUResourceVulkan
+{
+public:
+
+ GPUSamplerVulkan(GPUDeviceVulkan* device)
+ : GPUResourceVulkan(device, StringView::Empty)
+ {
+ }
+
+ VkSampler Sampler = VK_NULL_HANDLE;
+
+protected:
+
+ // [GPUSamplerVulkan]
+ bool OnInit() override;
+ void OnReleaseGPU() override;
+};
+
+#endif