A quick topology test

This commit is contained in:
CamilleLaVey
2026-09-17 16:25:34 -04:00
parent f92e326e17
commit 377478cd46
9 changed files with 192 additions and 24 deletions
+49 -9
View File
@@ -282,28 +282,68 @@ void Maxwell3D::DrawManager::DrawTexture(Maxwell3D& maxwell3d) {
}
void Maxwell3D::DrawManager::UpdateTopology(Maxwell3D& maxwell3d) {
using Topology = Maxwell3D::Regs::PrimitiveTopology;
using Override = Maxwell3D::Regs::PrimitiveTopologyOverride;
switch (maxwell3d.regs.primitive_topology_control) {
case Maxwell3D::Regs::PrimitiveTopologyControl::UseInBeginMethods:
break;
case Maxwell3D::Regs::PrimitiveTopologyControl::UseSeparateState:
switch (maxwell3d.regs.topology_override) {
case Maxwell3D::Regs::PrimitiveTopologyOverride::None:
case Override::None:
break;
case Maxwell3D::Regs::PrimitiveTopologyOverride::Points:
draw_state.topology = Maxwell3D::Regs::PrimitiveTopology::Points;
case Override::Points:
case Override::LegacyPoints:
draw_state.topology = Topology::Points;
break;
case Maxwell3D::Regs::PrimitiveTopologyOverride::Lines:
draw_state.topology = Maxwell3D::Regs::PrimitiveTopology::Lines;
case Override::Lines:
case Override::LegacyLines:
case Override::LegacyLinesImm:
case Override::LegacyIndexedLines:
case Override::LegacyIndexedLines2:
draw_state.topology = Topology::Lines;
break;
case Maxwell3D::Regs::PrimitiveTopologyOverride::LineStrip:
draw_state.topology = Maxwell3D::Regs::PrimitiveTopology::LineStrip;
case Override::LineStrip:
case Override::LegacyLineStrip:
case Override::LegacyIndexedLineStrip:
draw_state.topology = Topology::LineStrip;
break;
default:
draw_state.topology = Maxwell3D::Regs::PrimitiveTopology(maxwell3d.regs.topology_override);
case Override::Triangles:
case Override::LegacyTriangles:
case Override::LegacyIndexedTriangles:
case Override::LegacyIndexedTriangles2:
draw_state.topology = Topology::Triangles;
break;
case Override::TriangleStrip:
case Override::LegacyTriangleStrip:
case Override::LegacyIndexedTriangleStrip:
draw_state.topology = Topology::TriangleStrip;
break;
case Override::LegacyTriangleFan:
case Override::LegacyTriangleFanImm:
case Override::LegacyIndexedTriangleFan:
draw_state.topology = Topology::TriangleFan;
break;
case Override::LinesAdjacency:
draw_state.topology = Topology::LinesAdjacency;
break;
case Override::LineStripAdjacency:
draw_state.topology = Topology::LineStripAdjacency;
break;
case Override::TrianglesAdjacency:
draw_state.topology = Topology::TrianglesAdjacency;
break;
case Override::TriangleStripAdjacency:
draw_state.topology = Topology::TriangleStripAdjacency;
break;
case Override::Patches:
draw_state.topology = Topology::Patches;
break;
}
break;
}
if (u32(draw_state.topology) > u32(Topology::Patches)) {
draw_state.topology = Topology::Triangles;
}
}
void Maxwell3D::DrawManager::ProcessDraw(Maxwell3D& maxwell3d, bool draw_indexed, u32 instance_count) {
@@ -78,6 +78,7 @@ set(SHADER_FILES
${CMAKE_CURRENT_SOURCE_DIR}/vulkan_fidelityfx_fsr_easu_fp32.frag
${CMAKE_CURRENT_SOURCE_DIR}/vulkan_fidelityfx_fsr_rcas_fp16.frag
${CMAKE_CURRENT_SOURCE_DIR}/vulkan_fidelityfx_fsr_rcas_fp32.frag
${CMAKE_CURRENT_SOURCE_DIR}/vulkan_indirect_quads.comp
${CMAKE_CURRENT_SOURCE_DIR}/vulkan_present.frag
${CMAKE_CURRENT_SOURCE_DIR}/vulkan_present.vert
${CMAKE_CURRENT_SOURCE_DIR}/vulkan_present_scaleforce_fp16.frag
@@ -0,0 +1,33 @@
// SPDX-FileCopyrightText: Copyright 2026 Eden Emulator Project
// SPDX-License-Identifier: GPL-3.0-or-later
#version 460 core
layout (local_size_x = 32) in;
layout (std430, set = 0, binding = 0) readonly buffer InputBuffer {
uint input_commands[];
};
layout (std430, set = 0, binding = 1) writeonly buffer OutputBuffer {
uint output_commands[];
};
layout (push_constant) uniform PushConstants {
uint num_draws;
uint src_stride;
};
void main() {
uint draw = gl_GlobalInvocationID.x;
if (draw >= num_draws) {
return;
}
uint src = draw * src_stride;
uint dst = draw * 5;
output_commands[dst + 0] = input_commands[src + 0] / 4 * 6;
output_commands[dst + 1] = input_commands[src + 1];
output_commands[dst + 2] = input_commands[src + 2] / 4 * 6;
output_commands[dst + 3] = input_commands[src + 3];
output_commands[dst + 4] = input_commands[src + 4];
}
+6 -2
View File
@@ -62,6 +62,10 @@ bool IsTopologySafe(Maxwell3D::Regs::PrimitiveTopology topology) {
}
}
bool IsTopologySafeIndexedIndirect(Maxwell3D::Regs::PrimitiveTopology topology) {
return IsTopologySafe(topology) || topology == Maxwell3D::Regs::PrimitiveTopology::Quads;
}
} // Anonymous namespace
void HLE_DrawArraysIndirect::Execute(Core::System& system, Engines::Maxwell3D& maxwell3d, std::span<const u32> parameters, [[maybe_unused]] u32 method) {
@@ -125,7 +129,7 @@ void HLE_DrawArraysIndirect::Fallback(Core::System& system, Engines::Maxwell3D&
void HLE_DrawIndexedIndirect::Execute(Core::System& system, Engines::Maxwell3D& maxwell3d, std::span<const u32> parameters, [[maybe_unused]] u32 method) {
auto topology = static_cast<Maxwell3D::Regs::PrimitiveTopology>(parameters[0]);
if (!maxwell3d.AnyParametersDirty() || !IsTopologySafe(topology)) {
if (!maxwell3d.AnyParametersDirty() || !IsTopologySafeIndexedIndirect(topology)) {
Fallback(system, maxwell3d, parameters);
return;
}
@@ -198,7 +202,7 @@ void HLE_MultiLayerClear::Execute(Core::System& system, Engines::Maxwell3D& maxw
}
void HLE_MultiDrawIndexedIndirectCount::Execute(Core::System& system, Engines::Maxwell3D& maxwell3d, std::span<const u32> parameters, [[maybe_unused]] u32 method) {
const auto topology = Maxwell3D::Regs::PrimitiveTopology(parameters[2]);
if (IsTopologySafe(topology)) {
if (IsTopologySafeIndexedIndirect(topology)) {
const u32 start_indirect = parameters[0];
const u32 end_indirect = parameters[1];
if (start_indirect >= end_indirect) {
@@ -20,6 +20,7 @@
#include "video_core/host_shaders/queries_prefix_scan_sum_comp_spv.h"
#include "video_core/host_shaders/queries_prefix_scan_sum_nosubgroups_comp_spv.h"
#include "video_core/host_shaders/resolve_conditional_render_comp_spv.h"
#include "video_core/host_shaders/vulkan_indirect_quads_comp_spv.h"
#include "video_core/host_shaders/vulkan_quad_indexed_comp_spv.h"
#include "video_core/host_shaders/vulkan_uint8_comp_spv.h"
#include "video_core/renderer_vulkan/vk_compute_pass.h"
@@ -375,6 +376,56 @@ std::pair<VkBuffer, VkDeviceSize> QuadIndexedPass::Assemble(
return {staging.buffer, staging.offset};
}
IndirectQuadsPass::IndirectQuadsPass(const Device& device_, Scheduler& scheduler_,
DescriptorPool& descriptor_pool_,
StagingBufferPool& staging_buffer_pool_,
ComputePassDescriptorQueue& compute_pass_descriptor_queue_)
: ComputePass(device_, scheduler_, descriptor_pool_, INPUT_OUTPUT_DESCRIPTOR_SET_BINDINGS,
INPUT_OUTPUT_DESCRIPTOR_UPDATE_TEMPLATE, INPUT_OUTPUT_BANK_INFO,
COMPUTE_PUSH_CONSTANT_RANGE<sizeof(u32) * 2>, VULKAN_INDIRECT_QUADS_COMP_SPV),
scheduler{scheduler_}, staging_buffer_pool{staging_buffer_pool_},
compute_pass_descriptor_queue{compute_pass_descriptor_queue_} {}
IndirectQuadsPass::~IndirectQuadsPass() = default;
std::pair<VkBuffer, VkDeviceSize> IndirectQuadsPass::Assemble(u32 num_draws, u32 stride,
VkBuffer src_buffer, u32 src_offset) {
u32 src_stride = stride / static_cast<u32>(sizeof(u32));
if (src_stride < COMMAND_WORDS) {
src_stride = COMMAND_WORDS;
}
const u32 input_size = num_draws * src_stride * static_cast<u32>(sizeof(u32));
const std::size_t staging_size = std::size_t(num_draws) * COMMAND_WORDS * sizeof(u32);
const auto staging = staging_buffer_pool.Request(staging_size, MemoryUsage::DeviceLocal);
compute_pass_descriptor_queue.Acquire(scheduler, 2);
compute_pass_descriptor_queue.AddBuffer(src_buffer, src_offset, input_size);
compute_pass_descriptor_queue.AddBuffer(staging.buffer, staging.offset, staging_size);
const void* const descriptor_data{compute_pass_descriptor_queue.UpdateData()};
scheduler.RequestOutsideRenderPassOperationContext();
scheduler.Record([this, descriptor_data, num_draws, src_stride](vk::CommandBuffer cmdbuf) {
static constexpr u32 DISPATCH_SIZE = 32;
static constexpr VkMemoryBarrier WRITE_BARRIER{
.sType = VK_STRUCTURE_TYPE_MEMORY_BARRIER,
.pNext = nullptr,
.srcAccessMask = VK_ACCESS_SHADER_WRITE_BIT,
.dstAccessMask = VK_ACCESS_INDIRECT_COMMAND_READ_BIT,
};
const std::array<u32, 2> push_constants{num_draws, src_stride};
const VkDescriptorSet set = descriptor_allocator.Commit();
device.GetLogical().UpdateDescriptorSet(set, *descriptor_template, descriptor_data);
cmdbuf.BindPipeline(VK_PIPELINE_BIND_POINT_COMPUTE, *pipeline);
cmdbuf.BindDescriptorSets(VK_PIPELINE_BIND_POINT_COMPUTE, *layout, 0, set, {});
cmdbuf.PushConstants(*layout, VK_SHADER_STAGE_COMPUTE_BIT, 0, sizeof(push_constants),
&push_constants);
cmdbuf.Dispatch(Common::DivCeil(num_draws, DISPATCH_SIZE), 1, 1);
cmdbuf.PipelineBarrier(VK_PIPELINE_STAGE_COMPUTE_SHADER_BIT,
VK_PIPELINE_STAGE_DRAW_INDIRECT_BIT, 0, WRITE_BARRIER);
});
return {staging.buffer, staging.offset};
}
ConditionalRenderingResolvePass::ConditionalRenderingResolvePass(
const Device& device_, Scheduler& scheduler_, DescriptorPool& descriptor_pool_,
ComputePassDescriptorQueue& compute_pass_descriptor_queue_)
@@ -88,6 +88,25 @@ private:
ComputePassDescriptorQueue& compute_pass_descriptor_queue;
};
class IndirectQuadsPass final : public ComputePass {
public:
explicit IndirectQuadsPass(const Device& device_, Scheduler& scheduler_,
DescriptorPool& descriptor_pool_,
StagingBufferPool& staging_buffer_pool_,
ComputePassDescriptorQueue& compute_pass_descriptor_queue_);
~IndirectQuadsPass();
static constexpr u32 COMMAND_WORDS = 5;
std::pair<VkBuffer, VkDeviceSize> Assemble(u32 num_draws, u32 stride, VkBuffer src_buffer,
u32 src_offset);
private:
Scheduler& scheduler;
StagingBufferPool& staging_buffer_pool;
ComputePassDescriptorQueue& compute_pass_descriptor_queue;
};
class ConditionalRenderingResolvePass final : public ComputePass {
public:
explicit ConditionalRenderingResolvePass(
@@ -212,6 +212,8 @@ RasterizerVulkan::RasterizerVulkan(Core::Frontend::EmuWindow& emu_window_, Tegra
compute_pass_descriptor_queue(device, UpdateDescriptorQueue::COMPUTE_FRAME_PAYLOAD_SIZE),
descriptor_buffer_ring(device, memory_allocator),
blit_image(device, scheduler, state_tracker, descriptor_pool), render_pass_cache(device),
indirect_quads_pass(device, scheduler, descriptor_pool, staging_pool,
compute_pass_descriptor_queue),
texture_cache_runtime{
device, scheduler, memory_allocator, staging_pool,
blit_image, render_pass_cache, descriptor_pool, compute_pass_descriptor_queue},
@@ -314,6 +316,18 @@ void RasterizerVulkan::DrawIndirect() {
const auto indirect_buffer = buffer_cache.GetDrawIndirectBuffer();
const auto& buffer = indirect_buffer.first;
const auto& offset = indirect_buffer.second;
VkBuffer command_buffer = buffer->Handle();
VkDeviceSize command_offset = offset;
u32 command_stride = static_cast<u32>(params.stride);
if (params.is_indexed &&
maxwell3d->draw_manager.draw_state.topology == Maxwell::PrimitiveTopology::Quads) {
const auto patched = indirect_quads_pass.Assemble(
static_cast<u32>(params.max_draw_counts), command_stride, command_buffer,
static_cast<u32>(offset));
command_buffer = patched.first;
command_offset = patched.second;
command_stride = IndirectQuadsPass::COMMAND_WORDS * static_cast<u32>(sizeof(u32));
}
if (params.is_byte_count) {
scheduler.Record([buffer_obj = buffer->Handle(), offset,
stride = params.stride](vk::CommandBuffer cmdbuf) {
@@ -326,29 +340,32 @@ void RasterizerVulkan::DrawIndirect() {
const auto count = buffer_cache.GetDrawIndirectCount();
const auto& draw_buffer = count.first;
const auto& offset_base = count.second;
scheduler.Record([draw_buffer_obj = draw_buffer->Handle(),
buffer_obj = buffer->Handle(), offset_base, offset,
scheduler.Record([draw_buffer_obj = draw_buffer->Handle(), command_buffer,
offset_base, command_offset, command_stride,
params](vk::CommandBuffer cmdbuf) {
if (params.is_indexed) {
cmdbuf.DrawIndexedIndirectCount(
buffer_obj, offset, draw_buffer_obj, offset_base,
static_cast<u32>(params.max_draw_counts), static_cast<u32>(params.stride));
cmdbuf.DrawIndexedIndirectCount(command_buffer, command_offset, draw_buffer_obj,
offset_base,
static_cast<u32>(params.max_draw_counts),
command_stride);
} else {
cmdbuf.DrawIndirectCount(buffer_obj, offset, draw_buffer_obj, offset_base,
cmdbuf.DrawIndirectCount(command_buffer, command_offset, draw_buffer_obj,
offset_base,
static_cast<u32>(params.max_draw_counts),
static_cast<u32>(params.stride));
command_stride);
}
});
return;
}
scheduler.Record([buffer_obj = buffer->Handle(), offset, params](vk::CommandBuffer cmdbuf) {
scheduler.Record([command_buffer, command_offset, command_stride,
params](vk::CommandBuffer cmdbuf) {
if (params.is_indexed) {
cmdbuf.DrawIndexedIndirect(buffer_obj, offset,
cmdbuf.DrawIndexedIndirect(command_buffer, command_offset,
static_cast<u32>(params.max_draw_counts),
static_cast<u32>(params.stride));
command_stride);
} else {
cmdbuf.DrawIndirect(buffer_obj, offset, static_cast<u32>(params.max_draw_counts),
static_cast<u32>(params.stride));
cmdbuf.DrawIndirect(command_buffer, command_offset,
static_cast<u32>(params.max_draw_counts), command_stride);
}
});
@@ -17,6 +17,7 @@
#include "video_core/rasterizer_interface.h"
#include "video_core/renderer_vulkan/blit_image.h"
#include "video_core/renderer_vulkan/vk_buffer_cache.h"
#include "video_core/renderer_vulkan/vk_compute_pass.h"
#include "video_core/renderer_vulkan/vk_descriptor_buffer.h"
#include "video_core/renderer_vulkan/vk_descriptor_pool.h"
#include "video_core/renderer_vulkan/vk_fence_manager.h"
@@ -211,6 +212,7 @@ private:
DescriptorBufferRing descriptor_buffer_ring;
BlitImageHelper blit_image;
RenderPassCache render_pass_cache;
IndirectQuadsPass indirect_quads_pass;
TextureCacheRuntime texture_cache_runtime;
TextureCache texture_cache;
@@ -211,7 +211,8 @@ StagingBufferRef StagingBufferPool::CreateStagingBuffer(size_t size, MemoryUsage
.size = 1ULL << log2_size,
.usage = VK_BUFFER_USAGE_TRANSFER_SRC_BIT | VK_BUFFER_USAGE_TRANSFER_DST_BIT |
VK_BUFFER_USAGE_UNIFORM_BUFFER_BIT | VK_BUFFER_USAGE_STORAGE_BUFFER_BIT |
VK_BUFFER_USAGE_INDEX_BUFFER_BIT | VK_BUFFER_USAGE_VERTEX_BUFFER_BIT,
VK_BUFFER_USAGE_INDEX_BUFFER_BIT | VK_BUFFER_USAGE_VERTEX_BUFFER_BIT |
VK_BUFFER_USAGE_INDIRECT_BUFFER_BIT,
.sharingMode = VK_SHARING_MODE_EXCLUSIVE,
.queueFamilyIndexCount = 0,
.pQueueFamilyIndices = nullptr,