1
0
mirror of https://github.com/doitsujin/dxvk.git synced 2024-12-11 10:24:10 +01:00

[dxvk] Implement sampler pool

Deduplicates redundant sampler objects and makes sampler creation
as well as lifetime tracking a bit more efficient.
This commit is contained in:
Philip Rebohle 2024-09-26 23:33:28 +02:00 committed by Philip Rebohle
parent 5f9f43e658
commit 4635397bb1
15 changed files with 546 additions and 228 deletions

View File

@ -9,63 +9,58 @@ namespace dxvk {
const D3D11_SAMPLER_DESC& desc)
: D3D11StateObject<ID3D11SamplerState>(device),
m_desc(desc), m_d3d10(this) {
DxvkSamplerCreateInfo info;
DxvkSamplerKey info = { };
// While D3D11_FILTER is technically an enum, its value bits
// can be used to decode the filter properties more efficiently.
const uint32_t filterBits = uint32_t(desc.Filter);
info.magFilter = (filterBits & 0x04) ? VK_FILTER_LINEAR : VK_FILTER_NEAREST;
info.minFilter = (filterBits & 0x10) ? VK_FILTER_LINEAR : VK_FILTER_NEAREST;
VkFilter minFilter = (filterBits & 0x10) ? VK_FILTER_LINEAR : VK_FILTER_NEAREST;
VkFilter magFilter = (filterBits & 0x04) ? VK_FILTER_LINEAR : VK_FILTER_NEAREST;
info.setFilter(minFilter, magFilter,
(filterBits & 0x01) ? VK_SAMPLER_MIPMAP_MODE_LINEAR : VK_SAMPLER_MIPMAP_MODE_NEAREST);
// Enforce LOD bias specified in the device options
float lodBias = desc.MipLODBias;
if (minFilter == VK_FILTER_LINEAR && magFilter == VK_FILTER_LINEAR) {
lodBias += device->GetOptions()->samplerLodBias;
if (device->GetOptions()->clampNegativeLodBias)
lodBias = std::max(lodBias, 0.0f);
}
info.setLodRange(desc.MinLOD, desc.MaxLOD, lodBias);
// Enforce anisotropy specified in the device options
uint32_t anisotropy = (filterBits & 0x40) ? desc.MaxAnisotropy : 0u;
int32_t samplerAnisotropyOption = device->GetOptions()->samplerAnisotropy;
if (samplerAnisotropyOption >= 0 && minFilter == VK_FILTER_LINEAR)
anisotropy = samplerAnisotropyOption > 0;
info.setAniso(anisotropy);
// Set up the remaining properties, which are
// stored directly in the sampler description
info.mipmapMode = (filterBits & 0x01) ? VK_SAMPLER_MIPMAP_MODE_LINEAR : VK_SAMPLER_MIPMAP_MODE_NEAREST;
info.mipmapLodBias = desc.MipLODBias;
info.mipmapLodMin = desc.MinLOD;
info.mipmapLodMax = desc.MaxLOD;
info.useAnisotropy = (filterBits & 0x40) ? VK_TRUE : VK_FALSE;
info.maxAnisotropy = float(desc.MaxAnisotropy);
info.addressModeU = DecodeAddressMode(desc.AddressU);
info.addressModeV = DecodeAddressMode(desc.AddressV);
info.addressModeW = DecodeAddressMode(desc.AddressW);
info.compareToDepth = (filterBits & 0x180) == 0x80 ? VK_TRUE : VK_FALSE;
info.compareOp = DecodeCompareOp(desc.ComparisonFunc);
info.setAddressModes(
DecodeAddressMode(desc.AddressU),
DecodeAddressMode(desc.AddressV),
DecodeAddressMode(desc.AddressW));
info.reductionMode = DecodeReductionMode(filterBits);
info.setDepthCompare((filterBits & 0x180) == 0x80,
DecodeCompareOp(desc.ComparisonFunc));
info.setReduction(DecodeReductionMode(filterBits));
for (uint32_t i = 0; i < 4; i++)
info.borderColor.float32[i] = desc.BorderColor[i];
info.usePixelCoord = VK_FALSE; // Not supported in D3D11
info.nonSeamless = VK_FALSE;
// Make sure to use a valid anisotropy value
if (desc.MaxAnisotropy < 1) info.maxAnisotropy = 1.0f;
if (desc.MaxAnisotropy > 16) info.maxAnisotropy = 16.0f;
// Enforce LOD bias specified in the device options
if (info.minFilter == VK_FILTER_LINEAR && info.magFilter == VK_FILTER_LINEAR) {
info.mipmapLodBias += device->GetOptions()->samplerLodBias;
if (device->GetOptions()->clampNegativeLodBias)
info.mipmapLodBias = std::max(info.mipmapLodBias, 0.0f);
}
// Enforce anisotropy specified in the device options
int32_t samplerAnisotropyOption = device->GetOptions()->samplerAnisotropy;
if (samplerAnisotropyOption >= 0 && info.minFilter == VK_FILTER_LINEAR) {
info.useAnisotropy = samplerAnisotropyOption > 0;
info.maxAnisotropy = float(samplerAnisotropyOption);
}
m_sampler = device->GetDXVKDevice()->createSampler(info);
}
D3D11SamplerState::~D3D11SamplerState() {
}

