video_core: Rewrite the texture cache
The current texture cache has several points that hurt maintainability and performance. It's easy to break unrelated parts of the cache when doing minor changes. The cache can easily forget valuable information about the cached textures by CPU writes or simply by its normal usage.The current texture cache has several points that hurt maintainability and performance. It's easy to break unrelated parts of the cache when doing minor changes. The cache can easily forget valuable information about the cached textures by CPU writes or simply by its normal usage. This commit aims to address those issues.
This commit is contained in:
parent
9106ac1e6b
commit
9764c13d6d
152 changed files with 10609 additions and 8351 deletions
|
@ -137,10 +137,9 @@ void AsyncShaders::QueueVulkanShader(Vulkan::VKPipelineCache* pp_cache,
|
|||
const Vulkan::VKDevice& device, Vulkan::VKScheduler& scheduler,
|
||||
Vulkan::VKDescriptorPool& descriptor_pool,
|
||||
Vulkan::VKUpdateDescriptorQueue& update_descriptor_queue,
|
||||
Vulkan::VKRenderPassCache& renderpass_cache,
|
||||
std::vector<VkDescriptorSetLayoutBinding> bindings,
|
||||
Vulkan::SPIRVProgram program,
|
||||
Vulkan::GraphicsPipelineCacheKey key) {
|
||||
Vulkan::GraphicsPipelineCacheKey key, u32 num_color_buffers) {
|
||||
std::unique_lock lock(queue_mutex);
|
||||
pending_queue.push({
|
||||
.backend = Backend::Vulkan,
|
||||
|
@ -149,10 +148,10 @@ void AsyncShaders::QueueVulkanShader(Vulkan::VKPipelineCache* pp_cache,
|
|||
.scheduler = &scheduler,
|
||||
.descriptor_pool = &descriptor_pool,
|
||||
.update_descriptor_queue = &update_descriptor_queue,
|
||||
.renderpass_cache = &renderpass_cache,
|
||||
.bindings = std::move(bindings),
|
||||
.program = std::move(program),
|
||||
.key = key,
|
||||
.num_color_buffers = num_color_buffers,
|
||||
});
|
||||
cv.notify_one();
|
||||
}
|
||||
|
@ -205,8 +204,8 @@ void AsyncShaders::ShaderCompilerThread(Core::Frontend::GraphicsContext* context
|
|||
} else if (work.backend == Backend::Vulkan) {
|
||||
auto pipeline = std::make_unique<Vulkan::VKGraphicsPipeline>(
|
||||
*work.vk_device, *work.scheduler, *work.descriptor_pool,
|
||||
*work.update_descriptor_queue, *work.renderpass_cache, work.key, work.bindings,
|
||||
work.program);
|
||||
*work.update_descriptor_queue, work.key, work.bindings, work.program,
|
||||
work.num_color_buffers);
|
||||
|
||||
work.pp_cache->EmplacePipeline(std::move(pipeline));
|
||||
}
|
||||
|
|
|
@ -98,9 +98,9 @@ public:
|
|||
Vulkan::VKScheduler& scheduler,
|
||||
Vulkan::VKDescriptorPool& descriptor_pool,
|
||||
Vulkan::VKUpdateDescriptorQueue& update_descriptor_queue,
|
||||
Vulkan::VKRenderPassCache& renderpass_cache,
|
||||
std::vector<VkDescriptorSetLayoutBinding> bindings,
|
||||
Vulkan::SPIRVProgram program, Vulkan::GraphicsPipelineCacheKey key);
|
||||
Vulkan::SPIRVProgram program, Vulkan::GraphicsPipelineCacheKey key,
|
||||
u32 num_color_buffers);
|
||||
|
||||
private:
|
||||
void ShaderCompilerThread(Core::Frontend::GraphicsContext* context);
|
||||
|
@ -127,10 +127,10 @@ private:
|
|||
Vulkan::VKScheduler* scheduler;
|
||||
Vulkan::VKDescriptorPool* descriptor_pool;
|
||||
Vulkan::VKUpdateDescriptorQueue* update_descriptor_queue;
|
||||
Vulkan::VKRenderPassCache* renderpass_cache;
|
||||
std::vector<VkDescriptorSetLayoutBinding> bindings;
|
||||
Vulkan::SPIRVProgram program;
|
||||
Vulkan::GraphicsPipelineCacheKey key;
|
||||
u32 num_color_buffers;
|
||||
};
|
||||
|
||||
std::condition_variable cv;
|
||||
|
|
|
@ -25,7 +25,7 @@ using Tegra::Shader::OpCode;
|
|||
namespace {
|
||||
|
||||
void DeduceTextureHandlerSize(VideoCore::GuestDriverProfile& gpu_driver,
|
||||
const std::list<Sampler>& used_samplers) {
|
||||
const std::list<SamplerEntry>& used_samplers) {
|
||||
if (gpu_driver.IsTextureHandlerSizeKnown() || used_samplers.size() <= 1) {
|
||||
return;
|
||||
}
|
||||
|
@ -43,9 +43,9 @@ void DeduceTextureHandlerSize(VideoCore::GuestDriverProfile& gpu_driver,
|
|||
}
|
||||
}
|
||||
|
||||
std::optional<u32> TryDeduceSamplerSize(const Sampler& sampler_to_deduce,
|
||||
std::optional<u32> TryDeduceSamplerSize(const SamplerEntry& sampler_to_deduce,
|
||||
VideoCore::GuestDriverProfile& gpu_driver,
|
||||
const std::list<Sampler>& used_samplers) {
|
||||
const std::list<SamplerEntry>& used_samplers) {
|
||||
const u32 base_offset = sampler_to_deduce.offset;
|
||||
u32 max_offset{std::numeric_limits<u32>::max()};
|
||||
for (const auto& sampler : used_samplers) {
|
||||
|
|
|
@ -497,11 +497,12 @@ u32 ShaderIR::DecodeImage(NodeBlock& bb, u32 pc) {
|
|||
return pc;
|
||||
}
|
||||
|
||||
Image& ShaderIR::GetImage(Tegra::Shader::Image image, Tegra::Shader::ImageType type) {
|
||||
ImageEntry& ShaderIR::GetImage(Tegra::Shader::Image image, Tegra::Shader::ImageType type) {
|
||||
const auto offset = static_cast<u32>(image.index.Value());
|
||||
|
||||
const auto it = std::find_if(std::begin(used_images), std::end(used_images),
|
||||
[offset](const Image& entry) { return entry.offset == offset; });
|
||||
const auto it =
|
||||
std::find_if(std::begin(used_images), std::end(used_images),
|
||||
[offset](const ImageEntry& entry) { return entry.offset == offset; });
|
||||
if (it != std::end(used_images)) {
|
||||
ASSERT(!it->is_bindless && it->type == type);
|
||||
return *it;
|
||||
|
@ -511,7 +512,7 @@ Image& ShaderIR::GetImage(Tegra::Shader::Image image, Tegra::Shader::ImageType t
|
|||
return used_images.emplace_back(next_index, offset, type);
|
||||
}
|
||||
|
||||
Image& ShaderIR::GetBindlessImage(Tegra::Shader::Register reg, Tegra::Shader::ImageType type) {
|
||||
ImageEntry& ShaderIR::GetBindlessImage(Tegra::Shader::Register reg, Tegra::Shader::ImageType type) {
|
||||
const Node image_register = GetRegister(reg);
|
||||
const auto result =
|
||||
TrackCbuf(image_register, global_code, static_cast<s64>(global_code.size()));
|
||||
|
@ -520,7 +521,7 @@ Image& ShaderIR::GetBindlessImage(Tegra::Shader::Register reg, Tegra::Shader::Im
|
|||
const auto offset = std::get<2>(result);
|
||||
|
||||
const auto it = std::find_if(std::begin(used_images), std::end(used_images),
|
||||
[buffer, offset](const Image& entry) {
|
||||
[buffer, offset](const ImageEntry& entry) {
|
||||
return entry.buffer == buffer && entry.offset == offset;
|
||||
});
|
||||
if (it != std::end(used_images)) {
|
||||
|
|
|
@ -141,7 +141,7 @@ u32 ShaderIR::DecodeTexture(NodeBlock& bb, u32 pc) {
|
|||
|
||||
SamplerInfo info;
|
||||
info.is_shadow = is_depth_compare;
|
||||
const std::optional<Sampler> sampler = GetSampler(instr.sampler, info);
|
||||
const std::optional<SamplerEntry> sampler = GetSampler(instr.sampler, info);
|
||||
|
||||
Node4 values;
|
||||
for (u32 element = 0; element < values.size(); ++element) {
|
||||
|
@ -173,9 +173,9 @@ u32 ShaderIR::DecodeTexture(NodeBlock& bb, u32 pc) {
|
|||
SamplerInfo info;
|
||||
info.type = texture_type;
|
||||
info.is_array = is_array;
|
||||
const std::optional<Sampler> sampler = is_bindless
|
||||
? GetBindlessSampler(base_reg, info, index_var)
|
||||
: GetSampler(instr.sampler, info);
|
||||
const std::optional<SamplerEntry> sampler =
|
||||
is_bindless ? GetBindlessSampler(base_reg, info, index_var)
|
||||
: GetSampler(instr.sampler, info);
|
||||
Node4 values;
|
||||
if (!sampler) {
|
||||
std::generate(values.begin(), values.end(), [this] { return Immediate(0); });
|
||||
|
@ -217,9 +217,9 @@ u32 ShaderIR::DecodeTexture(NodeBlock& bb, u32 pc) {
|
|||
[[fallthrough]];
|
||||
case OpCode::Id::TXQ: {
|
||||
Node index_var;
|
||||
const std::optional<Sampler> sampler = is_bindless
|
||||
? GetBindlessSampler(instr.gpr8, {}, index_var)
|
||||
: GetSampler(instr.sampler, {});
|
||||
const std::optional<SamplerEntry> sampler =
|
||||
is_bindless ? GetBindlessSampler(instr.gpr8, {}, index_var)
|
||||
: GetSampler(instr.sampler, {});
|
||||
|
||||
if (!sampler) {
|
||||
u32 indexer = 0;
|
||||
|
@ -272,7 +272,7 @@ u32 ShaderIR::DecodeTexture(NodeBlock& bb, u32 pc) {
|
|||
info.type = texture_type;
|
||||
info.is_array = is_array;
|
||||
Node index_var;
|
||||
const std::optional<Sampler> sampler =
|
||||
const std::optional<SamplerEntry> sampler =
|
||||
is_bindless ? GetBindlessSampler(instr.gpr20, info, index_var)
|
||||
: GetSampler(instr.sampler, info);
|
||||
|
||||
|
@ -379,14 +379,15 @@ ShaderIR::SamplerInfo ShaderIR::GetSamplerInfo(
|
|||
return info;
|
||||
}
|
||||
|
||||
std::optional<Sampler> ShaderIR::GetSampler(Tegra::Shader::Sampler sampler,
|
||||
SamplerInfo sampler_info) {
|
||||
std::optional<SamplerEntry> ShaderIR::GetSampler(Tegra::Shader::Sampler sampler,
|
||||
SamplerInfo sampler_info) {
|
||||
const u32 offset = static_cast<u32>(sampler.index.Value());
|
||||
const auto info = GetSamplerInfo(sampler_info, registry.ObtainBoundSampler(offset));
|
||||
|
||||
// If this sampler has already been used, return the existing mapping.
|
||||
const auto it = std::find_if(used_samplers.begin(), used_samplers.end(),
|
||||
[offset](const Sampler& entry) { return entry.offset == offset; });
|
||||
const auto it =
|
||||
std::find_if(used_samplers.begin(), used_samplers.end(),
|
||||
[offset](const SamplerEntry& entry) { return entry.offset == offset; });
|
||||
if (it != used_samplers.end()) {
|
||||
ASSERT(!it->is_bindless && it->type == info.type && it->is_array == info.is_array &&
|
||||
it->is_shadow == info.is_shadow && it->is_buffer == info.is_buffer);
|
||||
|
@ -399,8 +400,8 @@ std::optional<Sampler> ShaderIR::GetSampler(Tegra::Shader::Sampler sampler,
|
|||
*info.is_shadow, *info.is_buffer, false);
|
||||
}
|
||||
|
||||
std::optional<Sampler> ShaderIR::GetBindlessSampler(Tegra::Shader::Register reg, SamplerInfo info,
|
||||
Node& index_var) {
|
||||
std::optional<SamplerEntry> ShaderIR::GetBindlessSampler(Tegra::Shader::Register reg,
|
||||
SamplerInfo info, Node& index_var) {
|
||||
const Node sampler_register = GetRegister(reg);
|
||||
const auto [base_node, tracked_sampler_info] =
|
||||
TrackBindlessSampler(sampler_register, global_code, static_cast<s64>(global_code.size()));
|
||||
|
@ -416,7 +417,7 @@ std::optional<Sampler> ShaderIR::GetBindlessSampler(Tegra::Shader::Register reg,
|
|||
|
||||
// If this sampler has already been used, return the existing mapping.
|
||||
const auto it = std::find_if(used_samplers.begin(), used_samplers.end(),
|
||||
[buffer, offset](const Sampler& entry) {
|
||||
[buffer, offset](const SamplerEntry& entry) {
|
||||
return entry.buffer == buffer && entry.offset == offset;
|
||||
});
|
||||
if (it != used_samplers.end()) {
|
||||
|
@ -436,11 +437,12 @@ std::optional<Sampler> ShaderIR::GetBindlessSampler(Tegra::Shader::Register reg,
|
|||
info = GetSamplerInfo(info, registry.ObtainSeparateSampler(indices, offsets));
|
||||
|
||||
// Try to use an already created sampler if it exists
|
||||
const auto it = std::find_if(
|
||||
used_samplers.begin(), used_samplers.end(), [indices, offsets](const Sampler& entry) {
|
||||
return offsets == std::pair{entry.offset, entry.secondary_offset} &&
|
||||
indices == std::pair{entry.buffer, entry.secondary_buffer};
|
||||
});
|
||||
const auto it =
|
||||
std::find_if(used_samplers.begin(), used_samplers.end(),
|
||||
[indices, offsets](const SamplerEntry& entry) {
|
||||
return offsets == std::pair{entry.offset, entry.secondary_offset} &&
|
||||
indices == std::pair{entry.buffer, entry.secondary_buffer};
|
||||
});
|
||||
if (it != used_samplers.end()) {
|
||||
ASSERT(it->is_separated && it->type == info.type && it->is_array == info.is_array &&
|
||||
it->is_shadow == info.is_shadow && it->is_buffer == info.is_buffer);
|
||||
|
@ -460,7 +462,7 @@ std::optional<Sampler> ShaderIR::GetBindlessSampler(Tegra::Shader::Register reg,
|
|||
// If this sampler has already been used, return the existing mapping.
|
||||
const auto it = std::find_if(
|
||||
used_samplers.begin(), used_samplers.end(),
|
||||
[base_offset](const Sampler& entry) { return entry.offset == base_offset; });
|
||||
[base_offset](const SamplerEntry& entry) { return entry.offset == base_offset; });
|
||||
if (it != used_samplers.end()) {
|
||||
ASSERT(!it->is_bindless && it->type == info.type && it->is_array == info.is_array &&
|
||||
it->is_shadow == info.is_shadow && it->is_buffer == info.is_buffer &&
|
||||
|
@ -565,9 +567,9 @@ Node4 ShaderIR::GetTextureCode(Instruction instr, TextureType texture_type,
|
|||
info.is_buffer = false;
|
||||
|
||||
Node index_var;
|
||||
const std::optional<Sampler> sampler = is_bindless
|
||||
? GetBindlessSampler(*bindless_reg, info, index_var)
|
||||
: GetSampler(instr.sampler, info);
|
||||
const std::optional<SamplerEntry> sampler =
|
||||
is_bindless ? GetBindlessSampler(*bindless_reg, info, index_var)
|
||||
: GetSampler(instr.sampler, info);
|
||||
if (!sampler) {
|
||||
return {Immediate(0), Immediate(0), Immediate(0), Immediate(0)};
|
||||
}
|
||||
|
@ -724,7 +726,7 @@ Node4 ShaderIR::GetTld4Code(Instruction instr, TextureType texture_type, bool de
|
|||
info.is_shadow = depth_compare;
|
||||
|
||||
Node index_var;
|
||||
const std::optional<Sampler> sampler =
|
||||
const std::optional<SamplerEntry> sampler =
|
||||
is_bindless ? GetBindlessSampler(parameter_register++, info, index_var)
|
||||
: GetSampler(instr.sampler, info);
|
||||
Node4 values;
|
||||
|
@ -783,7 +785,7 @@ Node4 ShaderIR::GetTldCode(Tegra::Shader::Instruction instr) {
|
|||
// const Node aoffi_register{is_aoffi ? GetRegister(gpr20_cursor++) : nullptr};
|
||||
// const Node multisample{is_multisample ? GetRegister(gpr20_cursor++) : nullptr};
|
||||
|
||||
const std::optional<Sampler> sampler = GetSampler(instr.sampler, {});
|
||||
const std::optional<SamplerEntry> sampler = GetSampler(instr.sampler, {});
|
||||
|
||||
Node4 values;
|
||||
for (u32 element = 0; element < values.size(); ++element) {
|
||||
|
@ -800,7 +802,7 @@ Node4 ShaderIR::GetTldsCode(Instruction instr, TextureType texture_type, bool is
|
|||
info.type = texture_type;
|
||||
info.is_array = is_array;
|
||||
info.is_shadow = false;
|
||||
const std::optional<Sampler> sampler = GetSampler(instr.sampler, info);
|
||||
const std::optional<SamplerEntry> sampler = GetSampler(instr.sampler, info);
|
||||
|
||||
const std::size_t type_coord_count = GetCoordCount(texture_type);
|
||||
const bool lod_enabled = instr.tlds.GetTextureProcessMode() == TextureProcessMode::LL;
|
||||
|
|
|
@ -282,25 +282,24 @@ struct SeparateSamplerNode;
|
|||
using TrackSamplerData = std::variant<BindlessSamplerNode, SeparateSamplerNode, ArraySamplerNode>;
|
||||
using TrackSampler = std::shared_ptr<TrackSamplerData>;
|
||||
|
||||
struct Sampler {
|
||||
struct SamplerEntry {
|
||||
/// Bound samplers constructor
|
||||
constexpr explicit Sampler(u32 index_, u32 offset_, Tegra::Shader::TextureType type_,
|
||||
bool is_array_, bool is_shadow_, bool is_buffer_, bool is_indexed_)
|
||||
explicit SamplerEntry(u32 index_, u32 offset_, Tegra::Shader::TextureType type_, bool is_array_,
|
||||
bool is_shadow_, bool is_buffer_, bool is_indexed_)
|
||||
: index{index_}, offset{offset_}, type{type_}, is_array{is_array_}, is_shadow{is_shadow_},
|
||||
is_buffer{is_buffer_}, is_indexed{is_indexed_} {}
|
||||
|
||||
/// Separate sampler constructor
|
||||
constexpr explicit Sampler(u32 index_, std::pair<u32, u32> offsets_,
|
||||
std::pair<u32, u32> buffers_, Tegra::Shader::TextureType type_,
|
||||
bool is_array_, bool is_shadow_, bool is_buffer_)
|
||||
: index{index_}, offset{offsets_.first}, secondary_offset{offsets_.second},
|
||||
buffer{buffers_.first}, secondary_buffer{buffers_.second}, type{type_},
|
||||
is_array{is_array_}, is_shadow{is_shadow_}, is_buffer{is_buffer_}, is_separated{true} {}
|
||||
explicit SamplerEntry(u32 index_, std::pair<u32, u32> offsets, std::pair<u32, u32> buffers,
|
||||
Tegra::Shader::TextureType type_, bool is_array_, bool is_shadow_,
|
||||
bool is_buffer_)
|
||||
: index{index_}, offset{offsets.first}, secondary_offset{offsets.second},
|
||||
buffer{buffers.first}, secondary_buffer{buffers.second}, type{type_}, is_array{is_array_},
|
||||
is_shadow{is_shadow_}, is_buffer{is_buffer_}, is_separated{true} {}
|
||||
|
||||
/// Bindless samplers constructor
|
||||
constexpr explicit Sampler(u32 index_, u32 offset_, u32 buffer_,
|
||||
Tegra::Shader::TextureType type_, bool is_array_, bool is_shadow_,
|
||||
bool is_buffer_, bool is_indexed_)
|
||||
explicit SamplerEntry(u32 index_, u32 offset_, u32 buffer_, Tegra::Shader::TextureType type_,
|
||||
bool is_array_, bool is_shadow_, bool is_buffer_, bool is_indexed_)
|
||||
: index{index_}, offset{offset_}, buffer{buffer_}, type{type_}, is_array{is_array_},
|
||||
is_shadow{is_shadow_}, is_buffer{is_buffer_}, is_bindless{true}, is_indexed{is_indexed_} {
|
||||
}
|
||||
|
@ -340,14 +339,14 @@ struct BindlessSamplerNode {
|
|||
u32 offset;
|
||||
};
|
||||
|
||||
struct Image {
|
||||
struct ImageEntry {
|
||||
public:
|
||||
/// Bound images constructor
|
||||
constexpr explicit Image(u32 index_, u32 offset_, Tegra::Shader::ImageType type_)
|
||||
explicit ImageEntry(u32 index_, u32 offset_, Tegra::Shader::ImageType type_)
|
||||
: index{index_}, offset{offset_}, type{type_} {}
|
||||
|
||||
/// Bindless samplers constructor
|
||||
constexpr explicit Image(u32 index_, u32 offset_, u32 buffer_, Tegra::Shader::ImageType type_)
|
||||
explicit ImageEntry(u32 index_, u32 offset_, u32 buffer_, Tegra::Shader::ImageType type_)
|
||||
: index{index_}, offset{offset_}, buffer{buffer_}, type{type_}, is_bindless{true} {}
|
||||
|
||||
void MarkWrite() {
|
||||
|
@ -391,7 +390,7 @@ struct MetaArithmetic {
|
|||
|
||||
/// Parameters describing a texture sampler
|
||||
struct MetaTexture {
|
||||
Sampler sampler;
|
||||
SamplerEntry sampler;
|
||||
Node array;
|
||||
Node depth_compare;
|
||||
std::vector<Node> aoffi;
|
||||
|
@ -405,7 +404,7 @@ struct MetaTexture {
|
|||
};
|
||||
|
||||
struct MetaImage {
|
||||
const Image& image;
|
||||
const ImageEntry& image;
|
||||
std::vector<Node> values;
|
||||
u32 element{};
|
||||
};
|
||||
|
|
|
@ -94,11 +94,11 @@ public:
|
|||
return used_cbufs;
|
||||
}
|
||||
|
||||
const std::list<Sampler>& GetSamplers() const {
|
||||
const std::list<SamplerEntry>& GetSamplers() const {
|
||||
return used_samplers;
|
||||
}
|
||||
|
||||
const std::list<Image>& GetImages() const {
|
||||
const std::list<ImageEntry>& GetImages() const {
|
||||
return used_images;
|
||||
}
|
||||
|
||||
|
@ -334,17 +334,17 @@ private:
|
|||
std::optional<Tegra::Engines::SamplerDescriptor> sampler);
|
||||
|
||||
/// Accesses a texture sampler.
|
||||
std::optional<Sampler> GetSampler(Tegra::Shader::Sampler sampler, SamplerInfo info);
|
||||
std::optional<SamplerEntry> GetSampler(Tegra::Shader::Sampler sampler, SamplerInfo info);
|
||||
|
||||
/// Accesses a texture sampler for a bindless texture.
|
||||
std::optional<Sampler> GetBindlessSampler(Tegra::Shader::Register reg, SamplerInfo info,
|
||||
Node& index_var);
|
||||
std::optional<SamplerEntry> GetBindlessSampler(Tegra::Shader::Register reg, SamplerInfo info,
|
||||
Node& index_var);
|
||||
|
||||
/// Accesses an image.
|
||||
Image& GetImage(Tegra::Shader::Image image, Tegra::Shader::ImageType type);
|
||||
ImageEntry& GetImage(Tegra::Shader::Image image, Tegra::Shader::ImageType type);
|
||||
|
||||
/// Access a bindless image sampler.
|
||||
Image& GetBindlessImage(Tegra::Shader::Register reg, Tegra::Shader::ImageType type);
|
||||
ImageEntry& GetBindlessImage(Tegra::Shader::Register reg, Tegra::Shader::ImageType type);
|
||||
|
||||
/// Extracts a sequence of bits from a node
|
||||
Node BitfieldExtract(Node value, u32 offset, u32 bits);
|
||||
|
@ -454,8 +454,8 @@ private:
|
|||
std::set<Tegra::Shader::Attribute::Index> used_input_attributes;
|
||||
std::set<Tegra::Shader::Attribute::Index> used_output_attributes;
|
||||
std::map<u32, ConstBuffer> used_cbufs;
|
||||
std::list<Sampler> used_samplers;
|
||||
std::list<Image> used_images;
|
||||
std::list<SamplerEntry> used_samplers;
|
||||
std::list<ImageEntry> used_images;
|
||||
std::array<bool, Tegra::Engines::Maxwell3D::Regs::NumClipDistances> used_clip_distances{};
|
||||
std::map<GlobalMemoryBase, GlobalMemoryUsage> used_global_memory;
|
||||
bool uses_layer{};
|
||||
|
|
Loading…
Add table
Add a link
Reference in a new issue