mirror of
https://github.com/skyline-emu/skyline.git
synced 2024-11-27 00:24:14 +01:00
Implement combined image samplers and make descriptor code common between quick/normal updates
This commit is contained in:
parent
ccf2d59351
commit
55b85d0691
@ -637,18 +637,65 @@ namespace skyline::gpu::interconnect::maxwell3d {
|
|||||||
return view;
|
return view;
|
||||||
}
|
}
|
||||||
|
|
||||||
// TODO: EXEC ID FOR STORAGE BUFS PURGE REMAP
|
union BindlessHandle {
|
||||||
DescriptorUpdateInfo *Pipeline::SyncDescriptors(InterconnectContext &ctx, ConstantBufferSet &constantBuffers) {
|
u32 raw;
|
||||||
u32 bindingIdx{};
|
|
||||||
size_t writeIdx{};
|
|
||||||
size_t bufferIdx{};
|
|
||||||
size_t imageIdx{};
|
|
||||||
size_t storageBufferIdx{};
|
|
||||||
|
|
||||||
|
struct {
|
||||||
|
u32 textureIndex : 20;
|
||||||
|
u32 samplerIndex : 12;
|
||||||
|
};
|
||||||
|
};
|
||||||
|
|
||||||
|
static BindlessHandle ReadBindlessHandle(InterconnectContext &ctx, std::array<ConstantBuffer, engine::ShaderStageConstantBufferCount> &constantBuffers, const auto &desc, size_t arrayIdx) {
|
||||||
|
ConstantBuffer &primaryCbuf{constantBuffers[desc.cbuf_index]};
|
||||||
|
size_t elemOffset{arrayIdx << desc.size_shift};
|
||||||
|
size_t primaryCbufOffset{desc.cbuf_offset + elemOffset};
|
||||||
|
u32 primaryVal{primaryCbuf.Read<u32>(ctx.executor, primaryCbufOffset)};
|
||||||
|
|
||||||
|
if constexpr (requires { desc.has_secondary; } ) {
|
||||||
|
if (desc.has_secondary) {
|
||||||
|
ConstantBuffer &secondaryCbuf{constantBuffers[desc.secondary_cbuf_index]};
|
||||||
|
size_t secondaryCbufOffset{desc.secondary_cbuf_offset + elemOffset};
|
||||||
|
u32 secondaryVal{secondaryCbuf.Read<u32>(ctx.executor, secondaryCbufOffset)};
|
||||||
|
return {primaryVal | secondaryVal};
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return { .raw = primaryVal };
|
||||||
|
}
|
||||||
|
|
||||||
|
static vk::DescriptorImageInfo GetTextureBinding(InterconnectContext &ctx, const Shader::TextureDescriptor &desc, Samplers &samplers, Textures &textures, BindlessHandle handle) {
|
||||||
|
auto sampler{samplers.GetSampler(ctx, handle.samplerIndex, handle.textureIndex)};
|
||||||
|
auto texture{textures.GetTexture(ctx, handle.textureIndex, desc.type)};
|
||||||
|
ctx.executor.AttachTexture(texture);
|
||||||
|
auto view{texture->GetView()};
|
||||||
|
|
||||||
|
return vk::DescriptorImageInfo{
|
||||||
|
.sampler = **sampler,
|
||||||
|
.imageView = view,
|
||||||
|
.imageLayout = texture->texture->layout,
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
|
DescriptorUpdateInfo *Pipeline::SyncDescriptors(InterconnectContext &ctx, ConstantBufferSet &constantBuffers, Samplers &samplers, Textures &textures) {
|
||||||
|
SyncCachedStorageBufferViews(ctx.executor.executionNumber);
|
||||||
|
|
||||||
|
u32 writeIdx{};
|
||||||
auto writes{ctx.executor.allocator->AllocateUntracked<vk::WriteDescriptorSet>(descriptorInfo.totalWriteDescCount)};
|
auto writes{ctx.executor.allocator->AllocateUntracked<vk::WriteDescriptorSet>(descriptorInfo.totalWriteDescCount)};
|
||||||
|
|
||||||
|
u32 bufferIdx{};
|
||||||
auto bufferDescs{ctx.executor.allocator->AllocateUntracked<vk::DescriptorBufferInfo>(descriptorInfo.totalBufferDescCount)};
|
auto bufferDescs{ctx.executor.allocator->AllocateUntracked<vk::DescriptorBufferInfo>(descriptorInfo.totalBufferDescCount)};
|
||||||
auto bufferDescDynamicBindings{ctx.executor.allocator->AllocateUntracked<DynamicBufferBinding>(descriptorInfo.totalBufferDescCount)};
|
auto bufferDescDynamicBindings{ctx.executor.allocator->AllocateUntracked<DynamicBufferBinding>(descriptorInfo.totalBufferDescCount)};
|
||||||
|
u32 imageIdx{};
|
||||||
|
auto imageDescs{ctx.executor.allocator->AllocateUntracked<vk::DescriptorImageInfo>(descriptorInfo.totalImageDescCount)};
|
||||||
|
|
||||||
|
u32 storageBufferIdx{}; // Need to keep track of this since to index into the cached view array
|
||||||
|
u32 bindingIdx{};
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @brief Adds descriptor writes for a single Vulkan descriptor type that uses buffer descriptors
|
||||||
|
* @param count Total number of descriptors to write, including array elements
|
||||||
|
*/
|
||||||
auto writeBufferDescs{[&](vk::DescriptorType type, const auto &descs, u32 count, auto getBufferCb) {
|
auto writeBufferDescs{[&](vk::DescriptorType type, const auto &descs, u32 count, auto getBufferCb) {
|
||||||
if (!descs.empty()) {
|
if (!descs.empty()) {
|
||||||
writes[writeIdx++] = {
|
writes[writeIdx++] = {
|
||||||
@ -660,12 +707,42 @@ namespace skyline::gpu::interconnect::maxwell3d {
|
|||||||
|
|
||||||
bindingIdx += descs.size();
|
bindingIdx += descs.size();
|
||||||
|
|
||||||
|
// The underlying buffer bindings will be resolved from the dynamic ones during recording
|
||||||
for (const auto &desc : descs)
|
for (const auto &desc : descs)
|
||||||
for (size_t arrayIdx{}; arrayIdx < desc.count; arrayIdx++)
|
for (u32 arrayIdx{}; arrayIdx < desc.count; arrayIdx++)
|
||||||
bufferDescDynamicBindings[bufferIdx++] = getBufferCb(desc, arrayIdx);
|
bufferDescDynamicBindings[bufferIdx++] = getBufferCb(desc, arrayIdx);
|
||||||
}
|
}
|
||||||
}};
|
}};
|
||||||
|
|
||||||
|
auto writeImageDescs{[&](vk::DescriptorType type, const auto &descs, u32 count, auto getTextureCb, bool needsIndividualTextureBindingWrites) {
|
||||||
|
if (!descs.empty()) {
|
||||||
|
if (!needsIndividualTextureBindingWrites) {
|
||||||
|
writes[writeIdx++] = {
|
||||||
|
.dstBinding = bindingIdx,
|
||||||
|
.descriptorCount = count,
|
||||||
|
.descriptorType = type,
|
||||||
|
.pImageInfo = &imageDescs[imageIdx],
|
||||||
|
};
|
||||||
|
|
||||||
|
bindingIdx += descs.size();
|
||||||
|
}
|
||||||
|
|
||||||
|
for (const auto &desc : descs) {
|
||||||
|
if (needsIndividualTextureBindingWrites) {
|
||||||
|
writes[writeIdx++] = {
|
||||||
|
.dstBinding = bindingIdx++,
|
||||||
|
.descriptorCount = desc.count,
|
||||||
|
.descriptorType = type,
|
||||||
|
.pImageInfo = &imageDescs[imageIdx],
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
|
for (u32 arrayIdx{}; arrayIdx < desc.count; arrayIdx++)
|
||||||
|
imageDescs[imageIdx++] = getTextureCb(desc, arrayIdx);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}};
|
||||||
|
|
||||||
for (size_t i{}; i < shaderStages.size(); i++) {
|
for (size_t i{}; i < shaderStages.size(); i++) {
|
||||||
const auto &stage{shaderStages[i]};
|
const auto &stage{shaderStages[i]};
|
||||||
if (!stage.module)
|
if (!stage.module)
|
||||||
@ -681,12 +758,20 @@ namespace skyline::gpu::interconnect::maxwell3d {
|
|||||||
|
|
||||||
writeBufferDescs(vk::DescriptorType::eStorageBuffer, stage.info.storage_buffers_descriptors, stageDescInfo.storageBufferDescCount,
|
writeBufferDescs(vk::DescriptorType::eStorageBuffer, stage.info.storage_buffers_descriptors, stageDescInfo.storageBufferDescCount,
|
||||||
[&](const Shader::StorageBufferDescriptor &desc, size_t arrayIdx) {
|
[&](const Shader::StorageBufferDescriptor &desc, size_t arrayIdx) {
|
||||||
auto binding{GetStorageBufferBinding(ctx, desc, constantBuffers[i][desc.cbuf_index], storageBufferViews[storageBufferIdx])};
|
auto binding{GetStorageBufferBinding(ctx, desc, constantBuffers[i][desc.cbuf_index], storageBufferViews[storageBufferIdx - arrayIdx ? 1 : 0])};
|
||||||
|
// Storage buffer arrays all share the same view index, so to only increment the index once per array do it at element zero and subtract that for all subsequent array elems (see above)
|
||||||
storageBufferIdx += arrayIdx ? 0 : 1;
|
storageBufferIdx += arrayIdx ? 0 : 1;
|
||||||
return binding;
|
return binding;
|
||||||
});
|
});
|
||||||
|
|
||||||
|
writeImageDescs(vk::DescriptorType::eCombinedImageSampler, stage.info.texture_descriptors, stageDescInfo.combinedImageSamplerDescCount,
|
||||||
|
[&](const Shader::TextureDescriptor &desc, size_t arrayIdx) {
|
||||||
|
BindlessHandle handle{ReadBindlessHandle(ctx, constantBuffers[i], desc, arrayIdx)};
|
||||||
|
return GetTextureBinding(ctx, desc, samplers, textures, handle);
|
||||||
|
}, ctx.gpu.traits.quirks.needsIndividualTextureBindingWrites);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// Since we don't implement all descriptor types the number of writes might not match what's expected
|
||||||
if (!writeIdx)
|
if (!writeIdx)
|
||||||
return nullptr;
|
return nullptr;
|
||||||
|
|
||||||
@ -701,7 +786,9 @@ namespace skyline::gpu::interconnect::maxwell3d {
|
|||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
DescriptorUpdateInfo *Pipeline::SyncDescriptorsQuickBind(InterconnectContext &ctx, ConstantBufferSet &constantBuffers, ConstantBuffers::QuickBind quickBind) {
|
DescriptorUpdateInfo *Pipeline::SyncDescriptorsQuickBind(InterconnectContext &ctx, ConstantBufferSet &constantBuffers, Samplers &samplers, Textures &textures, ConstantBuffers::QuickBind quickBind) {
|
||||||
|
SyncCachedStorageBufferViews(ctx.executor.executionNumber);
|
||||||
|
|
||||||
size_t stageIndex{static_cast<size_t>(quickBind.stage)};
|
size_t stageIndex{static_cast<size_t>(quickBind.stage)};
|
||||||
const auto &stageDescInfo{descriptorInfo.stages[stageIndex]};
|
const auto &stageDescInfo{descriptorInfo.stages[stageIndex]};
|
||||||
const auto &cbufUsageInfo{stageDescInfo.cbufUsages[quickBind.index]};
|
const auto &cbufUsageInfo{stageDescInfo.cbufUsages[quickBind.index]};
|
||||||
@ -721,33 +808,54 @@ namespace skyline::gpu::interconnect::maxwell3d {
|
|||||||
u32 imageIdx{};
|
u32 imageIdx{};
|
||||||
auto imageDescs{ctx.executor.allocator->AllocateUntracked<vk::DescriptorImageInfo>(cbufUsageInfo.totalImageDescCount)};
|
auto imageDescs{ctx.executor.allocator->AllocateUntracked<vk::DescriptorImageInfo>(cbufUsageInfo.totalImageDescCount)};
|
||||||
|
|
||||||
auto writeBufferDescs{[&](vk::DescriptorType type, const auto &usages, const auto &descs, auto getBufferCb) {
|
/**
|
||||||
|
* @brief Unified function to add descriptor set writes for any descriptor type
|
||||||
|
* @note Since quick bind always results in one write per buffer, `needsIndividualTextureBindingWrites` is implicit
|
||||||
|
*/
|
||||||
|
auto writeDescs{[&]<bool ImageDesc, bool BufferDesc>(vk::DescriptorType type, const auto &usages, const auto &descs, auto getBindingCb) {
|
||||||
for (const auto &usage : usages) {
|
for (const auto &usage : usages) {
|
||||||
const auto &shaderDesc{descs[usage.shaderDescIdx]};
|
const auto &shaderDesc{descs[usage.shaderDescIdx]};
|
||||||
|
|
||||||
writes[writeIdx++] = {
|
writes[writeIdx] = {
|
||||||
.dstBinding = usage.binding,
|
.dstBinding = usage.binding,
|
||||||
.descriptorCount = shaderDesc.count,
|
.descriptorCount = shaderDesc.count,
|
||||||
.descriptorType = type,
|
.descriptorType = type,
|
||||||
.pBufferInfo = &bufferDescs[bufferIdx],
|
|
||||||
};
|
};
|
||||||
|
|
||||||
for (size_t i{}; i < shaderDesc.count; i++)
|
if constexpr (ImageDesc)
|
||||||
bufferDescDynamicBindings[bufferIdx++] = getBufferCb(usage, shaderDesc, i);
|
writes[writeIdx].pImageInfo = &imageDescs[imageIdx];
|
||||||
|
else if constexpr (BufferDesc)
|
||||||
|
writes[writeIdx].pBufferInfo = &bufferDescs[bufferIdx];
|
||||||
|
|
||||||
|
writeIdx++;
|
||||||
|
|
||||||
|
for (size_t i{}; i < shaderDesc.count; i++) {
|
||||||
|
if constexpr (ImageDesc)
|
||||||
|
imageDescs[imageIdx++] = getBindingCb(usage, shaderDesc, i);
|
||||||
|
else if constexpr (BufferDesc)
|
||||||
|
bufferDescDynamicBindings[bufferIdx++] = getBindingCb(usage, shaderDesc, i);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}};
|
}};
|
||||||
|
|
||||||
writeBufferDescs(vk::DescriptorType::eUniformBuffer, cbufUsageInfo.uniformBuffers, shaderInfo.constant_buffer_descriptors,
|
writeDescs.operator()<false, true>(vk::DescriptorType::eUniformBuffer, cbufUsageInfo.uniformBuffers, shaderInfo.constant_buffer_descriptors,
|
||||||
[&](auto usage, const Shader::ConstantBufferDescriptor &desc, size_t arrayIdx) -> DynamicBufferBinding {
|
[&](auto usage, const Shader::ConstantBufferDescriptor &desc, size_t arrayIdx) -> DynamicBufferBinding {
|
||||||
size_t cbufIdx{desc.index + arrayIdx};
|
size_t cbufIdx{desc.index + arrayIdx};
|
||||||
return GetConstantBufferBinding(ctx, shaderInfo, stageConstantBuffers[cbufIdx].view, cbufIdx);
|
return GetConstantBufferBinding(ctx, shaderInfo, stageConstantBuffers[cbufIdx].view, cbufIdx);
|
||||||
});
|
});
|
||||||
|
|
||||||
writeBufferDescs(vk::DescriptorType::eStorageBuffer, cbufUsageInfo.storageBuffers, shaderInfo.storage_buffers_descriptors,
|
writeDescs.operator()<false, true>(vk::DescriptorType::eStorageBuffer, cbufUsageInfo.storageBuffers, shaderInfo.storage_buffers_descriptors,
|
||||||
[&](auto usage, const Shader::StorageBufferDescriptor &desc, size_t arrayIdx) {
|
[&](auto usage, const Shader::StorageBufferDescriptor &desc, size_t arrayIdx) {
|
||||||
return GetStorageBufferBinding(ctx, desc, stageConstantBuffers[desc.cbuf_index], storageBufferViews[usage.storageBufferIdx]);
|
return GetStorageBufferBinding(ctx, desc, stageConstantBuffers[desc.cbuf_index], storageBufferViews[usage.storageBufferIdx]);
|
||||||
});
|
});
|
||||||
|
|
||||||
|
writeDescs.operator()<true, false>(vk::DescriptorType::eCombinedImageSampler, cbufUsageInfo.combinedImageSamplers, shaderInfo.texture_descriptors,
|
||||||
|
[&](auto usage, const Shader::TextureDescriptor &desc, size_t arrayIdx) {
|
||||||
|
BindlessHandle handle{ReadBindlessHandle(ctx, stageConstantBuffers, desc, arrayIdx)};
|
||||||
|
return GetTextureBinding(ctx, desc, samplers, textures, handle);
|
||||||
|
});
|
||||||
|
|
||||||
|
// Since we don't implement all descriptor types the number of writes might not match what's expected
|
||||||
if (!writeIdx)
|
if (!writeIdx)
|
||||||
return nullptr;
|
return nullptr;
|
||||||
|
|
||||||
|
@ -13,7 +13,8 @@ namespace skyline::gpu::interconnect::maxwell3d {
|
|||||||
SamplerPoolState::SamplerPoolState(dirty::Handle dirtyHandle, DirtyManager &manager, const EngineRegisters &engine) : engine{manager, dirtyHandle, engine} {}
|
SamplerPoolState::SamplerPoolState(dirty::Handle dirtyHandle, DirtyManager &manager, const EngineRegisters &engine) : engine{manager, dirtyHandle, engine} {}
|
||||||
|
|
||||||
void SamplerPoolState::Flush(InterconnectContext &ctx) {
|
void SamplerPoolState::Flush(InterconnectContext &ctx) {
|
||||||
u32 maximumIndex{engine->samplerBinding.value == engine::SamplerBinding::Value::ViaHeaderBinding ? engine->texHeaderPool.maximumIndex : engine->texSamplerPool.maximumIndex};
|
useTexHeaderBinding = engine->samplerBinding.value == engine::SamplerBinding::Value::ViaHeaderBinding;
|
||||||
|
u32 maximumIndex{useTexHeaderBinding ? engine->texHeaderPool.maximumIndex : engine->texSamplerPool.maximumIndex};
|
||||||
auto mapping{ctx.channelCtx.asCtx->gmmu.LookupBlock(engine->texSamplerPool.offset)};
|
auto mapping{ctx.channelCtx.asCtx->gmmu.LookupBlock(engine->texSamplerPool.offset)};
|
||||||
|
|
||||||
texSamplers = mapping.first.subspan(mapping.second).cast<TextureSamplerControl>().first(maximumIndex + 1);
|
texSamplers = mapping.first.subspan(mapping.second).cast<TextureSamplerControl>().first(maximumIndex + 1);
|
||||||
@ -27,6 +28,7 @@ namespace skyline::gpu::interconnect::maxwell3d {
|
|||||||
|
|
||||||
void Samplers::MarkAllDirty() {
|
void Samplers::MarkAllDirty() {
|
||||||
samplerPool.MarkDirty(true);
|
samplerPool.MarkDirty(true);
|
||||||
|
std::fill(texSamplerCache.begin(), texSamplerCache.end(), nullptr);
|
||||||
}
|
}
|
||||||
|
|
||||||
static vk::Filter ConvertSamplerFilter(TextureSamplerControl::Filter filter) {
|
static vk::Filter ConvertSamplerFilter(TextureSamplerControl::Filter filter) {
|
||||||
@ -137,63 +139,72 @@ namespace skyline::gpu::interconnect::maxwell3d {
|
|||||||
return vk::BorderColor::eFloatTransparentBlack;
|
return vk::BorderColor::eFloatTransparentBlack;
|
||||||
}
|
}
|
||||||
|
|
||||||
std::shared_ptr<vk::raii::Sampler> Samplers::GetSampler(InterconnectContext &ctx, u32 index) {
|
vk::raii::Sampler *Samplers::GetSampler(InterconnectContext &ctx, u32 samplerIndex, u32 textureIndex) {
|
||||||
auto texSamplers{samplerPool.UpdateGet(ctx).texSamplers};
|
const auto &samplerPoolObj{samplerPool.UpdateGet(ctx)};
|
||||||
|
u32 index{samplerPoolObj.useTexHeaderBinding ? textureIndex : samplerIndex};
|
||||||
TextureSamplerControl &texSampler{texSamplers[index]};
|
auto texSamplers{samplerPoolObj.texSamplers};
|
||||||
auto &sampler{texSamplerCache[texSampler]};
|
if (texSamplers.size() != texSamplerCache.size()) {
|
||||||
if (sampler)
|
texSamplerCache.resize(texSamplers.size());
|
||||||
return sampler;
|
std::fill(texSamplerCache.begin(), texSamplerCache.end(), nullptr);
|
||||||
|
} else if (texSamplerCache[index]) {
|
||||||
auto convertAddressModeWithCheck{[&](TextureSamplerControl::AddressMode mode) {
|
return texSamplerCache[index];
|
||||||
auto vkMode{ConvertSamplerAddressMode(mode)};
|
|
||||||
if (vkMode == vk::SamplerAddressMode::eMirrorClampToEdge && !ctx.gpu.traits.supportsSamplerMirrorClampToEdge) [[unlikely]] {
|
|
||||||
Logger::Warn("Cannot use Mirror Clamp To Edge as Sampler Address Mode without host GPU support");
|
|
||||||
return vk::SamplerAddressMode::eClampToEdge; // We use a normal clamp to edge to approximate it
|
|
||||||
}
|
|
||||||
return vkMode;
|
|
||||||
}};
|
|
||||||
|
|
||||||
auto maxAnisotropy{texSampler.MaxAnisotropy()};
|
|
||||||
vk::StructureChain<vk::SamplerCreateInfo, vk::SamplerReductionModeCreateInfoEXT, vk::SamplerCustomBorderColorCreateInfoEXT> samplerInfo{
|
|
||||||
vk::SamplerCreateInfo{
|
|
||||||
.magFilter = ConvertSamplerFilter(texSampler.magFilter),
|
|
||||||
.minFilter = ConvertSamplerFilter(texSampler.minFilter),
|
|
||||||
.mipmapMode = ConvertSamplerMipFilter(texSampler.mipFilter),
|
|
||||||
.addressModeU = convertAddressModeWithCheck(texSampler.addressModeU),
|
|
||||||
.addressModeV = convertAddressModeWithCheck(texSampler.addressModeV),
|
|
||||||
.addressModeW = convertAddressModeWithCheck(texSampler.addressModeP),
|
|
||||||
.mipLodBias = texSampler.MipLodBias(),
|
|
||||||
.anisotropyEnable = ctx.gpu.traits.supportsAnisotropicFiltering && maxAnisotropy > 1.0f,
|
|
||||||
.maxAnisotropy = maxAnisotropy,
|
|
||||||
.compareEnable = texSampler.depthCompareEnable,
|
|
||||||
.compareOp = ConvertSamplerCompareOp(texSampler.depthCompareOp),
|
|
||||||
.minLod = texSampler.mipFilter == TextureSamplerControl::MipFilter::None ? 0.0f : texSampler.MinLodClamp(),
|
|
||||||
.maxLod = texSampler.mipFilter == TextureSamplerControl::MipFilter::None ? 0.25f : texSampler.MaxLodClamp(),
|
|
||||||
.unnormalizedCoordinates = false,
|
|
||||||
}, vk::SamplerReductionModeCreateInfoEXT{
|
|
||||||
.reductionMode = ConvertSamplerReductionFilter(texSampler.reductionFilter),
|
|
||||||
}, vk::SamplerCustomBorderColorCreateInfoEXT{
|
|
||||||
.customBorderColor.float32 = {{texSampler.borderColorR, texSampler.borderColorG, texSampler.borderColorB, texSampler.borderColorA}},
|
|
||||||
.format = vk::Format::eUndefined,
|
|
||||||
},
|
|
||||||
};
|
|
||||||
|
|
||||||
if (!ctx.gpu.traits.supportsSamplerReductionMode)
|
|
||||||
samplerInfo.unlink<vk::SamplerReductionModeCreateInfoEXT>();
|
|
||||||
|
|
||||||
vk::BorderColor &borderColor{samplerInfo.get<vk::SamplerCreateInfo>().borderColor};
|
|
||||||
if (ctx.gpu.traits.supportsCustomBorderColor) {
|
|
||||||
borderColor = ConvertBorderColorWithCustom(texSampler.borderColorR, texSampler.borderColorG, texSampler.borderColorB, texSampler.borderColorA);
|
|
||||||
if (borderColor != vk::BorderColor::eFloatCustomEXT)
|
|
||||||
samplerInfo.unlink<vk::SamplerCustomBorderColorCreateInfoEXT>();
|
|
||||||
} else {
|
|
||||||
borderColor = ConvertBorderColorFixed(texSampler.borderColorR, texSampler.borderColorG, texSampler.borderColorB, texSampler.borderColorA);
|
|
||||||
samplerInfo.unlink<vk::SamplerCustomBorderColorCreateInfoEXT>();
|
|
||||||
}
|
}
|
||||||
|
|
||||||
sampler = std::make_shared<vk::raii::Sampler>(ctx.gpu.vkDevice, samplerInfo.get<vk::SamplerCreateInfo>());
|
TextureSamplerControl &texSampler{texSamplers[index]};
|
||||||
return sampler;
|
auto &sampler{texSamplerStore[texSampler]};
|
||||||
|
if (!sampler) {
|
||||||
|
auto convertAddressModeWithCheck{[&](TextureSamplerControl::AddressMode mode) {
|
||||||
|
auto vkMode{ConvertSamplerAddressMode(mode)};
|
||||||
|
if (vkMode == vk::SamplerAddressMode::eMirrorClampToEdge && !ctx.gpu.traits.supportsSamplerMirrorClampToEdge) [[unlikely]] {
|
||||||
|
Logger::Warn("Cannot use Mirror Clamp To Edge as Sampler Address Mode without host GPU support");
|
||||||
|
return vk::SamplerAddressMode::eClampToEdge; // We use a normal clamp to edge to approximate it
|
||||||
|
}
|
||||||
|
return vkMode;
|
||||||
|
}};
|
||||||
|
|
||||||
|
auto maxAnisotropy{texSampler.MaxAnisotropy()};
|
||||||
|
vk::StructureChain<vk::SamplerCreateInfo, vk::SamplerReductionModeCreateInfoEXT, vk::SamplerCustomBorderColorCreateInfoEXT> samplerInfo{
|
||||||
|
vk::SamplerCreateInfo{
|
||||||
|
.magFilter = ConvertSamplerFilter(texSampler.magFilter),
|
||||||
|
.minFilter = ConvertSamplerFilter(texSampler.minFilter),
|
||||||
|
.mipmapMode = ConvertSamplerMipFilter(texSampler.mipFilter),
|
||||||
|
.addressModeU = convertAddressModeWithCheck(texSampler.addressModeU),
|
||||||
|
.addressModeV = convertAddressModeWithCheck(texSampler.addressModeV),
|
||||||
|
.addressModeW = convertAddressModeWithCheck(texSampler.addressModeP),
|
||||||
|
.mipLodBias = texSampler.MipLodBias(),
|
||||||
|
.anisotropyEnable = ctx.gpu.traits.supportsAnisotropicFiltering && maxAnisotropy > 1.0f,
|
||||||
|
.maxAnisotropy = maxAnisotropy,
|
||||||
|
.compareEnable = texSampler.depthCompareEnable,
|
||||||
|
.compareOp = ConvertSamplerCompareOp(texSampler.depthCompareOp),
|
||||||
|
.minLod = texSampler.mipFilter == TextureSamplerControl::MipFilter::None ? 0.0f : texSampler.MinLodClamp(),
|
||||||
|
.maxLod = texSampler.mipFilter == TextureSamplerControl::MipFilter::None ? 0.25f : texSampler.MaxLodClamp(),
|
||||||
|
.unnormalizedCoordinates = false,
|
||||||
|
}, vk::SamplerReductionModeCreateInfoEXT{
|
||||||
|
.reductionMode = ConvertSamplerReductionFilter(texSampler.reductionFilter),
|
||||||
|
}, vk::SamplerCustomBorderColorCreateInfoEXT{
|
||||||
|
.customBorderColor.float32 = {{texSampler.borderColorR, texSampler.borderColorG, texSampler.borderColorB, texSampler.borderColorA}},
|
||||||
|
.format = vk::Format::eUndefined,
|
||||||
|
},
|
||||||
|
};
|
||||||
|
|
||||||
|
if (!ctx.gpu.traits.supportsSamplerReductionMode)
|
||||||
|
samplerInfo.unlink<vk::SamplerReductionModeCreateInfoEXT>();
|
||||||
|
|
||||||
|
vk::BorderColor &borderColor{samplerInfo.get<vk::SamplerCreateInfo>().borderColor};
|
||||||
|
if (ctx.gpu.traits.supportsCustomBorderColor) {
|
||||||
|
borderColor = ConvertBorderColorWithCustom(texSampler.borderColorR, texSampler.borderColorG, texSampler.borderColorB, texSampler.borderColorA);
|
||||||
|
if (borderColor != vk::BorderColor::eFloatCustomEXT)
|
||||||
|
samplerInfo.unlink<vk::SamplerCustomBorderColorCreateInfoEXT>();
|
||||||
|
} else {
|
||||||
|
borderColor = ConvertBorderColorFixed(texSampler.borderColorR, texSampler.borderColorG, texSampler.borderColorB, texSampler.borderColorA);
|
||||||
|
samplerInfo.unlink<vk::SamplerCustomBorderColorCreateInfoEXT>();
|
||||||
|
}
|
||||||
|
|
||||||
|
sampler = std::make_unique<vk::raii::Sampler>(ctx.gpu.vkDevice, samplerInfo.get<vk::SamplerCreateInfo>());
|
||||||
|
}
|
||||||
|
|
||||||
|
texSamplerCache[index] = sampler.get();
|
||||||
|
return sampler.get();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
@ -23,6 +23,7 @@ namespace skyline::gpu::interconnect::maxwell3d {
|
|||||||
|
|
||||||
public:
|
public:
|
||||||
span<TextureSamplerControl> texSamplers;
|
span<TextureSamplerControl> texSamplers;
|
||||||
|
bool useTexHeaderBinding;
|
||||||
|
|
||||||
SamplerPoolState(dirty::Handle dirtyHandle, DirtyManager &manager, const EngineRegisters &engine);
|
SamplerPoolState(dirty::Handle dirtyHandle, DirtyManager &manager, const EngineRegisters &engine);
|
||||||
|
|
||||||
@ -35,13 +36,15 @@ namespace skyline::gpu::interconnect::maxwell3d {
|
|||||||
private:
|
private:
|
||||||
dirty::ManualDirtyState<SamplerPoolState> samplerPool;
|
dirty::ManualDirtyState<SamplerPoolState> samplerPool;
|
||||||
|
|
||||||
tsl::robin_map<TextureSamplerControl, std::shared_ptr<vk::raii::Sampler>, util::ObjectHash<TextureSamplerControl>> texSamplerCache;
|
tsl::robin_map<TextureSamplerControl, std::unique_ptr<vk::raii::Sampler>, util::ObjectHash<TextureSamplerControl>> texSamplerStore;
|
||||||
|
std::vector<vk::raii::Sampler *> texSamplerCache;
|
||||||
|
|
||||||
|
|
||||||
public:
|
public:
|
||||||
Samplers(DirtyManager &manager, const SamplerPoolState::EngineRegisters &engine);
|
Samplers(DirtyManager &manager, const SamplerPoolState::EngineRegisters &engine);
|
||||||
|
|
||||||
void MarkAllDirty();
|
void MarkAllDirty();
|
||||||
|
|
||||||
std::shared_ptr<vk::raii::Sampler> GetSampler(InterconnectContext &ctx, u32 index);
|
vk::raii::Sampler *GetSampler(InterconnectContext &ctx, u32 samplerIndex, u32 textureIndex);
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
140
app/src/main/cpp/skyline/gpu/interconnect/maxwell_3d/tsc.h
Normal file
140
app/src/main/cpp/skyline/gpu/interconnect/maxwell_3d/tsc.h
Normal file
@ -0,0 +1,140 @@
|
|||||||
|
// SPDX-License-Identifier: MPL-2.0
|
||||||
|
// Copyright © 2021 Skyline Team and Contributors (https://github.com/skyline-emu/)
|
||||||
|
// Copyright © 2018-2020 fincs (https://github.com/devkitPro/deko3d)
|
||||||
|
|
||||||
|
#pragma once
|
||||||
|
|
||||||
|
#include <common/base.h>
|
||||||
|
|
||||||
|
namespace skyline::gpu::interconnect {
|
||||||
|
#pragma pack(push, 1)
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @brief The Texture Sampler Control is a descriptor used to configure the texture sampler in Maxwell GPUs
|
||||||
|
* @url https://github.com/envytools/envytools/blob/master/rnndb/graph/g80_texture.xml#L367
|
||||||
|
* @url https://github.com/devkitPro/deko3d/blob/00c12d1f4809014f1cc22719dd2e3476735eec64/source/maxwell/texture_sampler_control_block.h
|
||||||
|
*/
|
||||||
|
struct TextureSamplerControl {
|
||||||
|
enum class AddressMode : u32 {
|
||||||
|
Repeat = 0,
|
||||||
|
MirroredRepeat = 1,
|
||||||
|
ClampToEdge = 2,
|
||||||
|
ClampToBorder = 3,
|
||||||
|
Clamp = 4,
|
||||||
|
MirrorClampToEdge = 5,
|
||||||
|
MirrorClampToBorder = 6,
|
||||||
|
MirrorClamp = 7,
|
||||||
|
};
|
||||||
|
|
||||||
|
enum class CompareOp : u32 {
|
||||||
|
Never = 0,
|
||||||
|
Less = 1,
|
||||||
|
Equal = 2,
|
||||||
|
LessOrEqual = 3,
|
||||||
|
Greater = 4,
|
||||||
|
NotEqual = 5,
|
||||||
|
GreaterOrEqual = 6,
|
||||||
|
Always = 7,
|
||||||
|
};
|
||||||
|
|
||||||
|
enum class Filter : u32 {
|
||||||
|
Nearest = 1,
|
||||||
|
Linear = 2,
|
||||||
|
};
|
||||||
|
|
||||||
|
enum class MipFilter : u32 {
|
||||||
|
None = 1,
|
||||||
|
Nearest = 2,
|
||||||
|
Linear = 3,
|
||||||
|
};
|
||||||
|
|
||||||
|
enum class SamplerReduction : u32 {
|
||||||
|
WeightedAverage = 0,
|
||||||
|
Min = 1,
|
||||||
|
Max = 2,
|
||||||
|
};
|
||||||
|
|
||||||
|
// 0x00
|
||||||
|
AddressMode addressModeU : 3;
|
||||||
|
AddressMode addressModeV : 3;
|
||||||
|
AddressMode addressModeP : 3;
|
||||||
|
u32 depthCompareEnable : 1;
|
||||||
|
CompareOp depthCompareOp : 3;
|
||||||
|
u32 srgbConversion : 1;
|
||||||
|
u32 fontFilterWidth : 3;
|
||||||
|
u32 fontFilterHeight : 3;
|
||||||
|
u32 maxAnisotropy : 3;
|
||||||
|
u32 _pad0_ : 9;
|
||||||
|
|
||||||
|
// 0x04
|
||||||
|
Filter magFilter : 2;
|
||||||
|
u32 _pad1_ : 2;
|
||||||
|
Filter minFilter : 2;
|
||||||
|
MipFilter mipFilter : 2;
|
||||||
|
u32 cubemapAnisotropy : 1;
|
||||||
|
u32 cubemapInterfaceFiltering : 1;
|
||||||
|
SamplerReduction reductionFilter : 2;
|
||||||
|
i32 mipLodBias : 13;
|
||||||
|
u32 floatCoordNormalization : 1;
|
||||||
|
u32 trilinearOptimization : 5;
|
||||||
|
u32 _pad2_ : 1;
|
||||||
|
|
||||||
|
// 0x08
|
||||||
|
u32 minLodClamp : 12;
|
||||||
|
u32 maxLodClamp : 12;
|
||||||
|
u32 srgbBorderColorR : 8;
|
||||||
|
|
||||||
|
// 0x0C
|
||||||
|
u32 _pad3_ : 12;
|
||||||
|
u32 srgbBorderColorG : 8;
|
||||||
|
u32 srgbBorderColorB : 8;
|
||||||
|
u32 _pad4_ : 4;
|
||||||
|
|
||||||
|
// 0x10
|
||||||
|
float borderColorR;
|
||||||
|
|
||||||
|
// 0x14
|
||||||
|
float borderColorG;
|
||||||
|
|
||||||
|
// 0x18
|
||||||
|
float borderColorB;
|
||||||
|
|
||||||
|
// 0x1C
|
||||||
|
float borderColorA;
|
||||||
|
|
||||||
|
private:
|
||||||
|
/**
|
||||||
|
* @brief Convert a fixed point integer to a floating point integer
|
||||||
|
*/
|
||||||
|
template<typename T, size_t FractionalBits = 8>
|
||||||
|
float ConvertFixedToFloat(T fixed) {
|
||||||
|
return static_cast<float>(fixed) / static_cast<float>(1 << FractionalBits);
|
||||||
|
};
|
||||||
|
|
||||||
|
public:
|
||||||
|
bool operator==(const TextureSamplerControl&) const = default;
|
||||||
|
|
||||||
|
float MaxAnisotropy() {
|
||||||
|
constexpr size_t AnisotropyCount{8}; //!< The amount of unique anisotropy values that can be represented (2^3 — 3-bit value)
|
||||||
|
constexpr std::array<float, AnisotropyCount> anisotropyLut{
|
||||||
|
1.0f, 3.14f, 5.28f, 7.42f, 9.57f, 11.71f, 13.85f, 16.0f
|
||||||
|
}; //!< A linear mapping of value range (0..7) to anisotropy range (1..16) calculated using `(index * 15 / 7) + 1`
|
||||||
|
return anisotropyLut[maxAnisotropy];
|
||||||
|
}
|
||||||
|
|
||||||
|
float MipLodBias() {
|
||||||
|
return ConvertFixedToFloat(mipLodBias);
|
||||||
|
}
|
||||||
|
|
||||||
|
float MinLodClamp() {
|
||||||
|
return ConvertFixedToFloat(minLodClamp);
|
||||||
|
}
|
||||||
|
|
||||||
|
float MaxLodClamp() {
|
||||||
|
return ConvertFixedToFloat(maxLodClamp);
|
||||||
|
}
|
||||||
|
};
|
||||||
|
static_assert(sizeof(TextureSamplerControl) == 0x20);
|
||||||
|
|
||||||
|
#pragma pack(pop)
|
||||||
|
}
|
Loading…
Reference in New Issue
Block a user