renderer: expand rectangle lists with tessellation

Remove the legacy NGG rectangle workaround and its configuration toggle.
This commit is contained in:
nmzik
2026-08-02 23:06:25 +02:00
parent 06cbd602c1
commit 86a586025f
19 changed files with 649 additions and 135 deletions
@@ -154,8 +154,9 @@ PipelineCache::GraphicsPipeline& PipelineCache::CreateGraphicsPipeline(
for (uint32_t i = 0; i < RENDER_COLOR_ATTACHMENTS_MAX; i++) {
static_params.color_mask[i] = color_mask[i];
}
static_params.cull_back = mc.cull_back;
static_params.cull_front = mc.cull_front;
const bool rect_list = topology == vk::PrimitiveTopology::ePatchList;
static_params.cull_back = !rect_list && mc.cull_back;
static_params.cull_front = !rect_list && mc.cull_front;
static_params.face = mc.face;
for (uint32_t i = 0; i < color_count; i++) {
@@ -14,6 +14,7 @@
#include "graphics/host_gpu/renderer/renderTarget.h"
#include "graphics/host_gpu/vulkanCommon.h"
#include "graphics/shader/recompiler/ir/ShaderIR.h"
#include "graphics/shader/rectListShader.h"
#include "graphics/shader/shader.h"
#include <algorithm>
@@ -463,8 +464,12 @@ void CreatePipelineInternal(
uint32_t ps_hash0, uint32_t ps_crc32, bool ps_active) {
EXIT_IF(ps_active && ps_input_info == nullptr);
vk::ShaderModule vert_shader_module = nullptr;
vk::ShaderModule frag_shader_module = nullptr;
const bool rect_list = static_params.topology == vk::PrimitiveTopology::ePatchList;
vk::ShaderModule vert_shader_module = nullptr;
vk::ShaderModule tess_control_shader_module = nullptr;
vk::ShaderModule tess_eval_shader_module = nullptr;
vk::ShaderModule frag_shader_module = nullptr;
vk::ShaderModuleCreateInfo create_info {};
@@ -491,8 +496,33 @@ void CreatePipelineInternal(
}
EXIT_NOT_IMPLEMENTED(result != vk::Result::eSuccess);
}
if (rect_list) {
const auto shaders =
BuildRectListShaders(vs_input_info, ps_active ? ps_input_info : nullptr);
create_info.codeSize = shaders.control.size() * 4;
create_info.pCode = shaders.control.data();
result =
graphics.device.createShaderModule(&create_info, nullptr, &tess_control_shader_module);
if (graphics_debug_dump_enabled()) {
LOGF("PipelineTrace: vkCreateShaderModule RectList TCS done result=%s module=%p\n",
VulkanToString(result).c_str(), static_cast<void*>(tess_control_shader_module));
}
EXIT_NOT_IMPLEMENTED(result != vk::Result::eSuccess);
create_info.codeSize = shaders.evaluation.size() * 4;
create_info.pCode = shaders.evaluation.data();
result =
graphics.device.createShaderModule(&create_info, nullptr, &tess_eval_shader_module);
if (graphics_debug_dump_enabled()) {
LOGF("PipelineTrace: vkCreateShaderModule RectList TES done result=%s module=%p\n",
VulkanToString(result).c_str(), static_cast<void*>(tess_eval_shader_module));
}
EXIT_NOT_IMPLEMENTED(result != vk::Result::eSuccess);
}
EXIT_NOT_IMPLEMENTED(vert_shader_module == nullptr);
EXIT_NOT_IMPLEMENTED(
rect_list && (tess_control_shader_module == nullptr || tess_eval_shader_module == nullptr));
EXIT_NOT_IMPLEMENTED(ps_active && frag_shader_module == nullptr);
vk::PipelineShaderStageCreateInfo vert_shader_stage_info {};
@@ -525,9 +555,28 @@ void CreatePipelineInternal(
frag_shader_stage_info);
}
vk::PipelineShaderStageCreateInfo shader_stages[] = {vert_shader_stage_info,
frag_shader_stage_info};
const uint32_t shader_stage_count = ps_active ? 2u : 1u;
vk::PipelineShaderStageCreateInfo tess_control_shader_stage_info {};
tess_control_shader_stage_info.sType = vk::StructureType::ePipelineShaderStageCreateInfo;
tess_control_shader_stage_info.stage = vk::ShaderStageFlagBits::eTessellationControl;
tess_control_shader_stage_info.module = tess_control_shader_module;
tess_control_shader_stage_info.pName = "main";
vk::PipelineShaderStageCreateInfo tess_eval_shader_stage_info {};
tess_eval_shader_stage_info.sType = vk::StructureType::ePipelineShaderStageCreateInfo;
tess_eval_shader_stage_info.stage = vk::ShaderStageFlagBits::eTessellationEvaluation;
tess_eval_shader_stage_info.module = tess_eval_shader_module;
tess_eval_shader_stage_info.pName = "main";
vk::PipelineShaderStageCreateInfo shader_stages[4] = {};
uint32_t shader_stage_count = 0;
shader_stages[shader_stage_count++] = vert_shader_stage_info;
if (rect_list) {
shader_stages[shader_stage_count++] = tess_control_shader_stage_info;
shader_stages[shader_stage_count++] = tess_eval_shader_stage_info;
}
if (ps_active) {
shader_stages[shader_stage_count++] = frag_shader_stage_info;
}
vk::VertexInputAttributeDescription input_attr[ShaderVertexInputInfo::RES_MAX];
vk::VertexInputBindingDescription input_desc[ShaderVertexInputInfo::RES_MAX];
@@ -929,10 +978,13 @@ void CreatePipelineInternal(
pipeline_info.pStages = shader_stages;
pipeline_info.pVertexInputState = &vertex_input_info;
pipeline_info.pInputAssemblyState = &input_assembly;
pipeline_info.pTessellationState = nullptr;
pipeline_info.pViewportState = &viewport_state;
pipeline_info.pRasterizationState = &rasterizer;
pipeline_info.pMultisampleState = &multisampling;
vk::PipelineTessellationStateCreateInfo tessellation_state {};
tessellation_state.sType = vk::StructureType::ePipelineTessellationStateCreateInfo;
tessellation_state.patchControlPoints = 3;
pipeline_info.pTessellationState = (rect_list ? &tessellation_state : nullptr);
pipeline_info.pViewportState = &viewport_state;
pipeline_info.pRasterizationState = &rasterizer;
pipeline_info.pMultisampleState = &multisampling;
pipeline_info.pDepthStencilState = (static_params.with_depth ? &depth_stencil_info : nullptr);
pipeline_info.pColorBlendState = &color_blending;
pipeline_info.pDynamicState = &dynamic_state;
@@ -968,6 +1020,12 @@ void CreatePipelineInternal(
if (frag_shader_module != nullptr) {
graphics.device.destroyShaderModule(frag_shader_module, nullptr);
}
if (tess_control_shader_module != nullptr) {
graphics.device.destroyShaderModule(tess_control_shader_module, nullptr);
}
if (tess_eval_shader_module != nullptr) {
graphics.device.destroyShaderModule(tess_eval_shader_module, nullptr);
}
graphics.device.destroyShaderModule(vert_shader_module, nullptr);
}
+15 -51
View File
@@ -2,7 +2,6 @@
#include "common/assert.h"
#include "common/common.h"
#include "common/emulatorConfig.h"
#include "common/file.h"
#include "common/logging/log.h"
#include "common/profiler.h"
@@ -650,11 +649,9 @@ static bool ConsumeMetadataColorOperation(const RenderCommandBuffer& buffer) {
}
struct DrawEmitInfo {
bool indexed = false;
bool draw_prim7_as_ngg = false;
uint32_t draw_vertex_count = 0;
int32_t vertex_offset = 0;
uint32_t first_vertex = 0;
bool indexed = false;
int32_t vertex_offset = 0;
uint32_t first_vertex = 0;
};
struct DrawIndexBufferSource {
@@ -767,7 +764,7 @@ static void SetDrawDebugPhase(RenderCommandBuffer& buffer, uint64_t submit_id,
draw.flags, draw.instance_count, draw.first_instance);
}
static bool GetDrawTopology(const HW::UserConfig& ucfg, bool auto_draw, bool use_ngg_rectlist_draw,
static bool GetDrawTopology(const HW::UserConfig& ucfg, bool auto_draw,
vk::PrimitiveTopology& topology) {
topology = vk::PrimitiveTopology::ePointList;
@@ -791,8 +788,7 @@ static bool GetDrawTopology(const HW::UserConfig& ucfg, bool auto_draw, bool use
topology = vk::PrimitiveTopology::eTriangleStrip;
break;
case Prospero::PrimitiveType::kRectList:
topology = (auto_draw && use_ngg_rectlist_draw ? vk::PrimitiveTopology::eTriangleStrip
: vk::PrimitiveTopology::eTriangleList);
topology = vk::PrimitiveTopology::ePatchList;
break;
case Prospero::PrimitiveType::kRectListLegacy:
if (!auto_draw) {
@@ -991,20 +987,6 @@ static void LogDrawStateIfNeeded(const RenderCommandBuffer& buffer, const DrawCa
// LogDrawTextureState(draw.name, state.color_info[0], state.ps_input_info);
}
static bool IsHostExpandedRectListDrawSupported(const ShaderVertexInputInfo& vs_input_info,
const DrawCallInfo& draw,
const DrawEmitInfo& emit) {
if (!emit.draw_prim7_as_ngg) {
return true;
}
if (vs_input_info.buffers_num != 0) {
return false;
}
return draw.index_count == 3 || draw.index_count == emit.draw_vertex_count;
}
static void EmitDrawPrimitives(const HW::UserConfig& ucfg, vk::CommandBuffer vk_buffer,
const ShaderVertexInputInfo& vs_input_info, const DrawCallInfo& draw,
const DrawEmitInfo& emit) {
@@ -1017,22 +999,12 @@ static void EmitDrawPrimitives(const HW::UserConfig& ucfg, vk::CommandBuffer vk_
case Prospero::PrimitiveType::kTriList:
case Prospero::PrimitiveType::kTriFan:
case Prospero::PrimitiveType::kTriStrip:
if (emit.indexed) {
vk_buffer.drawIndexed(draw.index_count, draw.instance_count, 0, emit.vertex_offset,
draw.first_instance);
} else {
vk_buffer.draw(draw.index_count, draw.instance_count, emit.first_vertex,
draw.first_instance);
}
break;
case Prospero::PrimitiveType::kRectList:
if (emit.indexed) {
vk_buffer.drawIndexed(draw.index_count, draw.instance_count, 0, emit.vertex_offset,
draw.first_instance);
} else {
EXIT_NOT_IMPLEMENTED(
!IsHostExpandedRectListDrawSupported(vs_input_info, draw, emit));
vk_buffer.draw(emit.draw_vertex_count, draw.instance_count, emit.first_vertex,
vk_buffer.draw(draw.index_count, draw.instance_count, emit.first_vertex,
draw.first_instance);
}
break;
@@ -1201,7 +1173,7 @@ void RenderExecutor::DrawIndex(uint64_t submit_id, RenderCommandBuffer& buffer,
hw_check(buffer);
vk::PrimitiveTopology topology = vk::PrimitiveTopology::ePointList;
if (!GetDrawTopology(ucfg, false, false, topology)) {
if (!GetDrawTopology(ucfg, false, topology)) {
return;
}
@@ -1337,20 +1309,15 @@ void RenderExecutor::DrawAuto(uint64_t submit_id, RenderCommandBuffer& buffer, u
return;
}
vk::PrimitiveTopology topology = vk::PrimitiveTopology::ePointList;
const bool use_ngg_rectlist_draw = Config::NggRectlistDrawEnabled();
if (!GetDrawTopology(ucfg, true, use_ngg_rectlist_draw, topology)) {
vk::PrimitiveTopology topology = vk::PrimitiveTopology::ePointList;
if (!GetDrawTopology(ucfg, true, topology)) {
ResetBindings();
return;
}
const bool draw_prim7_as_ngg =
(use_ngg_rectlist_draw &&
ucfg.GetPrimType() == Prospero::GpuEnumValue(Prospero::PrimitiveType::kRectList));
RefreshShaders(buffer, draw, false, state);
if (draw_prim7_as_ngg && state.vs_input_info.buffers_num == 0 &&
const bool rect_list = topology == vk::PrimitiveTopology::ePatchList;
if (rect_list && state.vs_input_info.buffers_num == 0 &&
state.vs_input_info.param_export_mask == 0 && state.ps_input_info.input_num != 0) {
if (graphics_debug_dump_enabled()) {
LOGF("DrawIndexAuto: skipping rect-list draw with no VS param exports and PS inputs: "
@@ -1367,13 +1334,10 @@ void RenderExecutor::DrawAuto(uint64_t submit_id, RenderCommandBuffer& buffer, u
Prospero::GpuEnumValue(Prospero::PrimitiveType::kRectListLegacy),
0, nullptr);
const uint32_t draw_vertex_count = (draw_prim7_as_ngg ? 4u : index_count);
const auto vertex_offset = ResolveVertexOffset(ucfg.GetIndexOffset(), state.vs_input_info) +
static_cast<int32_t>(first_vertex);
DrawEmitInfo emit {};
emit.draw_prim7_as_ngg = draw_prim7_as_ngg;
emit.draw_vertex_count = draw_vertex_count;
emit.first_vertex = static_cast<uint32_t>(vertex_offset);
const auto vertex_offset = ResolveVertexOffset(ucfg.GetIndexOffset(), state.vs_input_info) +
static_cast<int32_t>(first_vertex);
DrawEmitInfo emit {};
emit.first_vertex = static_cast<uint32_t>(vertex_offset);
DrawIndexBufferSource index_source {};
ExecutePreparedDraw(submit_id, buffer, draw, state, topology, emit, index_source, false, false,