Implement transform feedback queries and draws.

This commit is contained in:
Kelebek1 2023-03-13 21:47:24 +00:00
parent 3fded314f2
commit 88f0014bc7
20 changed files with 252 additions and 71 deletions

View file

@ -82,6 +82,7 @@ void LogSettings() {
values.use_asynchronous_gpu_emulation.GetValue());
log_setting("Renderer_NvdecEmulation", values.nvdec_emulation.GetValue());
log_setting("Renderer_AccelerateASTC", values.accelerate_astc.GetValue());
log_setting("Renderer_TransformFeedbackQuery", values.transform_feedback_query.GetValue());
log_setting("Renderer_AsyncASTC", values.async_astc.GetValue());
log_setting("Renderer_AstcRecompression", values.astc_recompression.GetValue());
log_setting("Renderer_UseVsync", values.vsync_mode.GetValue());
@ -246,6 +247,7 @@ void RestoreGlobalState(bool is_powered_on) {
values.use_asynchronous_gpu_emulation.SetGlobal(true);
values.nvdec_emulation.SetGlobal(true);
values.accelerate_astc.SetGlobal(true);
values.transform_feedback_query.SetGlobal(true);
values.async_astc.SetGlobal(true);
values.astc_recompression.SetGlobal(true);
values.use_reactive_flushing.SetGlobal(true);

View file

@ -468,6 +468,7 @@ struct Values {
SwitchableSetting<bool> use_asynchronous_gpu_emulation{true, "use_asynchronous_gpu_emulation"};
SwitchableSetting<NvdecEmulation> nvdec_emulation{NvdecEmulation::GPU, "nvdec_emulation"};
SwitchableSetting<bool> accelerate_astc{true, "accelerate_astc"};
SwitchableSetting<bool> transform_feedback_query{false, "transform_feedback_query"};
SwitchableSetting<bool> async_astc{false, "async_astc"};
Setting<VSyncMode, true> vsync_mode{VSyncMode::FIFO, VSyncMode::Immediate,
VSyncMode::FIFORelaxed, "use_vsync"};

View file

@ -255,6 +255,8 @@ void TelemetrySession::AddInitialInfo(Loader::AppLoader& app_loader,
AddField(field_type, "Renderer_NvdecEmulation",
TranslateNvdecEmulation(Settings::values.nvdec_emulation.GetValue()));
AddField(field_type, "Renderer_AccelerateASTC", Settings::values.accelerate_astc.GetValue());
AddField(field_type, "Renderer_TransformFeedbackQuery",
Settings::values.transform_feedback_query.GetValue());
AddField(field_type, "Renderer_UseVsync",
TranslateVSyncMode(Settings::values.vsync_mode.GetValue()));
AddField(field_type, "Renderer_ShaderBackend",

View file

@ -631,6 +631,22 @@ std::optional<u64> Maxwell3D::GetQueryResult() {
rasterizer->Query(regs.report_semaphore.Address(), QueryType::SamplesPassed,
system.GPU().GetTicks());
return std::nullopt;
case Regs::ReportSemaphore::Report::StreamingPrimitivesSucceeded:
if (Settings::values.transform_feedback_query) {
if (regs.report_semaphore.query.sub_report == 0) {
ASSERT(regs.transform_feedback.controls[0].stride != 0);
return rasterizer->GetTransformFeedbackByteCount() /
regs.transform_feedback.controls[0].stride;
}
}
return 0;
case Regs::ReportSemaphore::Report::StreamingByteCount:
if (Settings::values.transform_feedback_query) {
if (regs.report_semaphore.query.sub_report == 0) {
return rasterizer->GetTransformFeedbackByteCount();
}
}
return 0;
default:
LOG_DEBUG(HW_GPU, "Unimplemented query report type {}",
regs.report_semaphore.query.report.Value());

View file

@ -462,6 +462,25 @@ public:
}
};
class HLE_B5F74EDB717278EC final : public HLEMacroImpl {
public:
explicit HLE_B5F74EDB717278EC(Maxwell3D& maxwell3d_) : HLEMacroImpl(maxwell3d_) {}
void Execute(const std::vector<u32>& parameters, [[maybe_unused]] u32 method) override {
maxwell3d.RefreshParameters();
maxwell3d.regs.draw.begin = parameters[0];
maxwell3d.regs.draw_auto_stride = parameters[1];
maxwell3d.regs.draw_auto_byte_count = parameters[2];
if (maxwell3d.ShouldExecute()) {
maxwell3d.draw_manager->DrawArray(
maxwell3d.regs.draw.topology, 0,
maxwell3d.regs.draw_auto_byte_count / maxwell3d.regs.draw_auto_stride, 0, 1);
}
}
};
} // Anonymous namespace
HLEMacro::HLEMacro(Maxwell3D& maxwell3d_) : maxwell3d{maxwell3d_} {
@ -536,6 +555,11 @@ HLEMacro::HLEMacro(Maxwell3D& maxwell3d_) : maxwell3d{maxwell3d_} {
[](Maxwell3D& maxwell3d__) -> std::unique_ptr<CachedMacro> {
return std::make_unique<HLE_TransformFeedbackSetup>(maxwell3d__);
}));
builders.emplace(0xB5F74EDB717278ECULL,
std::function<std::unique_ptr<CachedMacro>(Maxwell3D&)>(
[](Maxwell3D& maxwell3d__) -> std::unique_ptr<CachedMacro> {
return std::make_unique<HLE_B5F74EDB717278EC>(maxwell3d__);
}));
}
HLEMacro::~HLEMacro() = default;

View file

@ -176,5 +176,7 @@ public:
virtual void BindChannel(Tegra::Control::ChannelState& channel) {}
virtual void ReleaseChannel(s32 channel_id) {}
virtual u32 GetTransformFeedbackByteCount() = 0;
};
} // namespace VideoCore