View File

@ -6690,42 +6690,37 @@ namespace dxvk {
auto mipFilter = DecodeMipFilter(cKey.MipFilter);
DxvkSamplerCreateInfo info;
info.addressModeU = DecodeAddressMode(cKey.AddressU);
info.addressModeV = DecodeAddressMode(cKey.AddressV);
info.addressModeW = DecodeAddressMode(cKey.AddressW);
info.compareToDepth = cKey.Depth;
info.compareOp = cKey.Depth ? VK_COMPARE_OP_LESS_OR_EQUAL : VK_COMPARE_OP_NEVER;
info.magFilter = DecodeFilter(cKey.MagFilter);
info.minFilter = DecodeFilter(cKey.MinFilter);
info.mipmapMode = mipFilter.MipFilter;
info.maxAnisotropy = float(cKey.MaxAnisotropy);
info.useAnisotropy = cKey.MaxAnisotropy > 1;
DxvkSamplerKey info = { };
info.setFilter(
DecodeFilter(cKey.MinFilter),
DecodeFilter(cKey.MagFilter),
mipFilter.MipFilter);
info.setAddressModes(
DecodeAddressMode(cKey.AddressU),
DecodeAddressMode(cKey.AddressV),
DecodeAddressMode(cKey.AddressW));
info.setDepthCompare(cKey.Depth,
VK_COMPARE_OP_LESS_OR_EQUAL);
info.setAniso(cKey.MaxAnisotropy);
float lodBias = cKey.MipmapLodBias + m_d3d9Options.samplerLodBias;
info.mipmapLodBias = cKey.MipmapLodBias + m_d3d9Options.samplerLodBias;
if (m_d3d9Options.clampNegativeLodBias)
info.mipmapLodBias = std::max(info.mipmapLodBias, 0.0f);
lodBias = std::max(lodBias, 0.0f);
info.mipmapLodMin = mipFilter.MipsEnabled ? float(cKey.MaxMipLevel) : 0;
info.mipmapLodMax = mipFilter.MipsEnabled ? FLT_MAX : 0;
info.reductionMode = VK_SAMPLER_REDUCTION_MODE_WEIGHTED_AVERAGE;
info.usePixelCoord = VK_FALSE;
info.nonSeamless = m_dxvkDevice->features().extNonSeamlessCubeMap.nonSeamlessCubeMap && !m_d3d9Options.seamlessCubes;
info.setLodRange(
mipFilter.MipsEnabled ? float(cKey.MaxMipLevel) : 0.0f,
mipFilter.MipsEnabled ? FLT_MAX : 0.0f,
lodBias);
info.setLegacyCubeFilter(!m_d3d9Options.seamlessCubes);
DecodeD3DCOLOR(cKey.BorderColor, info.borderColor.float32);
if (!m_dxvkDevice->features().extCustomBorderColor.customBorderColorWithoutFormat) {
// HACK: Let's get OPAQUE_WHITE border color over
// TRANSPARENT_BLACK if the border RGB is white.
if (info.borderColor.float32[0] == 1.0f
&& info.borderColor.float32[1] == 1.0f
&& info.borderColor.float32[2] == 1.0f
&& !m_dxvkDevice->features().extCustomBorderColor.customBorderColors) {
// Then set the alpha to 1.
info.borderColor.float32[3] = 1.0f;
}
}
try {
auto sampler = m_dxvkDevice->createSampler(info);

View File

@ -281,6 +281,10 @@ namespace dxvk {
m_resources.trackResource(DxvkLifetime<DxvkResource>(rc, Access));
}
void trackSampler(const Rc<DxvkSampler>& sampler) {
m_resources.trackSampler(sampler);
}
/**
* \brief Tracks a GPU event
*

View File

@ -5260,7 +5260,7 @@ namespace dxvk {
descriptorInfo.image.imageLayout = VK_IMAGE_LAYOUT_UNDEFINED;
if (m_rcTracked.set(binding.resourceBinding))
m_cmd->trackResource<DxvkAccess::None>(res.sampler);
m_cmd->trackSampler(res.sampler);
} else {
descriptorInfo.image.sampler = m_common->dummyResources().samplerHandle();
descriptorInfo.image.imageView = VK_NULL_HANDLE;
@ -5312,7 +5312,7 @@ namespace dxvk {
descriptorInfo.image.imageLayout = res.imageView->image()->info().layout;
if (m_rcTracked.set(binding.resourceBinding)) {
m_cmd->trackResource<DxvkAccess::None>(res.sampler);
m_cmd->trackSampler(res.sampler);
m_cmd->trackResource<DxvkAccess::Read>(res.imageView->image());
}
} else {

View File

@ -172,8 +172,8 @@ namespace dxvk {
Rc<DxvkSampler> DxvkDevice::createSampler(
const DxvkSamplerCreateInfo& createInfo) {
return new DxvkSampler(this, createInfo);
const DxvkSamplerKey& createInfo) {
return m_objects.samplerPool().createSampler(createInfo);
}

View File

@ -330,7 +330,7 @@ namespace dxvk {
* \returns Newly created sampler object
*/
Rc<DxvkSampler> createSampler(
const DxvkSamplerCreateInfo& createInfo);
const DxvkSamplerKey& createInfo);
/**
* \brief Creates local allocation cache

View File

@ -9,6 +9,7 @@ namespace dxvk {
void DxvkLifetimeTracker::reset() {
m_resources.clear();
m_allocations.clear();
m_samplers.clear();
}
}

View File

@ -3,6 +3,7 @@
#include <vector>
#include "dxvk_resource.h"
#include "dxvk_sampler.h"
namespace dxvk {
@ -106,6 +107,14 @@ namespace dxvk {
DxvkLifetimeTracker();
~DxvkLifetimeTracker();
/**
* \brief Adds a sampler to track
* \param [in] res The sampler to track
*/
void trackSampler(const Rc<DxvkSampler>& res) {
m_samplers.push_back(res);
}
/**
* \brief Adds a resource to track
* \param [in] res The resource to track
@ -132,6 +141,8 @@ namespace dxvk {
private:
std::vector<Rc<DxvkSampler>> m_samplers;
std::vector<DxvkLifetime<DxvkResource>> m_resources;
std::vector<DxvkLifetime<DxvkResourceAllocation>> m_allocations;

View File

@ -11,6 +11,7 @@
#include "dxvk_meta_resolve.h"
#include "dxvk_pipemanager.h"
#include "dxvk_renderpass.h"
#include "dxvk_sampler.h"
#include "dxvk_unbound.h"
#include "../util/util_lazy.h"
@ -25,6 +26,7 @@ namespace dxvk {
: m_device (device),
m_memoryManager (device),
m_pipelineManager (device),
m_samplerPool (device),
m_eventPool (device),
m_queryPool (device),
m_dummyResources (device) {
@ -39,6 +41,10 @@ namespace dxvk {
return m_pipelineManager;
}
DxvkSamplerPool& samplerPool() {
return m_samplerPool;
}
DxvkGpuEventPool& eventPool() {
return m_eventPool;
}
@ -78,6 +84,7 @@ namespace dxvk {
DxvkMemoryAllocator m_memoryManager;
DxvkPipelineManager m_pipelineManager;
DxvkSamplerPool m_samplerPool;
DxvkGpuEventPool m_eventPool;
DxvkGpuQueryPool m_queryPool;

View File

@ -4,82 +4,216 @@
namespace dxvk {
DxvkSampler::DxvkSampler(
DxvkDevice* device,
const DxvkSamplerCreateInfo& info)
: m_vkd(device->vkd()) {
DxvkSamplerPool* pool,
const DxvkSamplerKey& key)
: m_pool(pool), m_key(key) {
auto vk = m_pool->m_device->vkd();
VkSamplerCustomBorderColorCreateInfoEXT borderColorInfo = { VK_STRUCTURE_TYPE_SAMPLER_CUSTOM_BORDER_COLOR_CREATE_INFO_EXT };
borderColorInfo.customBorderColor = info.borderColor;
borderColorInfo.customBorderColor = key.borderColor;
VkSamplerReductionModeCreateInfo reductionInfo = { VK_STRUCTURE_TYPE_SAMPLER_REDUCTION_MODE_CREATE_INFO };
reductionInfo.reductionMode = info.reductionMode;
reductionInfo.reductionMode = VkSamplerReductionMode(key.u.p.reduction);
VkSamplerCreateInfo samplerInfo = { VK_STRUCTURE_TYPE_SAMPLER_CREATE_INFO };
samplerInfo.flags = info.nonSeamless ? VK_SAMPLER_CREATE_NON_SEAMLESS_CUBE_MAP_BIT_EXT : 0;
samplerInfo.magFilter = info.magFilter;
samplerInfo.minFilter = info.minFilter;
samplerInfo.mipmapMode = info.mipmapMode;
samplerInfo.addressModeU = info.addressModeU;
samplerInfo.addressModeV = info.addressModeV;
samplerInfo.addressModeW = info.addressModeW;
samplerInfo.mipLodBias = info.mipmapLodBias;
samplerInfo.anisotropyEnable = info.useAnisotropy;
samplerInfo.maxAnisotropy = info.maxAnisotropy;
samplerInfo.compareEnable = info.compareToDepth;
samplerInfo.compareOp = info.compareOp;
samplerInfo.minLod = info.mipmapLodMin;
samplerInfo.maxLod = info.mipmapLodMax;
samplerInfo.borderColor = VK_BORDER_COLOR_FLOAT_TRANSPARENT_BLACK;
samplerInfo.unnormalizedCoordinates = info.usePixelCoord;
samplerInfo.magFilter = VkFilter(key.u.p.magFilter);
samplerInfo.minFilter = VkFilter(key.u.p.minFilter);
samplerInfo.mipmapMode = VkSamplerMipmapMode(key.u.p.mipMode);
samplerInfo.addressModeU = VkSamplerAddressMode(key.u.p.addressU);
samplerInfo.addressModeV = VkSamplerAddressMode(key.u.p.addressV);
samplerInfo.addressModeW = VkSamplerAddressMode(key.u.p.addressW);
samplerInfo.mipLodBias = bit::decodeFixed<int32_t, 6, 8>(key.u.p.lodBias);
samplerInfo.anisotropyEnable = key.u.p.anisotropy > 0u;
samplerInfo.maxAnisotropy = float(key.u.p.anisotropy);
samplerInfo.compareEnable = key.u.p.compareEnable != 0u;
samplerInfo.compareOp = VkCompareOp(key.u.p.compareOp);
samplerInfo.minLod = bit::decodeFixed<uint32_t, 4, 8>(key.u.p.minLod);
samplerInfo.maxLod = bit::decodeFixed<uint32_t, 4, 8>(key.u.p.maxLod);
samplerInfo.borderColor = VK_BORDER_COLOR_FLOAT_TRANSPARENT_BLACK;
samplerInfo.unnormalizedCoordinates = key.u.p.pixelCoord;
if (!device->features().core.features.samplerAnisotropy)
if (key.u.p.legacyCube && m_pool->m_device->features().extNonSeamlessCubeMap.nonSeamlessCubeMap)
samplerInfo.flags |= VK_SAMPLER_CREATE_NON_SEAMLESS_CUBE_MAP_BIT_EXT;
if (!m_pool->m_device->features().core.features.samplerAnisotropy)
samplerInfo.anisotropyEnable = VK_FALSE;
if (samplerInfo.addressModeU == VK_SAMPLER_ADDRESS_MODE_CLAMP_TO_BORDER
|| samplerInfo.addressModeV == VK_SAMPLER_ADDRESS_MODE_CLAMP_TO_BORDER
|| samplerInfo.addressModeW == VK_SAMPLER_ADDRESS_MODE_CLAMP_TO_BORDER)
samplerInfo.borderColor = getBorderColor(device, info);
if (key.u.p.hasBorder)
samplerInfo.borderColor = determineBorderColorType();
if (samplerInfo.borderColor == VK_BORDER_COLOR_FLOAT_CUSTOM_EXT)
if (samplerInfo.borderColor == VK_BORDER_COLOR_FLOAT_CUSTOM_EXT
|| samplerInfo.borderColor == VK_BORDER_COLOR_INT_CUSTOM_EXT)
borderColorInfo.pNext = std::exchange(samplerInfo.pNext, &borderColorInfo);
if (reductionInfo.reductionMode != VK_SAMPLER_REDUCTION_MODE_WEIGHTED_AVERAGE)
reductionInfo.pNext = std::exchange(samplerInfo.pNext, &reductionInfo);
if (m_vkd->vkCreateSampler(m_vkd->device(),
if (vk->vkCreateSampler(vk->device(),
&samplerInfo, nullptr, &m_sampler) != VK_SUCCESS)
throw DxvkError("DxvkSampler::DxvkSampler: Failed to create sampler");
}
DxvkSampler::~DxvkSampler() {
m_vkd->vkDestroySampler(
m_vkd->device(), m_sampler, nullptr);
auto vk = m_pool->m_device->vkd();
vk->vkDestroySampler(vk->device(), m_sampler, nullptr);
}
VkBorderColor DxvkSampler::getBorderColor(const Rc<DxvkDevice>& device, const DxvkSamplerCreateInfo& info) {
static const std::array<std::pair<VkClearColorValue, VkBorderColor>, 3> s_borderColors = {{
void DxvkSampler::release() {
m_pool->releaseSampler(this);
}
VkBorderColor DxvkSampler::determineBorderColorType() const {
static const std::array<std::pair<VkClearColorValue, VkBorderColor>, 4> s_borderColors = {{
{ { { 0.0f, 0.0f, 0.0f, 0.0f } }, VK_BORDER_COLOR_FLOAT_TRANSPARENT_BLACK },
{ { { 0.0f, 0.0f, 0.0f, 1.0f } }, VK_BORDER_COLOR_FLOAT_OPAQUE_BLACK },
{ { { 1.0f, 1.0f, 1.0f, 1.0f } }, VK_BORDER_COLOR_FLOAT_OPAQUE_WHITE },
}};
// Ignore G/B/A components for shadow samplers
size_t size = !info.compareToDepth
? sizeof(VkClearColorValue)
: sizeof(float);
// Iterate over border colors and try to find an exact match
uint32_t componentCount = m_key.u.p.compareEnable ? 1u : 4u;
for (const auto& e : s_borderColors) {
if (!std::memcmp(&e.first, &info.borderColor, size))
bool allEqual = true;
for (uint32_t i = 0; i < componentCount; i++)
allEqual &= m_key.borderColor.float32[i] == e.first.float32[i];
if (allEqual)
return e.second;
}
if (!device->features().extCustomBorderColor.customBorderColorWithoutFormat) {
Logger::warn("DXVK: Custom border colors not supported");
return VK_BORDER_COLOR_FLOAT_TRANSPARENT_BLACK;
// If custom border colors are supported, use that
if (m_pool->m_device->features().extCustomBorderColor.customBorderColorWithoutFormat)
return VK_BORDER_COLOR_FLOAT_CUSTOM_EXT;
// Otherwise, use the sum of absolute differences to find the
// closest fallback value. Some D3D9 games may rely on this.
Logger::warn("DXVK: Custom border colors not supported");
VkBorderColor result = VK_BORDER_COLOR_FLOAT_TRANSPARENT_BLACK;
float minSad = -1.0f;
for (const auto& e : s_borderColors) {
float sad = 0.0f;
for (uint32_t i = 0; i < componentCount; i++)
sad += std::abs(m_key.borderColor.float32[i] - e.first.float32[i]);
if (sad < minSad || minSad < 0.0f) {
minSad = sad;
result = e.second;
}
}
return VK_BORDER_COLOR_FLOAT_CUSTOM_EXT;
return result;
}
DxvkSamplerPool::DxvkSamplerPool(DxvkDevice* device)
: m_device(device) {
}
DxvkSamplerPool::~DxvkSamplerPool() {
m_samplers.clear();
}
Rc<DxvkSampler> DxvkSamplerPool::createSampler(const DxvkSamplerKey& key) {
std::unique_lock lock(m_mutex);
auto entry = m_samplers.find(key);
if (entry != m_samplers.end()) {
DxvkSampler* sampler = &entry->second;
// Remove the sampler from the LRU list if it's in there. Due
// to the way releasing samplers is implemented upon reaching
// a ref count of 0, it is possible that we reach this before
// the releasing thread inserted the list into the LRU list.
if (!sampler->m_refCount.fetch_add(1u, std::memory_order_acquire)) {
if (sampler->m_lruPrev)
sampler->m_lruPrev->m_lruNext = sampler->m_lruNext;
else if (m_lruHead == sampler)
m_lruHead = sampler->m_lruNext;
if (sampler->m_lruNext)
sampler->m_lruNext->m_lruPrev = sampler->m_lruPrev;
else if (m_lruTail == sampler)
m_lruTail = sampler->m_lruPrev;
sampler->m_lruPrev = nullptr;
sampler->m_lruNext = nullptr;
}
// We already took a reference, forward the pointer as-is
return Rc<DxvkSampler>::unsafeCreate(sampler);
}
// If we're spamming sampler allocations, we might need
// to clean up unused ones here to stay within the limit
if (m_samplers.size() >= MaxSamplerCount)
destroyLeastRecentlyUsedSampler();
// Create new sampler object
return &m_samplers.emplace(std::piecewise_construct,
std::forward_as_tuple(key),
std::forward_as_tuple(this, key)).first->second;
}
void DxvkSamplerPool::releaseSampler(DxvkSampler* sampler) {
std::unique_lock lock(m_mutex);
// Back off if another thread has re-aquired the sampler. This is
// safe since the ref count can only be incremented from zero when
// the pool is locked.
if (sampler->m_refCount.load())
return;
// It is also possible that two threads end up here while the ref
// count is zero. Make sure to not add the sampler to the LRU list
// more than once in that case.
if (sampler->m_lruPrev || m_lruHead == sampler)
return;
// Add sampler to the end of the LRU list
sampler->m_lruPrev = m_lruTail;
sampler->m_lruNext = nullptr;
if (m_lruTail)
m_lruTail->m_lruNext = sampler;
else
m_lruHead = sampler;
m_lruTail = sampler;
// Try to keep some samplers available for subsequent allocations
if (m_samplers.size() > MinSamplerCount)
destroyLeastRecentlyUsedSampler();
}
void DxvkSamplerPool::destroyLeastRecentlyUsedSampler() {
DxvkSampler* sampler = m_lruHead;
if (sampler) {
m_lruHead = sampler->m_lruNext;
if (m_lruHead)
m_lruHead->m_lruPrev = nullptr;
else
m_lruTail = nullptr;
m_samplers.erase(sampler->key());
}
}
}

View File

@ -1,50 +1,131 @@
#pragma once
#include <unordered_map>
#include "../util/util_bit.h"
#include "../util/thread.h"
#include "dxvk_resource.h"
namespace dxvk {
class DxvkDevice;
class DxvkSamplerPool;
/**
* \brief Sampler properties
* \brief Sampler key
*
* Stores packed sampler properties and in a way that
* can be reasonably efficiently used with a hash map.
*/
struct DxvkSamplerCreateInfo {
/// Texture filter propertoes
VkFilter magFilter;
VkFilter minFilter;
/// Mipmapping properties
VkSamplerMipmapMode mipmapMode;
float mipmapLodBias;
float mipmapLodMin;
float mipmapLodMax;
/// Anisotropic filtering
VkBool32 useAnisotropy;
float maxAnisotropy;
/// Address modes
VkSamplerAddressMode addressModeU;
VkSamplerAddressMode addressModeV;
VkSamplerAddressMode addressModeW;
/// Compare op for shadow textures
VkBool32 compareToDepth;
VkCompareOp compareOp;
/// Reduction mode for min/max samplers
VkSamplerReductionMode reductionMode;
/// Texture border color
VkClearColorValue borderColor;
struct DxvkSamplerKey {
union {
struct {
uint32_t minFilter : 1;
uint32_t magFilter : 1;
uint32_t mipMode : 1;
uint32_t anisotropy : 5;
/// Enables unnormalized coordinates
VkBool32 usePixelCoord;
uint32_t addressU : 3;
uint32_t addressV : 3;
uint32_t addressW : 3;
uint32_t hasBorder : 1;
uint32_t lodBias : 14;
uint32_t minLod : 12;
uint32_t maxLod : 12;
uint32_t compareEnable : 1;
uint32_t compareOp : 3;
uint32_t reduction : 2;
uint32_t pixelCoord : 1;
uint32_t legacyCube : 1;
} p;
uint32_t properties[2] = { 0u, 0u };
} u;
VkClearColorValue borderColor = { };
void setFilter(VkFilter min, VkFilter mag, VkSamplerMipmapMode mip) {
u.p.minFilter = uint32_t(min);
u.p.magFilter = uint32_t(mag);
u.p.mipMode = uint32_t(mip);
}
void setAniso(uint32_t anisotropy) {
u.p.anisotropy = std::min(anisotropy, 16u);
}
void setDepthCompare(bool enable, VkCompareOp op) {
u.p.compareEnable = uint32_t(enable);
u.p.compareOp = enable ? uint32_t(op) : 0u;
}
void setReduction(VkSamplerReductionMode reduction) {
u.p.reduction = uint32_t(reduction);
}
void setUsePixelCoordinates(bool enable) {
u.p.pixelCoord = uint32_t(enable);
}
void setLegacyCubeFilter(bool enable) {
u.p.legacyCube = uint32_t(enable);
}
void setAddressModes(VkSamplerAddressMode u_, VkSamplerAddressMode v_, VkSamplerAddressMode w_) {
u.p.addressU = u_;
u.p.addressV = v_;
u.p.addressW = w_;
u.p.hasBorder = uint32_t(u_ == VK_SAMPLER_ADDRESS_MODE_CLAMP_TO_BORDER
|| v_ == VK_SAMPLER_ADDRESS_MODE_CLAMP_TO_BORDER
|| w_ == VK_SAMPLER_ADDRESS_MODE_CLAMP_TO_BORDER);
}
void setLodRange(float min, float max, float bias) {
u.p.minLod = bit::encodeFixed<uint32_t, 4, 8>(min);
u.p.maxLod = bit::encodeFixed<uint32_t, 4, 8>(max);
u.p.lodBias = bit::encodeFixed<int32_t, 6, 8>(bias);
}
void setBorderColor(VkClearColorValue color) {
borderColor = color;
}
bool eq(const DxvkSamplerKey& other) const {
bool eq = u.properties[0] == other.u.properties[0]
&& u.properties[1] == other.u.properties[1];
if (eq && u.p.hasBorder) {
eq = borderColor.uint32[0] == other.borderColor.uint32[0]
&& borderColor.uint32[1] == other.borderColor.uint32[1]
&& borderColor.uint32[2] == other.borderColor.uint32[2]
&& borderColor.uint32[3] == other.borderColor.uint32[3];
}
return eq;
}
size_t hash() const {
DxvkHashState hash;
hash.add(u.properties[0]);
hash.add(u.properties[1]);
if (u.p.hasBorder) {
hash.add(borderColor.uint32[0]);
hash.add(borderColor.uint32[1]);
hash.add(borderColor.uint32[2]);
hash.add(borderColor.uint32[3]);
}
return hash;
}
/// Enables non seamless cube map filtering
VkBool32 nonSeamless;
};
static_assert(sizeof(DxvkSamplerKey) == 24u);
/**
@ -54,15 +135,33 @@ namespace dxvk {
* a pipeline. Sampler objects provide parameters
* for texture lookups within a shader.
*/
class DxvkSampler : public DxvkResource {
class DxvkSampler {
friend class DxvkSamplerPool;
public:
DxvkSampler(
DxvkDevice* device,
const DxvkSamplerCreateInfo& info);
DxvkSamplerPool* pool,
const DxvkSamplerKey& key);
~DxvkSampler();
/**
* \brief Increments reference count
*/
force_inline void incRef() {
m_refCount.fetch_add(1u, std::memory_order_acquire);
}
/**
* \brief Decrements reference count
*
* Recycles the sampler once the ref count reaches zero.
*/
force_inline void decRef() {
if (m_refCount.fetch_sub(1u, std::memory_order_relaxed) == 1u)
release();
}
/**
* \brief Sampler handle
* \returns Sampler handle
@ -71,15 +170,77 @@ namespace dxvk {
return m_sampler;
}
/**
* \brief Sampler key
* \returns Sampler properties
*/
const DxvkSamplerKey& key() const {
return m_key;
}
private:
Rc<vk::DeviceFn> m_vkd;
VkSampler m_sampler = VK_NULL_HANDLE;
std::atomic<uint32_t> m_refCount = { 0u };
DxvkSamplerPool* m_pool = nullptr;
DxvkSamplerKey m_key = { };
VkSampler m_sampler = VK_NULL_HANDLE;
DxvkSampler* m_lruPrev = nullptr;
DxvkSampler* m_lruNext = nullptr;
void release();
VkBorderColor determineBorderColorType() const;
static VkBorderColor getBorderColor(
const Rc<DxvkDevice>& device,
const DxvkSamplerCreateInfo& info);
};
/**
* \brief Sampler pool
*
* Manages unique samplers within a device.
*/
class DxvkSamplerPool {
friend DxvkSampler;
public:
// The Vulkan limit for samplers is at least 4000.
// Keep some objects available for internal use.
constexpr static uint32_t MaxSamplerCount = 3584u;
// Minimum number of samplers to keep alive.
constexpr static uint32_t MinSamplerCount = 1024u;
DxvkSamplerPool(DxvkDevice* device);
~DxvkSamplerPool();
/**
* \brief Creates sampler
*
* \param [in] key Sampler key
* \returns Sampler object
*/
Rc<DxvkSampler> createSampler(const DxvkSamplerKey& key);
private:
DxvkDevice* m_device;
dxvk::mutex m_mutex;
std::unordered_map<DxvkSamplerKey,
DxvkSampler, DxvkHash, DxvkEq> m_samplers;
DxvkSampler* m_lruHead = nullptr;
DxvkSampler* m_lruTail = nullptr;
void releaseSampler(DxvkSampler* sampler);
void destroyLeastRecentlyUsedSampler();
};
}

View File

@ -286,30 +286,23 @@ namespace dxvk {
void DxvkSwapchainBlitter::createSampler() {
DxvkSamplerCreateInfo samplerInfo;
samplerInfo.magFilter = VK_FILTER_LINEAR;
samplerInfo.minFilter = VK_FILTER_LINEAR;
samplerInfo.mipmapMode = VK_SAMPLER_MIPMAP_MODE_NEAREST;
samplerInfo.mipmapLodBias = 0.0f;
samplerInfo.mipmapLodMin = 0.0f;
samplerInfo.mipmapLodMax = 0.0f;
samplerInfo.useAnisotropy = VK_FALSE;
samplerInfo.maxAnisotropy = 1.0f;
samplerInfo.addressModeU = VK_SAMPLER_ADDRESS_MODE_CLAMP_TO_BORDER;
samplerInfo.addressModeV = VK_SAMPLER_ADDRESS_MODE_CLAMP_TO_BORDER;
samplerInfo.addressModeW = VK_SAMPLER_ADDRESS_MODE_CLAMP_TO_BORDER;
samplerInfo.compareToDepth = VK_FALSE;
samplerInfo.compareOp = VK_COMPARE_OP_ALWAYS;
samplerInfo.reductionMode = VK_SAMPLER_REDUCTION_MODE_WEIGHTED_AVERAGE;
samplerInfo.borderColor = VkClearColorValue();
samplerInfo.usePixelCoord = VK_TRUE;
samplerInfo.nonSeamless = VK_FALSE;
DxvkSamplerKey samplerInfo = { };
samplerInfo.setFilter(VK_FILTER_LINEAR, VK_FILTER_LINEAR,
VK_SAMPLER_MIPMAP_MODE_NEAREST);
samplerInfo.setAddressModes(
VK_SAMPLER_ADDRESS_MODE_CLAMP_TO_BORDER,
VK_SAMPLER_ADDRESS_MODE_CLAMP_TO_BORDER,
VK_SAMPLER_ADDRESS_MODE_CLAMP_TO_BORDER);
samplerInfo.setUsePixelCoordinates(true);
m_samplerPresent = m_device->createSampler(samplerInfo);
samplerInfo.addressModeU = VK_SAMPLER_ADDRESS_MODE_CLAMP_TO_EDGE;
samplerInfo.addressModeV = VK_SAMPLER_ADDRESS_MODE_CLAMP_TO_EDGE;
samplerInfo.addressModeW = VK_SAMPLER_ADDRESS_MODE_CLAMP_TO_EDGE;
samplerInfo.usePixelCoord = VK_FALSE;
samplerInfo.setAddressModes(
VK_SAMPLER_ADDRESS_MODE_CLAMP_TO_EDGE,
VK_SAMPLER_ADDRESS_MODE_CLAMP_TO_EDGE,
VK_SAMPLER_ADDRESS_MODE_CLAMP_TO_EDGE);
samplerInfo.setUsePixelCoordinates(false);
m_samplerGamma = m_device->createSampler(samplerInfo);
}

View File

@ -54,25 +54,15 @@ namespace dxvk {
Rc<DxvkSampler> DxvkUnboundResources::createSampler() {
DxvkSamplerCreateInfo info;
info.minFilter = VK_FILTER_LINEAR;
info.magFilter = VK_FILTER_LINEAR;
info.mipmapMode = VK_SAMPLER_MIPMAP_MODE_LINEAR;
info.mipmapLodBias = 0.0f;
info.mipmapLodMin = -256.0f;
info.mipmapLodMax = 256.0f;
info.useAnisotropy = VK_FALSE;
info.maxAnisotropy = 1.0f;
info.addressModeU = VK_SAMPLER_ADDRESS_MODE_CLAMP_TO_EDGE;
info.addressModeV = VK_SAMPLER_ADDRESS_MODE_CLAMP_TO_EDGE;
info.addressModeW = VK_SAMPLER_ADDRESS_MODE_CLAMP_TO_EDGE;
info.compareToDepth = VK_FALSE;
info.compareOp = VK_COMPARE_OP_NEVER;
info.reductionMode = VK_SAMPLER_REDUCTION_MODE_WEIGHTED_AVERAGE;
info.borderColor = VkClearColorValue();
info.usePixelCoord = VK_FALSE;
info.nonSeamless = VK_FALSE;
DxvkSamplerKey info;
info.setFilter(VK_FILTER_LINEAR, VK_FILTER_LINEAR, VK_SAMPLER_MIPMAP_MODE_LINEAR);
info.setLodRange(-256.0f, 256.0f, 0.0f);
info.setAddressModes(
VK_SAMPLER_ADDRESS_MODE_CLAMP_TO_EDGE,
VK_SAMPLER_ADDRESS_MODE_CLAMP_TO_EDGE,
VK_SAMPLER_ADDRESS_MODE_CLAMP_TO_EDGE);
info.setReduction(VK_SAMPLER_REDUCTION_MODE_WEIGHTED_AVERAGE);
return m_device->createSampler(info);
}

View File

@ -321,24 +321,14 @@ namespace dxvk::hud {
Rc<DxvkSampler> HudRenderer::createFontSampler() {
DxvkSamplerCreateInfo info;
info.magFilter = VK_FILTER_LINEAR;
info.minFilter = VK_FILTER_LINEAR;
info.mipmapMode = VK_SAMPLER_MIPMAP_MODE_NEAREST;
info.mipmapLodBias = 0.0f;
info.mipmapLodMin = 0.0f;
info.mipmapLodMax = 0.0f;
info.useAnisotropy = VK_FALSE;
info.maxAnisotropy = 1.0f;
info.addressModeU = VK_SAMPLER_ADDRESS_MODE_CLAMP_TO_EDGE;
info.addressModeV = VK_SAMPLER_ADDRESS_MODE_CLAMP_TO_EDGE;
info.addressModeW = VK_SAMPLER_ADDRESS_MODE_CLAMP_TO_EDGE;
info.compareToDepth = VK_FALSE;
info.compareOp = VK_COMPARE_OP_NEVER;
info.reductionMode = VK_SAMPLER_REDUCTION_MODE_WEIGHTED_AVERAGE;
info.borderColor = VkClearColorValue();
info.usePixelCoord = VK_TRUE;
info.nonSeamless = VK_FALSE;
DxvkSamplerKey info = { };
info.setFilter(VK_FILTER_LINEAR,
VK_FILTER_LINEAR, VK_SAMPLER_MIPMAP_MODE_NEAREST);
info.setAddressModes(
VK_SAMPLER_ADDRESS_MODE_CLAMP_TO_EDGE,
VK_SAMPLER_ADDRESS_MODE_CLAMP_TO_EDGE,
VK_SAMPLER_ADDRESS_MODE_CLAMP_TO_EDGE);
info.setUsePixelCoordinates(true);
return m_device->createSampler(info);
}

View File

@ -1,5 +1,7 @@
#pragma once
#include <cstddef>
#include <cstdint>
#include <functional>
#include <ostream>
#include <utility>
@ -107,19 +109,47 @@ namespace dxvk {
return m_object != nullptr;
}
/**
* \brief Sets pointer without acquiring a reference
*
* Must only be use when a reference has been taken via
* other means.
* \param [in] object Object pointer
*/
void unsafeInsert(T* object) {
this->decRef();
m_object = object;
}
/**
* \brief Extracts raw pointer
*
* Sets the smart pointer to null without decrementing the
* reference count. Must only be used when the reference
* count is decremented in some other way.
* \returns Pointer to owned object
*/
T* unsafeExtract() {
return std::exchange(m_object, nullptr);
}
/**
* \brief Creates smart pointer without taking reference
*
* Must only be used when a refernece has been obtained via other means.
* \param [in] object Pointer to object to take ownership of
*/
static Rc<T> unsafeCreate(T* object) {
return Rc<T>(object, false);
}
private:
T* m_object = nullptr;
explicit Rc(T* object, bool)
: m_object(object) { }
force_inline void incRef() const {
if (m_object != nullptr)
m_object->incRef();
@ -145,6 +175,13 @@ namespace dxvk {
template<typename Tx, typename Ty>
bool operator != (Tx* a, const Rc<Ty>& b) { return b != a; }
struct RcHash {
template<typename T>
size_t operator () (const Rc<T>& rc) const {
return reinterpret_cast<uintptr_t>(rc.ptr()) / sizeof(T);
}
};
}
template<typename T>