shader/node: Unpack bindless texture encoding

Bindless textures were using u64 to pack the buffer and offset from
where they come from. Drop this in favor of separated entries in the
struct.

Remove the usage of std::set in favor of std::list (it's not std::vector
to avoid reference invalidations) for samplers and images.
This commit is contained in:
ReinUsesLisp 2019-10-28 02:31:05 -03:00
parent 2ec5b55ee3
commit a993df1ee2
No known key found for this signature in database
GPG key ID: 2DFC508897B39CFE
8 changed files with 118 additions and 144 deletions

View file

@ -973,10 +973,9 @@ TextureBufferUsage RasterizerOpenGL::SetupDrawTextures(Maxwell::ShaderStage stag
if (!entry.IsBindless()) { if (!entry.IsBindless()) {
return maxwell3d.GetStageTexture(stage, entry.GetOffset()); return maxwell3d.GetStageTexture(stage, entry.GetOffset());
} }
const auto cbuf = entry.GetBindlessCBuf(); const auto shader_type = static_cast<Tegra::Engines::ShaderType>(stage);
Tegra::Texture::TextureHandle tex_handle; const Tegra::Texture::TextureHandle tex_handle =
Tegra::Engines::ShaderType shader_type = static_cast<Tegra::Engines::ShaderType>(stage); maxwell3d.AccessConstBuffer32(shader_type, entry.GetBuffer(), entry.GetOffset());
tex_handle.raw = maxwell3d.AccessConstBuffer32(shader_type, cbuf.first, cbuf.second);
return maxwell3d.GetTextureInfo(tex_handle); return maxwell3d.GetTextureInfo(tex_handle);
}(); }();
@ -1004,10 +1003,8 @@ TextureBufferUsage RasterizerOpenGL::SetupComputeTextures(const Shader& kernel)
if (!entry.IsBindless()) { if (!entry.IsBindless()) {
return compute.GetTexture(entry.GetOffset()); return compute.GetTexture(entry.GetOffset());
} }
const auto cbuf = entry.GetBindlessCBuf(); const Tegra::Texture::TextureHandle tex_handle = compute.AccessConstBuffer32(
Tegra::Texture::TextureHandle tex_handle; Tegra::Engines::ShaderType::Compute, entry.GetBuffer(), entry.GetOffset());
tex_handle.raw = compute.AccessConstBuffer32(Tegra::Engines::ShaderType::Compute,
cbuf.first, cbuf.second);
return compute.GetTextureInfo(tex_handle); return compute.GetTextureInfo(tex_handle);
}(); }();
@ -1050,10 +1047,8 @@ void RasterizerOpenGL::SetupComputeImages(const Shader& shader) {
if (!entry.IsBindless()) { if (!entry.IsBindless()) {
return compute.GetTexture(entry.GetOffset()).tic; return compute.GetTexture(entry.GetOffset()).tic;
} }
const auto cbuf = entry.GetBindlessCBuf(); const Tegra::Texture::TextureHandle tex_handle = compute.AccessConstBuffer32(
Tegra::Texture::TextureHandle tex_handle; Tegra::Engines::ShaderType::Compute, entry.GetBuffer(), entry.GetOffset());
tex_handle.raw = compute.AccessConstBuffer32(Tegra::Engines::ShaderType::Compute,
cbuf.first, cbuf.second);
return compute.GetTextureInfo(tex_handle).tic; return compute.GetTextureInfo(tex_handle).tic;
}(); }();
SetupImage(bindpoint, tic, entry); SetupImage(bindpoint, tic, entry);

View file