View file

@ -100,5 +100,8 @@ void RasterizerNull::LoadDiskResources(u64 title_id, std::stop_token stop_loadin
void RasterizerNull::InitializeChannel(Tegra::Control::ChannelState& channel) {}
void RasterizerNull::BindChannel(Tegra::Control::ChannelState& channel) {}
void RasterizerNull::ReleaseChannel(s32 channel_id) {}
u32 RasterizerNull::GetTransformFeedbackByteCount() {
return 0;
}
} // namespace Null

View file

@ -84,6 +84,7 @@ public:
void InitializeChannel(Tegra::Control::ChannelState& channel) override;
void BindChannel(Tegra::Control::ChannelState& channel) override;
void ReleaseChannel(s32 channel_id) override;
u32 GetTransformFeedbackByteCount() override;
private:
Tegra::GPU& m_gpu;

View file

@ -67,9 +67,13 @@ RasterizerOpenGL::RasterizerOpenGL(Core::Frontend::EmuWindow& emu_window_, Tegra
state_tracker, gpu.ShaderNotify()),
query_cache(*this, cpu_memory_), accelerate_dma(buffer_cache, texture_cache),
fence_manager(*this, gpu, texture_cache, buffer_cache, query_cache),
blit_image(program_manager_) {}
blit_image(program_manager_) {
glGenQueries(1, &transform_query);
}
RasterizerOpenGL::~RasterizerOpenGL() = default;
RasterizerOpenGL::~RasterizerOpenGL() {
glDeleteQueries(1, &transform_query);
}
void RasterizerOpenGL::SyncVertexFormats() {
auto& flags = maxwell3d->dirty.flags;
@ -233,12 +237,15 @@ void RasterizerOpenGL::PrepareDraw(bool is_indexed, Func&& draw_func) {
const auto& draw_state = maxwell3d->draw_manager->GetDrawState();
const GLenum primitive_mode = MaxwellToGL::PrimitiveTopology(draw_state.topology);
BeginTransformFeedback(pipeline, primitive_mode);
if (host_tfb_enabled && !maxwell3d->regs.transform_feedback_enabled) {
EndTransformFeedback();
} else {
BeginTransformFeedback(pipeline, primitive_mode);
}
draw_func(primitive_mode);
EndTransformFeedback();
++num_queued_commands;
has_written_global_memory |= pipeline->WritesGlobalMemory();
}
@ -1251,27 +1258,44 @@ void RasterizerOpenGL::SyncFramebufferSRGB() {
void RasterizerOpenGL::BeginTransformFeedback(GraphicsPipeline* program, GLenum primitive_mode) {
const auto& regs = maxwell3d->regs;
if (regs.transform_feedback_enabled == 0) {
UNIMPLEMENTED_IF(maxwell3d->regs.IsShaderConfigEnabled(Maxwell::ShaderType::TessellationInit) ||
maxwell3d->regs.IsShaderConfigEnabled(Maxwell::ShaderType::Tessellation));
if (host_tfb_enabled) {
prev_tfb_enabled = regs.transform_feedback_enabled;
return;
}
program->ConfigureTransformFeedback();
UNIMPLEMENTED_IF(regs.IsShaderConfigEnabled(Maxwell::ShaderType::TessellationInit) ||
regs.IsShaderConfigEnabled(Maxwell::ShaderType::Tessellation) ||
regs.IsShaderConfigEnabled(Maxwell::ShaderType::Geometry));
UNIMPLEMENTED_IF(primitive_mode != GL_POINTS);
// We may have to call BeginTransformFeedbackNV here since they seem to call different
// implementations on Nvidia's driver (the pointer is different) but we are using
// ARB_transform_feedback3 features with NV_transform_feedback interactions and the ARB
// extension doesn't define BeginTransformFeedback (without NV) interactions. It just works.
glBeginTransformFeedback(GL_POINTS);
if (regs.transform_feedback_enabled && prev_tfb_enabled) {
// if current and previous tfbs are enabled, we're resuming.
glResumeTransformFeedback();
host_tfb_enabled = true;
} else if (regs.transform_feedback_enabled) {
UNIMPLEMENTED_IF(primitive_mode != GL_POINTS);
// We may have to call BeginTransformFeedbackNV here since they seem to call different
// implementations on Nvidia's driver (the pointer is different) but we are using
// ARB_transform_feedback3 features with NV_transform_feedback interactions and the ARB
// extension doesn't define BeginTransformFeedback (without NV) interactions. It just works.
glBeginQuery(GL_TRANSFORM_FEEDBACK_PRIMITIVES_WRITTEN, transform_query);
glBeginTransformFeedback(GL_POINTS);
host_tfb_enabled = true;
}
prev_tfb_enabled = regs.transform_feedback_enabled;
}
void RasterizerOpenGL::EndTransformFeedback() {
if (maxwell3d->regs.transform_feedback_enabled != 0) {
void RasterizerOpenGL::EndTransformFeedback(bool force) {
if (!force && host_tfb_enabled && maxwell3d->regs.transform_feedback_enabled) {
// guest is still active, so pause
glPauseTransformFeedback();
host_tfb_enabled = false;
} else if (host_tfb_enabled) {
glEndTransformFeedback();
glEndQuery(GL_TRANSFORM_FEEDBACK_PRIMITIVES_WRITTEN);
host_tfb_enabled = false;
}
prev_tfb_enabled = maxwell3d->regs.transform_feedback_enabled;
}
void RasterizerOpenGL::InitializeChannel(Tegra::Control::ChannelState& channel) {
@ -1311,6 +1335,15 @@ void RasterizerOpenGL::ReleaseChannel(s32 channel_id) {
query_cache.EraseChannel(channel_id);
}
u32 RasterizerOpenGL::GetTransformFeedbackByteCount() {
EndTransformFeedback(true);
GLuint count{};
glGetQueryObjectuiv(transform_query, GL_QUERY_RESULT, &count);
return count * maxwell3d->regs.transform_feedback.controls[0].stride;
}
AccelerateDMA::AccelerateDMA(BufferCache& buffer_cache_, TextureCache& texture_cache_)
: buffer_cache{buffer_cache_}, texture_cache{texture_cache_} {}

View file

@ -138,6 +138,8 @@ public:
void ReleaseChannel(s32 channel_id) override;
u32 GetTransformFeedbackByteCount() override;
private:
static constexpr size_t MAX_TEXTURES = 192;
static constexpr size_t MAX_IMAGES = 48;
@ -222,7 +224,7 @@ private:
void BeginTransformFeedback(GraphicsPipeline* pipeline, GLenum primitive_mode);
/// End a transform feedback
void EndTransformFeedback();
void EndTransformFeedback(bool force = false);
Tegra::GPU& gpu;
@ -254,6 +256,9 @@ private:
bool has_written_global_memory = false;
u32 last_clip_distance_mask = 0;
bool host_tfb_enabled{};
bool prev_tfb_enabled{};
GLuint transform_query{};
};
} // namespace OpenGL

View file

@ -610,4 +610,20 @@ void BufferCacheRuntime::ReserveNullBuffer() {
});
}
vk::Buffer BufferCacheRuntime::CreateTransformCounterBuffer() {
return memory_allocator.CreateBuffer(
VkBufferCreateInfo{
.sType = VK_STRUCTURE_TYPE_BUFFER_CREATE_INFO,
.pNext = nullptr,
.flags = 0,
.size = 4,
.usage = VK_BUFFER_USAGE_TRANSFORM_FEEDBACK_COUNTER_BUFFER_BIT_EXT |
VK_BUFFER_USAGE_TRANSFER_SRC_BIT | VK_BUFFER_USAGE_INDIRECT_BUFFER_BIT,
.sharingMode = VK_SHARING_MODE_EXCLUSIVE,
.queueFamilyIndexCount = 0,
.pQueueFamilyIndices = nullptr,
},
MemoryUsage::DeviceLocal);
}
} // namespace Vulkan

View file

@ -83,7 +83,7 @@ public:
void PreCopyBarrier();
void CopyBuffer(VkBuffer src_buffer, VkBuffer dst_buffer,
void CopyBuffer(VkBuffer dst_buffer, VkBuffer src_buffer,
std::span<const VideoCommon::BufferCopy> copies, bool barrier = true);
void PostCopyBarrier();
@ -124,6 +124,8 @@ public:
guest_descriptor_queue.AddTexelBuffer(buffer.View(offset, size, format));
}
vk::Buffer CreateTransformCounterBuffer();
private:
void BindBuffer(VkBuffer buffer, u32 offset, u32 size) {
guest_descriptor_queue.AddBuffer(buffer, offset, size);

View file

@ -176,6 +176,7 @@ RasterizerVulkan::RasterizerVulkan(Core::Frontend::EmuWindow& emu_window_, Tegra
fence_manager(*this, gpu, texture_cache, buffer_cache, query_cache, device, scheduler),
wfi_event(device.GetLogical().CreateEvent()) {
scheduler.SetQueryCache(query_cache);
transform_counter = buffer_cache_runtime.CreateTransformCounterBuffer();
}
RasterizerVulkan::~RasterizerVulkan() = default;
@ -206,13 +207,15 @@ void RasterizerVulkan::PrepareDraw(bool is_indexed, Func&& draw_func) {
pipeline->SetEngine(maxwell3d, gpu_memory);
pipeline->Configure(is_indexed);
BeginTransformFeedback();
if (host_tfb_enabled && !maxwell3d->regs.transform_feedback_enabled) {
EndTransformFeedback();
} else {
BeginTransformFeedback();
}
UpdateDynamicStates();
draw_func();
EndTransformFeedback();
}
void RasterizerVulkan::Draw(bool is_indexed, u32 instance_count) {
@ -902,30 +905,46 @@ void RasterizerVulkan::UpdateDynamicStates() {
}
void RasterizerVulkan::BeginTransformFeedback() {
const auto& regs = maxwell3d->regs;
if (regs.transform_feedback_enabled == 0) {
return;
}
if (!device.IsExtTransformFeedbackSupported()) {
LOG_ERROR(Render_Vulkan, "Transform feedbacks used but not supported");
return;
}
const auto& regs = maxwell3d->regs;
UNIMPLEMENTED_IF(regs.IsShaderConfigEnabled(Maxwell::ShaderType::TessellationInit) ||
regs.IsShaderConfigEnabled(Maxwell::ShaderType::Tessellation));
scheduler.Record(
[](vk::CommandBuffer cmdbuf) { cmdbuf.BeginTransformFeedbackEXT(0, 0, nullptr, nullptr); });
if (host_tfb_enabled) {
prev_tfb_enabled = regs.transform_feedback_enabled;
return;
}
if (regs.transform_feedback_enabled && prev_tfb_enabled) {
// if current and previous tfbs are enabled, we're resuming.
scheduler.Record([buffer = *transform_counter](vk::CommandBuffer cmdbuf) {
cmdbuf.BeginTransformFeedbackEXT(0, 1, &buffer, nullptr);
});
host_tfb_enabled = true;
} else if (regs.transform_feedback_enabled) {
scheduler.Record([](vk::CommandBuffer cmdbuf) {
cmdbuf.BeginTransformFeedbackEXT(0, 0, nullptr, nullptr);
});
host_tfb_enabled = true;
}
prev_tfb_enabled = regs.transform_feedback_enabled;
}
void RasterizerVulkan::EndTransformFeedback() {
const auto& regs = maxwell3d->regs;
if (regs.transform_feedback_enabled == 0) {
return;
}
if (!device.IsExtTransformFeedbackSupported()) {
return;
}
scheduler.Record(
[](vk::CommandBuffer cmdbuf) { cmdbuf.EndTransformFeedbackEXT(0, 0, nullptr, nullptr); });
if (host_tfb_enabled) {
scheduler.Record([buffer = *transform_counter](vk::CommandBuffer cmdbuf) {
cmdbuf.EndTransformFeedbackEXT(0, 1, &buffer, nullptr);
});
host_tfb_enabled = false;
}
prev_tfb_enabled = maxwell3d->regs.transform_feedback_enabled;
}
void RasterizerVulkan::UpdateViewportsState(Tegra::Engines::Maxwell3D::Regs& regs) {
@ -1494,4 +1513,22 @@ void RasterizerVulkan::ReleaseChannel(s32 channel_id) {
query_cache.EraseChannel(channel_id);
}
u32 RasterizerVulkan::GetTransformFeedbackByteCount() {
EndTransformFeedback();
auto download_staging = buffer_cache_runtime.DownloadStagingBuffer(4);
std::array copy{VideoCommon::BufferCopy{
0,
download_staging.offset,
4,
}};
buffer_cache_runtime.CopyBuffer(download_staging.buffer, *transform_counter,
std::span<VideoCommon::BufferCopy>(copy));
scheduler.Finish();
u32 count{};
std::memcpy(&count, download_staging.mapped_span.data(), 4);
return count;
}
} // namespace Vulkan

View file

@ -131,6 +131,8 @@ public:
void ReleaseChannel(s32 channel_id) override;
u32 GetTransformFeedbackByteCount() override;
private:
static constexpr size_t MAX_TEXTURES = 192;
static constexpr size_t MAX_IMAGES = 48;
@ -206,6 +208,9 @@ private:
boost::container::static_vector<VkSampler, MAX_TEXTURES> sampler_handles;
u32 draw_counter = 0;
bool host_tfb_enabled{};
bool prev_tfb_enabled{};
vk::Buffer transform_counter;
};
} // namespace Vulkan

View file

@ -100,6 +100,7 @@ void Load(VkDevice device, DeviceDispatch& dld) noexcept {
X(vkCmdDrawIndexedIndirect);
X(vkCmdDrawIndirectCount);
X(vkCmdDrawIndexedIndirectCount);
X(vkCmdDrawIndirectByteCountEXT);
X(vkCmdEndQuery);
X(vkCmdEndRenderPass);
X(vkCmdEndTransformFeedbackEXT);

View file

@ -224,6 +224,7 @@ struct DeviceDispatch : InstanceDispatch {
PFN_vkCmdDrawIndexedIndirect vkCmdDrawIndexedIndirect{};
PFN_vkCmdDrawIndirectCount vkCmdDrawIndirectCount{};
PFN_vkCmdDrawIndexedIndirectCount vkCmdDrawIndexedIndirectCount{};
PFN_vkCmdDrawIndirectByteCountEXT vkCmdDrawIndirectByteCountEXT{};
PFN_vkCmdEndDebugUtilsLabelEXT vkCmdEndDebugUtilsLabelEXT{};
PFN_vkCmdEndQuery vkCmdEndQuery{};
PFN_vkCmdEndRenderPass vkCmdEndRenderPass{};
@ -1196,6 +1197,13 @@ public:
count_offset, draw_count, stride);
}
void DrawIndirectByteCountEXT(u32 instance_count, u32 first_instance, VkBuffer counter_buffer,
VkDeviceSize counter_buffer_offset, u32 counter_offset,
u32 vertex_stride) const noexcept {
dld->vkCmdDrawIndirectByteCountEXT(handle, instance_count, first_instance, counter_buffer,
counter_buffer_offset, counter_offset, vertex_stride);
}
void ClearAttachments(Span<VkClearAttachment> attachments,
Span<VkClearRect> rects) const noexcept {
dld->vkCmdClearAttachments(handle, attachments.size(), attachments.data(), rects.size(),

View file

@ -764,6 +764,7 @@ void Config::ReadRendererValues() {
ReadGlobalSetting(Settings::values.use_asynchronous_gpu_emulation);
ReadGlobalSetting(Settings::values.nvdec_emulation);
ReadGlobalSetting(Settings::values.accelerate_astc);
ReadGlobalSetting(Settings::values.transform_feedback_query);
ReadGlobalSetting(Settings::values.async_astc);
ReadGlobalSetting(Settings::values.astc_recompression);
ReadGlobalSetting(Settings::values.use_reactive_flushing);
@ -1429,6 +1430,7 @@ void Config::SaveRendererValues() {
static_cast<u32>(Settings::values.nvdec_emulation.GetDefault()),
Settings::values.nvdec_emulation.UsingGlobal());
WriteGlobalSetting(Settings::values.accelerate_astc);
WriteGlobalSetting(Settings::values.transform_feedback_query);
WriteGlobalSetting(Settings::values.async_astc);
WriteSetting(QString::fromStdString(Settings::values.astc_recompression.GetLabel()),
static_cast<u32>(Settings::values.astc_recompression.GetValue(global)),

View file

@ -45,6 +45,7 @@ void ConfigureGraphicsAdvanced::SetConfiguration() {
ui->use_video_framerate_checkbox->setChecked(Settings::values.use_video_framerate.GetValue());
ui->barrier_feedback_loops_checkbox->setChecked(
Settings::values.barrier_feedback_loops.GetValue());
ui->transform_feedback_query->setChecked(Settings::values.transform_feedback_query.GetValue());
if (Settings::IsConfiguringGlobal()) {
ui->gpu_accuracy->setCurrentIndex(
@ -99,6 +100,9 @@ void ConfigureGraphicsAdvanced::ApplyConfiguration() {
ConfigurationShared::ApplyPerGameSetting(&Settings::values.barrier_feedback_loops,
ui->barrier_feedback_loops_checkbox,
barrier_feedback_loops);
ConfigurationShared::ApplyPerGameSetting(&Settings::values.transform_feedback_query,
ui->transform_feedback_query,
transform_feedback_query);
}
void ConfigureGraphicsAdvanced::changeEvent(QEvent* event) {
@ -137,6 +141,8 @@ void ConfigureGraphicsAdvanced::SetupPerGameUI() {
Settings::values.use_video_framerate.UsingGlobal());
ui->barrier_feedback_loops_checkbox->setEnabled(
Settings::values.barrier_feedback_loops.UsingGlobal());
ui->transform_feedback_query->setEnabled(
Settings::values.transform_feedback_query.UsingGlobal());
return;
}
@ -167,6 +173,10 @@ void ConfigureGraphicsAdvanced::SetupPerGameUI() {
ConfigurationShared::SetColoredTristate(ui->barrier_feedback_loops_checkbox,
Settings::values.barrier_feedback_loops,
barrier_feedback_loops);
ConfigurationShared::SetColoredTristate(ui->transform_feedback_query,
Settings::values.transform_feedback_query,
transform_feedback_query);
ConfigurationShared::SetColoredComboBox(
ui->gpu_accuracy, ui->label_gpu_accuracy,
static_cast<int>(Settings::values.gpu_accuracy.GetValue(true)));

View file

@ -49,6 +49,7 @@ private:
ConfigurationShared::CheckState enable_compute_pipelines;
ConfigurationShared::CheckState use_video_framerate;
ConfigurationShared::CheckState barrier_feedback_loops;
ConfigurationShared::CheckState transform_feedback_query;
const Core::System& system;
};

View file

@ -72,44 +72,44 @@
<item>
<widget class="QWidget" name="astc_recompression_layout" native="true">
<layout class="QHBoxLayout" name="horizontalLayout_3">
<property name="leftMargin">
<number>0</number>
</property>
<property name="topMargin">
<number>0</number>
</property>
<property name="rightMargin">
<number>0</number>
</property>
<property name="bottomMargin">
<number>0</number>
</property>
<item>
<widget class="QLabel" name="label_astc_recompression">
<property name="leftMargin">
<number>0</number>
</property>
<property name="topMargin">
<number>0</number>
</property>
<property name="rightMargin">
<number>0</number>
</property>
<property name="bottomMargin">
<number>0</number>
</property>
<item>
<widget class="QLabel" name="label_astc_recompression">
<property name="text">
<string>ASTC recompression:</string>
</property>
</widget>
</item>
<item>
<widget class="QComboBox" name="astc_recompression_combobox">
<item>
<property name="text">
<string>ASTC recompression:</string>
<string>Uncompressed (Best quality)</string>
</property>
</widget>
</item>
<item>
<widget class="QComboBox" name="astc_recompression_combobox">
<item>
<property name="text">
<string>Uncompressed (Best quality)</string>
</property>
</item>
<item>
<property name="text">
<string>BC1 (Low quality)</string>
</property>
</item>
<item>
<property name="text">
<string>BC3 (Medium quality)</string>
</property>
</item>
</widget>
</item>
</item>
<item>
<property name="text">
<string>BC1 (Low quality)</string>
</property>
</item>
<item>
<property name="text">
<string>BC3 (Medium quality)</string>
</property>
</item>
</widget>
</item>
</layout>
</widget>
</item>
@ -211,6 +211,16 @@ Compute pipelines are always enabled on all other drivers.</string>
</property>
</widget>
</item>
<item>
<widget class="QCheckBox" name="transform_feedback_query">
<property name="text">
<string>Enable Transform Feedback queries</string>
</property>
<property name="toolTip">
<string>Enables querying the TFB counter for TFB draws. Can noticeably reduce performance of some games if they query TFB but not rely on it to draw.</string>
</property>
</widget>
</item>
<item>
<widget class="QWidget" name="af_layout" native="true">
<layout class="QHBoxLayout" name="horizontalLayout_1">