@ -735,7 +735,7 @@ private:
void DeclareImages() { void DeclareImages() {
const auto& images{ir.GetImages()}; const auto& images{ir.GetImages()};
for (const auto& [offset, image] : images) { for (const auto& image : images) {
std::string qualifier = "coherent volatile"; std::string qualifier = "coherent volatile";
if (image.IsRead() && !image.IsWritten()) { if (image.IsRead() && !image.IsWritten()) {
qualifier += " readonly"; qualifier += " readonly";
@ -2466,16 +2466,16 @@ ShaderEntries GetEntries(const VideoCommon::Shader::ShaderIR& ir) {
entries.const_buffers.emplace_back(cbuf.second.GetMaxOffset(), cbuf.second.IsIndirect(), entries.const_buffers.emplace_back(cbuf.second.GetMaxOffset(), cbuf.second.IsIndirect(),
cbuf.first); cbuf.first);
} }
for (const auto& sampler : ir.GetSamplers()) {
entries.samplers.emplace_back(sampler);
}
for (const auto& [offset, image] : ir.GetImages()) {
entries.images.emplace_back(image);
}
for (const auto& [base, usage] : ir.GetGlobalMemory()) { for (const auto& [base, usage] : ir.GetGlobalMemory()) {
entries.global_memory_entries.emplace_back(base.cbuf_index, base.cbuf_offset, usage.is_read, entries.global_memory_entries.emplace_back(base.cbuf_index, base.cbuf_offset, usage.is_read,
usage.is_written); usage.is_written);
} }
for (const auto& sampler : ir.GetSamplers()) {
entries.samplers.emplace_back(sampler);
}
for (const auto& image : ir.GetImages()) {
entries.images.emplace_back(image);
}
entries.clip_distances = ir.GetClipDistances(); entries.clip_distances = ir.GetClipDistances();
entries.shader_length = ir.GetLength(); entries.shader_length = ir.GetLength();
return entries; return entries;

View file

@ -82,10 +82,9 @@ private:
struct ShaderEntries { struct ShaderEntries {
std::vector<ConstBufferEntry> const_buffers; std::vector<ConstBufferEntry> const_buffers;
std::vector<SamplerEntry> samplers;
std::vector<SamplerEntry> bindless_samplers;
std::vector<ImageEntry> images;
std::vector<GlobalMemoryEntry> global_memory_entries; std::vector<GlobalMemoryEntry> global_memory_entries;
std::vector<SamplerEntry> samplers;
std::vector<ImageEntry> images;
std::array<bool, Maxwell::NumClipDistances> clip_distances{}; std::array<bool, Maxwell::NumClipDistances> clip_distances{};
std::size_t shader_length{}; std::size_t shader_length{};
}; };

View file

@ -143,39 +143,37 @@ u32 ShaderIR::DecodeImage(NodeBlock& bb, u32 pc) {
} }
Image& ShaderIR::GetImage(Tegra::Shader::Image image, Tegra::Shader::ImageType type) { Image& ShaderIR::GetImage(Tegra::Shader::Image image, Tegra::Shader::ImageType type) {
const auto offset{static_cast<std::size_t>(image.index.Value())}; const auto offset = static_cast<u32>(image.index.Value());
if (const auto existing_image = TryUseExistingImage(offset, type)) {
return *existing_image; const auto it =
std::find_if(std::begin(used_images), std::end(used_images),
[offset](const Image& entry) { return entry.GetOffset() == offset; });
if (it != std::end(used_images)) {
ASSERT(!it->IsBindless() && it->GetType() == it->GetType());
return *it;
} }
const std::size_t next_index{used_images.size()}; const auto next_index = static_cast<u32>(used_images.size());
return used_images.emplace(offset, Image{offset, next_index, type}).first->second; return used_images.emplace_back(next_index, offset, type);
} }
Image& ShaderIR::GetBindlessImage(Tegra::Shader::Register reg, Tegra::Shader::ImageType type) { Image& ShaderIR::GetBindlessImage(Tegra::Shader::Register reg, Tegra::Shader::ImageType type) {
const Node image_register{GetRegister(reg)}; const Node image_register = GetRegister(reg);
const auto [base_image, cbuf_index, cbuf_offset]{ const auto [base_image, buffer, offset] =
TrackCbuf(image_register, global_code, static_cast<s64>(global_code.size()))}; TrackCbuf(image_register, global_code, static_cast<s64>(global_code.size()));
const auto cbuf_key{(static_cast<u64>(cbuf_index) << 32) | static_cast<u64>(cbuf_offset)};
if (const auto image = TryUseExistingImage(cbuf_key, type)) { const auto it =
return *image; std::find_if(std::begin(used_images), std::end(used_images),
[buffer = buffer, offset = offset](const Image& entry) {
return entry.GetBuffer() == buffer && entry.GetOffset() == offset;
});
if (it != std::end(used_images)) {
ASSERT(it->IsBindless() && it->GetType() == it->GetType());
return *it;
} }
const std::size_t next_index{used_images.size()}; const auto next_index = static_cast<u32>(used_images.size());
return used_images.emplace(cbuf_key, Image{cbuf_index, cbuf_offset, next_index, type}) return used_images.emplace_back(next_index, offset, buffer, type);
.first->second;
}
Image* ShaderIR::TryUseExistingImage(u64 offset, Tegra::Shader::ImageType type) {
auto it = used_images.find(offset);
if (it == used_images.end()) {
return nullptr;
}
auto& image = it->second;
ASSERT(image.GetType() == type);
return &image;
} }
} // namespace VideoCommon::Shader } // namespace VideoCommon::Shader

View file

@ -255,7 +255,7 @@ u32 ShaderIR::DecodeTexture(NodeBlock& bb, u32 pc) {
break; break;
} }
case OpCode::Id::TLDS: { case OpCode::Id::TLDS: {
const Tegra::Shader::TextureType texture_type{instr.tlds.GetTextureType()}; const TextureType texture_type{instr.tlds.GetTextureType()};
const bool is_array{instr.tlds.IsArrayTexture()}; const bool is_array{instr.tlds.IsArrayTexture()};
UNIMPLEMENTED_IF_MSG(instr.tlds.UsesMiscMode(TextureMiscMode::AOFFI), UNIMPLEMENTED_IF_MSG(instr.tlds.UsesMiscMode(TextureMiscMode::AOFFI),
@ -286,77 +286,80 @@ const Sampler& ShaderIR::GetSampler(const Tegra::Shader::Sampler& sampler,
std::optional<SamplerInfo> sampler_info) { std::optional<SamplerInfo> sampler_info) {
const auto offset = static_cast<u32>(sampler.index.Value()); const auto offset = static_cast<u32>(sampler.index.Value());
Tegra::Shader::TextureType type; TextureType type;
bool is_array; bool is_array;
bool is_shadow; bool is_shadow;
if (sampler_info) { if (sampler_info) {
type = sampler_info->type; type = sampler_info->type;
is_array = sampler_info->is_array; is_array = sampler_info->is_array;
is_shadow = sampler_info->is_shadow; is_shadow = sampler_info->is_shadow;
} else if (auto sampler = locker.ObtainBoundSampler(offset); sampler) { } else if (const auto sampler = locker.ObtainBoundSampler(offset)) {
type = sampler->texture_type.Value(); type = sampler->texture_type.Value();
is_array = sampler->is_array.Value() != 0; is_array = sampler->is_array.Value() != 0;
is_shadow = sampler->is_shadow.Value() != 0; is_shadow = sampler->is_shadow.Value() != 0;
} else { } else {
type = Tegra::Shader::TextureType::Texture2D; LOG_WARNING(HW_GPU, "Unknown sampler info");
type = TextureType::Texture2D;
is_array = false; is_array = false;
is_shadow = false; is_shadow = false;
} }
// If this sampler has already been used, return the existing mapping. // If this sampler has already been used, return the existing mapping.
const auto itr = const auto it =
std::find_if(used_samplers.begin(), used_samplers.end(), std::find_if(used_samplers.begin(), used_samplers.end(),
[&](const Sampler& entry) { return entry.GetOffset() == offset; }); [offset](const Sampler& entry) { return entry.GetOffset() == offset; });
if (itr != used_samplers.end()) { if (it != used_samplers.end()) {
ASSERT(itr->GetType() == type && itr->IsArray() == is_array && ASSERT(!it->IsBindless() && it->GetType() == type && it->IsArray() == is_array &&
itr->IsShadow() == is_shadow); it->IsShadow() == is_shadow);
return *itr; return *it;
} }
// Otherwise create a new mapping for this sampler // Otherwise create a new mapping for this sampler
const std::size_t next_index = used_samplers.size(); const auto next_index = static_cast<u32>(used_samplers.size());
const Sampler entry{offset, next_index, type, is_array, is_shadow}; return used_samplers.emplace_back(Sampler(next_index, offset, type, is_array, is_shadow));
return *used_samplers.emplace(entry).first; }
} // namespace VideoCommon::Shader
const Sampler& ShaderIR::GetBindlessSampler(const Tegra::Shader::Register& reg, const Sampler& ShaderIR::GetBindlessSampler(const Tegra::Shader::Register& reg,
std::optional<SamplerInfo> sampler_info) { std::optional<SamplerInfo> sampler_info) {
const Node sampler_register = GetRegister(reg); const Node sampler_register = GetRegister(reg);
const auto [base_sampler, cbuf_index, cbuf_offset] = const auto [base_sampler, buffer, offset] =
TrackCbuf(sampler_register, global_code, static_cast<s64>(global_code.size())); TrackCbuf(sampler_register, global_code, static_cast<s64>(global_code.size()));
ASSERT(base_sampler != nullptr); ASSERT(base_sampler != nullptr);
const auto cbuf_key = (static_cast<u64>(cbuf_index) << 32) | static_cast<u64>(cbuf_offset);
Tegra::Shader::TextureType type; TextureType type;
bool is_array; bool is_array;
bool is_shadow; bool is_shadow;
if (sampler_info) { if (sampler_info) {
type = sampler_info->type; type = sampler_info->type;
is_array = sampler_info->is_array; is_array = sampler_info->is_array;
is_shadow = sampler_info->is_shadow; is_shadow = sampler_info->is_shadow;
} else if (auto sampler = locker.ObtainBindlessSampler(cbuf_index, cbuf_offset); sampler) { } else if (const auto sampler = locker.ObtainBindlessSampler(buffer, offset)) {
type = sampler->texture_type.Value(); type = sampler->texture_type.Value();
is_array = sampler->is_array.Value() != 0; is_array = sampler->is_array.Value() != 0;
is_shadow = sampler->is_shadow.Value() != 0; is_shadow = sampler->is_shadow.Value() != 0;
} else { } else {
type = Tegra::Shader::TextureType::Texture2D; LOG_WARNING(HW_GPU, "Unknown sampler info");
type = TextureType::Texture2D;
is_array = false; is_array = false;
is_shadow = false; is_shadow = false;
} }
// If this sampler has already been used, return the existing mapping. // If this sampler has already been used, return the existing mapping.
const auto itr = const auto it =
std::find_if(used_samplers.begin(), used_samplers.end(), std::find_if(used_samplers.begin(), used_samplers.end(),
[&](const Sampler& entry) { return entry.GetOffset() == cbuf_key; }); [buffer = buffer, offset = offset](const Sampler& entry) {
if (itr != used_samplers.end()) { return entry.GetBuffer() == buffer && entry.GetOffset() == offset;
ASSERT(itr->GetType() == type && itr->IsArray() == is_array && });
itr->IsShadow() == is_shadow); if (it != used_samplers.end()) {
return *itr; ASSERT(it->IsBindless() && it->GetType() == type && it->IsArray() == is_array &&
it->IsShadow() == is_shadow);
return *it;
} }
// Otherwise create a new mapping for this sampler // Otherwise create a new mapping for this sampler
const std::size_t next_index = used_samplers.size(); const auto next_index = static_cast<u32>(used_samplers.size());
const Sampler entry{cbuf_index, cbuf_offset, next_index, type, is_array, is_shadow}; return used_samplers.emplace_back(
return *used_samplers.emplace(entry).first; Sampler(next_index, offset, buffer, type, is_array, is_shadow));
} }
void ShaderIR::WriteTexInstructionFloat(NodeBlock& bb, Instruction instr, const Node4& components) { void ShaderIR::WriteTexInstructionFloat(NodeBlock& bb, Instruction instr, const Node4& components) {

View file

@ -230,62 +230,49 @@ using NodeBlock = std::vector<Node>;
class Sampler { class Sampler {
public: public:
/// This constructor is for bound samplers /// This constructor is for bound samplers
explicit Sampler(std::size_t offset, std::size_t index, Tegra::Shader::TextureType type, constexpr explicit Sampler(u32 index, u32 offset, Tegra::Shader::TextureType type,
bool is_array, bool is_shadow) bool is_array, bool is_shadow)
: offset{offset}, index{index}, type{type}, is_array{is_array}, is_shadow{is_shadow}, : index{index}, offset{offset}, type{type}, is_array{is_array}, is_shadow{is_shadow} {}
is_bindless{false} {}
/// This constructor is for bindless samplers /// This constructor is for bindless samplers
explicit Sampler(u32 cbuf_index, u32 cbuf_offset, std::size_t index, constexpr explicit Sampler(u32 index, u32 offset, u32 buffer, Tegra::Shader::TextureType type,
Tegra::Shader::TextureType type, bool is_array, bool is_shadow) bool is_array, bool is_shadow)
: offset{(static_cast<u64>(cbuf_index) << 32) | cbuf_offset}, index{index}, type{type}, : index{index}, offset{offset}, buffer{buffer}, type{type}, is_array{is_array},
is_array{is_array}, is_shadow{is_shadow}, is_bindless{true} {} is_shadow{is_shadow}, is_bindless{true} {}
/// This constructor is for serialization/deserialization constexpr u32 GetIndex() const {
explicit Sampler(std::size_t offset, std::size_t index, Tegra::Shader::TextureType type,
bool is_array, bool is_shadow, bool is_bindless)
: offset{offset}, index{index}, type{type}, is_array{is_array}, is_shadow{is_shadow},
is_bindless{is_bindless} {}
std::size_t GetOffset() const {
return offset;
}
std::size_t GetIndex() const {
return index; return index;
} }
Tegra::Shader::TextureType GetType() const { constexpr u32 GetOffset() const {
return offset;
}
constexpr u32 GetBuffer() const {
return buffer;
}
constexpr Tegra::Shader::TextureType GetType() const {
return type; return type;
} }
bool IsArray() const { constexpr bool IsArray() const {
return is_array; return is_array;
} }
bool IsShadow() const { constexpr bool IsShadow() const {
return is_shadow; return is_shadow;
} }
bool IsBindless() const { constexpr bool IsBindless() const {
return is_bindless; return is_bindless;
} }
std::pair<u32, u32> GetBindlessCBuf() const {
return {static_cast<u32>(offset >> 32), static_cast<u32>(offset)};
}
bool operator<(const Sampler& rhs) const {
return std::tie(index, offset, type, is_array, is_shadow, is_bindless) <
std::tie(rhs.index, rhs.offset, rhs.type, rhs.is_array, rhs.is_shadow,
rhs.is_bindless);
}
private: private:
/// Offset in TSC memory from which to read the sampler object, as specified by the sampling u32 index{}; ///< Emulated index given for the this sampler.
/// instruction. u32 offset{}; ///< Offset in the const buffer from where the sampler is being read.
std::size_t offset{}; u32 buffer{}; ///< Buffer where the bindless sampler is being read (unused on bound samplers).
std::size_t index{}; ///< Value used to index into the generated GLSL sampler array.
Tegra::Shader::TextureType type{}; ///< The type used to sample this texture (Texture2D, etc) Tegra::Shader::TextureType type{}; ///< The type used to sample this texture (Texture2D, etc)
bool is_array{}; ///< Whether the texture is being sampled as an array texture or not. bool is_array{}; ///< Whether the texture is being sampled as an array texture or not.
bool is_shadow{}; ///< Whether the texture is being sampled as a depth texture or not. bool is_shadow{}; ///< Whether the texture is being sampled as a depth texture or not.
@ -294,18 +281,13 @@ private:
class Image final { class Image final {
public: public:
constexpr explicit Image(std::size_t offset, std::size_t index, Tegra::Shader::ImageType type) /// This constructor is for bound images
: offset{offset}, index{index}, type{type}, is_bindless{false} {} constexpr explicit Image(u32 index, u32 offset, Tegra::Shader::ImageType type)
: index{index}, offset{offset}, type{type} {}
constexpr explicit Image(u32 cbuf_index, u32 cbuf_offset, std::size_t index, /// This constructor is for bindless samplers
Tegra::Shader::ImageType type) constexpr explicit Image(u32 index, u32 offset, u32 buffer, Tegra::Shader::ImageType type)
: offset{(static_cast<u64>(cbuf_index) << 32) | cbuf_offset}, index{index}, type{type}, : index{index}, offset{offset}, buffer{buffer}, type{type}, is_bindless{true} {}
is_bindless{true} {}
constexpr explicit Image(std::size_t offset, std::size_t index, Tegra::Shader::ImageType type,
bool is_bindless, bool is_written, bool is_read, bool is_atomic)
: offset{offset}, index{index}, type{type}, is_bindless{is_bindless},
is_written{is_written}, is_read{is_read}, is_atomic{is_atomic} {}
void MarkWrite() { void MarkWrite() {
is_written = true; is_written = true;
@ -321,12 +303,16 @@ public:
is_atomic = true; is_atomic = true;
} }
constexpr std::size_t GetOffset() const { constexpr u32 GetIndex() const {
return index;
}
constexpr u32 GetOffset() const {
return offset; return offset;
} }
constexpr std::size_t GetIndex() const { constexpr u32 GetBuffer() const {
return index; return buffer;
} }
constexpr Tegra::Shader::ImageType GetType() const { constexpr Tegra::Shader::ImageType GetType() const {
@ -349,18 +335,11 @@ public:
return is_atomic; return is_atomic;
} }
constexpr std::pair<u32, u32> GetBindlessCBuf() const {
return {static_cast<u32>(offset >> 32), static_cast<u32>(offset)};
}
constexpr bool operator<(const Image& rhs) const {
return std::tie(offset, index, type, is_bindless) <
std::tie(rhs.offset, rhs.index, rhs.type, rhs.is_bindless);
}
private: private:
u64 offset{}; u32 index{};
std::size_t index{}; u32 offset{};
u32 buffer{};
Tegra::Shader::ImageType type{}; Tegra::Shader::ImageType type{};
bool is_bindless{}; bool is_bindless{};
bool is_written{}; bool is_written{};

View file

@ -5,6 +5,7 @@
#pragma once #pragma once
#include <array> #include <array>
#include <list>
#include <map> #include <map>
#include <optional> #include <optional>
#include <set> #include <set>
@ -95,11 +96,11 @@ public:
return used_cbufs; return used_cbufs;
} }
const std::set<Sampler>& GetSamplers() const { const std::list<Sampler>& GetSamplers() const {
return used_samplers; return used_samplers;
} }
const std::map<u64, Image>& GetImages() const { const std::list<Image>& GetImages() const {
return used_images; return used_images;
} }
@ -316,9 +317,6 @@ private:
/// Access a bindless image sampler. /// Access a bindless image sampler.
Image& GetBindlessImage(Tegra::Shader::Register reg, Tegra::Shader::ImageType type); Image& GetBindlessImage(Tegra::Shader::Register reg, Tegra::Shader::ImageType type);
/// Tries to access an existing image, updating it's state as needed
Image* TryUseExistingImage(u64 offset, Tegra::Shader::ImageType type);
/// Extracts a sequence of bits from a node /// Extracts a sequence of bits from a node
Node BitfieldExtract(Node value, u32 offset, u32 bits); Node BitfieldExtract(Node value, u32 offset, u32 bits);
@ -402,8 +400,8 @@ private:
std::set<Tegra::Shader::Attribute::Index> used_input_attributes; std::set<Tegra::Shader::Attribute::Index> used_input_attributes;
std::set<Tegra::Shader::Attribute::Index> used_output_attributes; std::set<Tegra::Shader::Attribute::Index> used_output_attributes;
std::map<u32, ConstBuffer> used_cbufs; std::map<u32, ConstBuffer> used_cbufs;
std::set<Sampler> used_samplers; std::list<Sampler> used_samplers;
std::map<u64, Image> used_images; std::list<Image> used_images;
std::array<bool, Tegra::Engines::Maxwell3D::Regs::NumClipDistances> used_clip_distances{}; std::array<bool, Tegra::Engines::Maxwell3D::Regs::NumClipDistances> used_clip_distances{};
std::map<GlobalMemoryBase, GlobalMemoryUsage> used_global_memory; std::map<GlobalMemoryBase, GlobalMemoryUsage> used_global_memory;
bool uses_layer{}; bool uses_layer{};

View file

@ -132,6 +132,8 @@ enum class SwizzleSource : u32 {
}; };
union TextureHandle { union TextureHandle {
TextureHandle(u32 raw) : raw{raw} {}
u32 raw; u32 raw;
BitField<0, 20, u32> tic_id; BitField<0, 20, u32> tic_id;
BitField<20, 12, u32> tsc_id; BitField<20, 12, u32> tsc_id;