mirror of
https://git.eden-emu.dev/eden-emu/eden
synced 2026-04-10 07:38:56 +02:00
[vulkan] Adjust polygon mode handling + topology emulation resolve for line and point with polygon mode
This commit is contained in:
parent
a6c610cf26
commit
83c727794e
9 changed files with 333 additions and 36 deletions
|
|
@ -356,11 +356,23 @@ void BufferCache<P>::BindHostGeometryBuffers(bool is_indexed) {
|
|||
if (is_indexed) {
|
||||
BindHostIndexBuffer();
|
||||
} else if constexpr (!HAS_FULL_INDEX_AND_PRIMITIVE_SUPPORT) {
|
||||
using Maxwell = Tegra::Engines::Maxwell3D::Regs;
|
||||
const auto& regs = maxwell3d->regs;
|
||||
const auto& draw_state = maxwell3d->draw_manager->GetDrawState();
|
||||
const bool allow_polygon_mode_emulation =
|
||||
regs.transform_feedback_enabled == 0 &&
|
||||
!regs.IsShaderConfigEnabled(Maxwell::ShaderType::Geometry) &&
|
||||
!regs.IsShaderConfigEnabled(Maxwell::ShaderType::Tessellation);
|
||||
if (draw_state.topology == Maxwell::PrimitiveTopology::Quads ||
|
||||
draw_state.topology == Maxwell::PrimitiveTopology::QuadStrip) {
|
||||
runtime.BindQuadIndexBuffer(draw_state.topology, draw_state.vertex_buffer.first,
|
||||
draw_state.vertex_buffer.count);
|
||||
const auto polygon_mode = allow_polygon_mode_emulation
|
||||
? regs.polygon_mode_front
|
||||
: Maxwell::PolygonMode::Fill;
|
||||
if (polygon_mode != Maxwell::PolygonMode::Point) {
|
||||
runtime.BindQuadIndexBuffer(draw_state.topology, polygon_mode,
|
||||
draw_state.vertex_buffer.first,
|
||||
draw_state.vertex_buffer.count);
|
||||
}
|
||||
}
|
||||
}
|
||||
BindHostVertexBuffers();
|
||||
|
|
@ -736,6 +748,7 @@ bool BufferCache<P>::IsRegionCpuModified(DAddr addr, size_t size) {
|
|||
|
||||
template <class P>
|
||||
void BufferCache<P>::BindHostIndexBuffer() {
|
||||
using Maxwell = Tegra::Engines::Maxwell3D::Regs;
|
||||
Buffer& buffer = slot_buffers[channel_state->index_buffer.buffer_id];
|
||||
TouchBuffer(buffer, channel_state->index_buffer.buffer_id);
|
||||
const u32 offset = buffer.Offset(channel_state->index_buffer.device_addr);
|
||||
|
|
@ -760,8 +773,35 @@ void BufferCache<P>::BindHostIndexBuffer() {
|
|||
offset + draw_state.index_buffer.first * draw_state.index_buffer.FormatSizeInBytes();
|
||||
runtime.BindIndexBuffer(buffer, new_offset, size);
|
||||
} else {
|
||||
const auto& regs = maxwell3d->regs;
|
||||
const bool allow_polygon_mode_emulation =
|
||||
regs.transform_feedback_enabled == 0 &&
|
||||
!regs.IsShaderConfigEnabled(Maxwell::ShaderType::Geometry) &&
|
||||
!regs.IsShaderConfigEnabled(Maxwell::ShaderType::Tessellation);
|
||||
auto effective_topology = draw_state.topology;
|
||||
auto polygon_mode = allow_polygon_mode_emulation ? regs.polygon_mode_front
|
||||
: Maxwell::PolygonMode::Fill;
|
||||
if (allow_polygon_mode_emulation) {
|
||||
switch (draw_state.topology) {
|
||||
case Maxwell::PrimitiveTopology::Quads:
|
||||
case Maxwell::PrimitiveTopology::QuadStrip:
|
||||
case Maxwell::PrimitiveTopology::Polygon:
|
||||
if (polygon_mode == Maxwell::PolygonMode::Point) {
|
||||
effective_topology = Maxwell::PrimitiveTopology::Points;
|
||||
} else if (draw_state.topology == Maxwell::PrimitiveTopology::Polygon &&
|
||||
polygon_mode == Maxwell::PolygonMode::Line) {
|
||||
effective_topology = Maxwell::PrimitiveTopology::LineStrip;
|
||||
}
|
||||
break;
|
||||
default:
|
||||
polygon_mode = Maxwell::PolygonMode::Fill;
|
||||
break;
|
||||
}
|
||||
} else {
|
||||
polygon_mode = Maxwell::PolygonMode::Fill;
|
||||
}
|
||||
buffer.MarkUsage(offset, size);
|
||||
runtime.BindIndexBuffer(draw_state.topology, draw_state.index_buffer.format,
|
||||
runtime.BindIndexBuffer(effective_topology, polygon_mode, draw_state.index_buffer.format,
|
||||
draw_state.index_buffer.first, draw_state.index_buffer.count,
|
||||
buffer, offset, size);
|
||||
}
|
||||
|
|
|
|||
|
|
@ -315,8 +315,33 @@ VkShaderStageFlagBits ShaderStage(Shader::Stage stage) {
|
|||
return {};
|
||||
}
|
||||
|
||||
[[nodiscard]] bool IsUnsupportedAreaTopology(Maxwell::PrimitiveTopology topology) {
|
||||
switch (topology) {
|
||||
case Maxwell::PrimitiveTopology::Quads:
|
||||
case Maxwell::PrimitiveTopology::QuadStrip:
|
||||
case Maxwell::PrimitiveTopology::Polygon:
|
||||
return true;
|
||||
default:
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
VkPrimitiveTopology PrimitiveTopology([[maybe_unused]] const Device& device,
|
||||
Maxwell::PrimitiveTopology topology) {
|
||||
Maxwell::PrimitiveTopology topology,
|
||||
Maxwell::PolygonMode polygon_mode,
|
||||
bool allow_polygon_mode_emulation) {
|
||||
if (allow_polygon_mode_emulation && IsUnsupportedAreaTopology(topology)) {
|
||||
switch (polygon_mode) {
|
||||
case Maxwell::PolygonMode::Point:
|
||||
return VK_PRIMITIVE_TOPOLOGY_POINT_LIST;
|
||||
case Maxwell::PolygonMode::Line:
|
||||
return topology == Maxwell::PrimitiveTopology::Polygon
|
||||
? VK_PRIMITIVE_TOPOLOGY_LINE_STRIP
|
||||
: VK_PRIMITIVE_TOPOLOGY_LINE_LIST;
|
||||
case Maxwell::PolygonMode::Fill:
|
||||
break;
|
||||
}
|
||||
}
|
||||
switch (topology) {
|
||||
case Maxwell::PrimitiveTopology::Points:
|
||||
return VK_PRIMITIVE_TOPOLOGY_POINT_LIST;
|
||||
|
|
|
|||
|
|
@ -1,4 +1,4 @@
|
|||
// SPDX-FileCopyrightText: Copyright 2025 Eden Emulator Project
|
||||
// SPDX-FileCopyrightText: Copyright 2026 Eden Emulator Project
|
||||
// SPDX-License-Identifier: GPL-3.0-or-later
|
||||
|
||||
// SPDX-FileCopyrightText: Copyright 2019 yuzu Emulator Project
|
||||
|
|
@ -49,7 +49,10 @@ struct FormatInfo {
|
|||
|
||||
VkShaderStageFlagBits ShaderStage(Shader::Stage stage);
|
||||
|
||||
VkPrimitiveTopology PrimitiveTopology(const Device& device, Maxwell::PrimitiveTopology topology);
|
||||
VkPrimitiveTopology PrimitiveTopology(const Device& device, Maxwell::PrimitiveTopology topology,
|
||||
Maxwell::PolygonMode polygon_mode =
|
||||
Maxwell::PolygonMode::Fill,
|
||||
bool allow_polygon_mode_emulation = false);
|
||||
|
||||
VkFormat VertexFormat(const Device& device, Maxwell::VertexAttribute::Type type,
|
||||
Maxwell::VertexAttribute::Size size);
|
||||
|
|
|
|||
|
|
@ -161,10 +161,11 @@ public:
|
|||
index_type = IndexTypeFromNumElements(device, num_indices);
|
||||
|
||||
const u32 num_quads = GetQuadsNum(num_indices);
|
||||
const u32 num_triangle_indices = num_quads * 6;
|
||||
const u32 num_generated_indices = num_quads * GetIndicesPerPrimitive();
|
||||
const u32 num_first_offset_copies = 4;
|
||||
const size_t bytes_per_index = BytesPerIndex(index_type);
|
||||
const size_t size_bytes = num_triangle_indices * bytes_per_index * num_first_offset_copies;
|
||||
const size_t size_bytes =
|
||||
num_generated_indices * bytes_per_index * num_first_offset_copies;
|
||||
const VkBufferCreateInfo buffer_ci = {
|
||||
.sType = VK_STRUCTURE_TYPE_BUFFER_CREATE_INFO,
|
||||
.pNext = nullptr,
|
||||
|
|
@ -189,12 +190,12 @@ public:
|
|||
}();
|
||||
|
||||
u8* staging_data = host_visible ? buffer.Mapped().data() : staging.mapped_span.data();
|
||||
const size_t quad_size = bytes_per_index * 6;
|
||||
const size_t primitive_size = bytes_per_index * GetIndicesPerPrimitive();
|
||||
|
||||
for (u32 first = 0; first < num_first_offset_copies; ++first) {
|
||||
for (u32 quad = 0; quad < num_quads; ++quad) {
|
||||
MakeAndUpdateIndices(staging_data, quad_size, quad, first);
|
||||
staging_data += quad_size;
|
||||
MakeAndUpdateIndices(staging_data, primitive_size, quad, first);
|
||||
staging_data += primitive_size;
|
||||
}
|
||||
}
|
||||
|
||||
|
|
@ -231,13 +232,16 @@ public:
|
|||
const VkIndexType index_type_ = index_type;
|
||||
const size_t sub_first_offset = static_cast<size_t>(first % 4) * GetQuadsNum(num_indices);
|
||||
const size_t offset =
|
||||
(sub_first_offset + GetFirstOffsetQuads(first)) * 6ULL * BytesPerIndex(index_type);
|
||||
(sub_first_offset + GetFirstOffsetQuads(first)) *
|
||||
static_cast<size_t>(GetIndicesPerPrimitive()) * BytesPerIndex(index_type);
|
||||
scheduler.Record([buffer_ = *buffer, index_type_, offset](vk::CommandBuffer cmdbuf) {
|
||||
cmdbuf.BindIndexBuffer(buffer_, offset, index_type_);
|
||||
});
|
||||
}
|
||||
|
||||
protected:
|
||||
virtual u32 GetIndicesPerPrimitive() const = 0;
|
||||
|
||||
virtual u32 GetQuadsNum(u32 num_indices) const = 0;
|
||||
|
||||
virtual u32 GetFirstOffsetQuads(u32 first) const = 0;
|
||||
|
|
@ -264,6 +268,10 @@ public:
|
|||
~QuadArrayIndexBuffer() = default;
|
||||
|
||||
private:
|
||||
u32 GetIndicesPerPrimitive() const override {
|
||||
return 6;
|
||||
}
|
||||
|
||||
u32 GetQuadsNum(u32 num_indices_) const override {
|
||||
return num_indices_ / 4;
|
||||
}
|
||||
|
|
@ -308,6 +316,10 @@ public:
|
|||
~QuadStripIndexBuffer() = default;
|
||||
|
||||
private:
|
||||
u32 GetIndicesPerPrimitive() const override {
|
||||
return 6;
|
||||
}
|
||||
|
||||
u32 GetQuadsNum(u32 num_indices_) const override {
|
||||
return num_indices_ >= 4 ? (num_indices_ - 2) / 2 : 0;
|
||||
}
|
||||
|
|
@ -343,6 +355,104 @@ private:
|
|||
}
|
||||
};
|
||||
|
||||
class QuadArrayLineIndexBuffer : public QuadIndexBuffer {
|
||||
public:
|
||||
QuadArrayLineIndexBuffer(const Device& device_, MemoryAllocator& memory_allocator_,
|
||||
Scheduler& scheduler_, StagingBufferPool& staging_pool_)
|
||||
: QuadIndexBuffer(device_, memory_allocator_, scheduler_, staging_pool_) {}
|
||||
|
||||
~QuadArrayLineIndexBuffer() = default;
|
||||
|
||||
private:
|
||||
u32 GetIndicesPerPrimitive() const override {
|
||||
return 8;
|
||||
}
|
||||
|
||||
u32 GetQuadsNum(u32 num_indices_) const override {
|
||||
return num_indices_ / 4;
|
||||
}
|
||||
|
||||
u32 GetFirstOffsetQuads(u32 first) const override {
|
||||
return first / 4;
|
||||
}
|
||||
|
||||
template <typename T>
|
||||
static std::array<T, 8> MakeIndices(u32 quad, u32 first) {
|
||||
std::array<T, 8> indices{0, 1, 1, 2, 2, 3, 3, 0};
|
||||
for (T& index : indices) {
|
||||
index = static_cast<T>(first + index + quad * 4);
|
||||
}
|
||||
return indices;
|
||||
}
|
||||
|
||||
void MakeAndUpdateIndices(u8* staging_data, size_t primitive_size, u32 quad,
|
||||
u32 first) override {
|
||||
switch (index_type) {
|
||||
case VK_INDEX_TYPE_UINT8_EXT:
|
||||
std::memcpy(staging_data, MakeIndices<u8>(quad, first).data(), primitive_size);
|
||||
break;
|
||||
case VK_INDEX_TYPE_UINT16:
|
||||
std::memcpy(staging_data, MakeIndices<u16>(quad, first).data(), primitive_size);
|
||||
break;
|
||||
case VK_INDEX_TYPE_UINT32:
|
||||
std::memcpy(staging_data, MakeIndices<u32>(quad, first).data(), primitive_size);
|
||||
break;
|
||||
default:
|
||||
ASSERT(false);
|
||||
break;
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
class QuadStripLineIndexBuffer : public QuadIndexBuffer {
|
||||
public:
|
||||
QuadStripLineIndexBuffer(const Device& device_, MemoryAllocator& memory_allocator_,
|
||||
Scheduler& scheduler_, StagingBufferPool& staging_pool_)
|
||||
: QuadIndexBuffer(device_, memory_allocator_, scheduler_, staging_pool_) {}
|
||||
|
||||
~QuadStripLineIndexBuffer() = default;
|
||||
|
||||
private:
|
||||
u32 GetIndicesPerPrimitive() const override {
|
||||
return 8;
|
||||
}
|
||||
|
||||
u32 GetQuadsNum(u32 num_indices_) const override {
|
||||
return num_indices_ >= 4 ? (num_indices_ - 2) / 2 : 0;
|
||||
}
|
||||
|
||||
u32 GetFirstOffsetQuads(u32 first) const override {
|
||||
return (first / 4) * 2;
|
||||
}
|
||||
|
||||
template <typename T>
|
||||
static std::array<T, 8> MakeIndices(u32 quad, u32 first) {
|
||||
std::array<T, 8> indices{0, 1, 1, 3, 3, 2, 2, 0};
|
||||
for (T& index : indices) {
|
||||
index = static_cast<T>(first + index + quad * 2);
|
||||
}
|
||||
return indices;
|
||||
}
|
||||
|
||||
void MakeAndUpdateIndices(u8* staging_data, size_t primitive_size, u32 quad,
|
||||
u32 first) override {
|
||||
switch (index_type) {
|
||||
case VK_INDEX_TYPE_UINT8_EXT:
|
||||
std::memcpy(staging_data, MakeIndices<u8>(quad, first).data(), primitive_size);
|
||||
break;
|
||||
case VK_INDEX_TYPE_UINT16:
|
||||
std::memcpy(staging_data, MakeIndices<u16>(quad, first).data(), primitive_size);
|
||||
break;
|
||||
case VK_INDEX_TYPE_UINT32:
|
||||
std::memcpy(staging_data, MakeIndices<u32>(quad, first).data(), primitive_size);
|
||||
break;
|
||||
default:
|
||||
ASSERT(false);
|
||||
break;
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
BufferCacheRuntime::BufferCacheRuntime(const Device& device_, MemoryAllocator& memory_allocator_,
|
||||
Scheduler& scheduler_, StagingBufferPool& staging_pool_,
|
||||
GuestDescriptorQueue& guest_descriptor_queue_,
|
||||
|
|
@ -366,6 +476,10 @@ BufferCacheRuntime::BufferCacheRuntime(const Device& device_, MemoryAllocator& m
|
|||
scheduler_, staging_pool_);
|
||||
quad_strip_index_buffer = std::make_shared<QuadStripIndexBuffer>(device_, memory_allocator_,
|
||||
scheduler_, staging_pool_);
|
||||
quad_array_line_index_buffer = std::make_shared<QuadArrayLineIndexBuffer>(
|
||||
device_, memory_allocator_, scheduler_, staging_pool_);
|
||||
quad_strip_line_index_buffer = std::make_shared<QuadStripLineIndexBuffer>(
|
||||
device_, memory_allocator_, scheduler_, staging_pool_);
|
||||
}
|
||||
|
||||
StagingBufferRef BufferCacheRuntime::UploadStagingBuffer(size_t size) {
|
||||
|
|
@ -544,12 +658,24 @@ void BufferCacheRuntime::ClearBuffer(VkBuffer dest_buffer, u32 offset, size_t si
|
|||
});
|
||||
}
|
||||
|
||||
void BufferCacheRuntime::BindIndexBuffer(PrimitiveTopology topology, IndexFormat index_format,
|
||||
u32 base_vertex, u32 num_indices, VkBuffer buffer,
|
||||
u32 offset, [[maybe_unused]] u32 size) {
|
||||
void BufferCacheRuntime::BindIndexBuffer(PrimitiveTopology topology, PolygonMode polygon_mode,
|
||||
IndexFormat index_format, u32 base_vertex,
|
||||
u32 num_indices, VkBuffer buffer, u32 offset,
|
||||
[[maybe_unused]] u32 size) {
|
||||
VkIndexType vk_index_type = MaxwellToVK::IndexFormat(index_format);
|
||||
VkDeviceSize vk_offset = offset;
|
||||
VkBuffer vk_buffer = buffer;
|
||||
if (polygon_mode == PolygonMode::Line &&
|
||||
(topology == PrimitiveTopology::Quads || topology == PrimitiveTopology::QuadStrip)) {
|
||||
if (topology == PrimitiveTopology::Quads) {
|
||||
quad_array_line_index_buffer->UpdateBuffer(num_indices);
|
||||
quad_array_line_index_buffer->BindBuffer(base_vertex);
|
||||
} else {
|
||||
quad_strip_line_index_buffer->UpdateBuffer(num_indices);
|
||||
quad_strip_line_index_buffer->BindBuffer(base_vertex);
|
||||
}
|
||||
return;
|
||||
}
|
||||
if (topology == PrimitiveTopology::Quads || topology == PrimitiveTopology::QuadStrip) {
|
||||
vk_index_type = VK_INDEX_TYPE_UINT32;
|
||||
std::tie(vk_buffer, vk_offset) =
|
||||
|
|
@ -575,7 +701,8 @@ void BufferCacheRuntime::BindIndexBuffer(PrimitiveTopology topology, IndexFormat
|
|||
});
|
||||
}
|
||||
|
||||
void BufferCacheRuntime::BindQuadIndexBuffer(PrimitiveTopology topology, u32 first, u32 count) {
|
||||
void BufferCacheRuntime::BindQuadIndexBuffer(PrimitiveTopology topology, PolygonMode polygon_mode,
|
||||
u32 first, u32 count) {
|
||||
if (count == 0) {
|
||||
ReserveNullBuffer();
|
||||
scheduler.Record([this](vk::CommandBuffer cmdbuf) {
|
||||
|
|
@ -584,6 +711,17 @@ void BufferCacheRuntime::BindQuadIndexBuffer(PrimitiveTopology topology, u32 fir
|
|||
return;
|
||||
}
|
||||
|
||||
if (polygon_mode == PolygonMode::Line) {
|
||||
if (topology == PrimitiveTopology::Quads) {
|
||||
quad_array_line_index_buffer->UpdateBuffer(first + count);
|
||||
quad_array_line_index_buffer->BindBuffer(first);
|
||||
} else if (topology == PrimitiveTopology::QuadStrip) {
|
||||
quad_strip_line_index_buffer->UpdateBuffer(first + count);
|
||||
quad_strip_line_index_buffer->BindBuffer(first);
|
||||
}
|
||||
return;
|
||||
}
|
||||
|
||||
if (topology == PrimitiveTopology::Quads) {
|
||||
quad_array_index_buffer->UpdateBuffer(first + count);
|
||||
quad_array_index_buffer->BindBuffer(first);
|
||||
|
|
|
|||
|
|
@ -72,12 +72,15 @@ private:
|
|||
|
||||
class QuadArrayIndexBuffer;
|
||||
class QuadStripIndexBuffer;
|
||||
class QuadArrayLineIndexBuffer;
|
||||
class QuadStripLineIndexBuffer;
|
||||
|
||||
class BufferCacheRuntime {
|
||||
friend Buffer;
|
||||
|
||||
using PrimitiveTopology = Tegra::Engines::Maxwell3D::Regs::PrimitiveTopology;
|
||||
using IndexFormat = Tegra::Engines::Maxwell3D::Regs::IndexFormat;
|
||||
using PolygonMode = Tegra::Engines::Maxwell3D::Regs::PolygonMode;
|
||||
|
||||
public:
|
||||
explicit BufferCacheRuntime(const Device& device_, MemoryAllocator& memory_manager_,
|
||||
|
|
@ -118,10 +121,12 @@ public:
|
|||
|
||||
void ClearBuffer(VkBuffer dest_buffer, u32 offset, size_t size, u32 value);
|
||||
|
||||
void BindIndexBuffer(PrimitiveTopology topology, IndexFormat index_format, u32 num_indices,
|
||||
u32 base_vertex, VkBuffer buffer, u32 offset, u32 size);
|
||||
void BindIndexBuffer(PrimitiveTopology topology, PolygonMode polygon_mode,
|
||||
IndexFormat index_format, u32 base_vertex, u32 num_indices,
|
||||
VkBuffer buffer, u32 offset, u32 size);
|
||||
|
||||
void BindQuadIndexBuffer(PrimitiveTopology topology, u32 first, u32 count);
|
||||
void BindQuadIndexBuffer(PrimitiveTopology topology, PolygonMode polygon_mode, u32 first,
|
||||
u32 count);
|
||||
|
||||
void BindVertexBuffer(u32 index, VkBuffer buffer, u32 offset, u32 size, u32 stride);
|
||||
|
||||
|
|
@ -181,6 +186,8 @@ private:
|
|||
|
||||
std::shared_ptr<QuadArrayIndexBuffer> quad_array_index_buffer;
|
||||
std::shared_ptr<QuadStripIndexBuffer> quad_strip_index_buffer;
|
||||
std::shared_ptr<QuadArrayLineIndexBuffer> quad_array_line_index_buffer;
|
||||
std::shared_ptr<QuadStripLineIndexBuffer> quad_strip_line_index_buffer;
|
||||
|
||||
vk::Buffer null_buffer;
|
||||
|
||||
|
|
|
|||
|
|
@ -662,9 +662,15 @@ void GraphicsPipeline::MakePipeline(VkRenderPass render_pass) {
|
|||
vertex_input_ci.pNext = &input_divisor_ci;
|
||||
}
|
||||
const bool has_tess_stages = spv_modules[1] || spv_modules[2];
|
||||
const bool has_geometry_stage = spv_modules[3];
|
||||
const bool dynamic_topology = key.state.extended_dynamic_state != 0;
|
||||
const bool dynamic_primitive_restart = key.state.extended_dynamic_state_2 != 0;
|
||||
auto exact_input_assembly_topology = MaxwellToVK::PrimitiveTopology(device, key.state.topology);
|
||||
const auto polygon_mode =
|
||||
FixedPipelineState::UnpackPolygonMode(key.state.polygon_mode);
|
||||
const bool allow_polygon_mode_emulation =
|
||||
!has_tess_stages && !has_geometry_stage && key.state.xfb_enabled == 0;
|
||||
auto exact_input_assembly_topology = MaxwellToVK::PrimitiveTopology(
|
||||
device, key.state.topology, polygon_mode, allow_polygon_mode_emulation);
|
||||
if (exact_input_assembly_topology == VK_PRIMITIVE_TOPOLOGY_PATCH_LIST) {
|
||||
if (!has_tess_stages) {
|
||||
LOG_WARNING(Render_Vulkan, "Patch topology used without tessellation, using points");
|
||||
|
|
|
|||
|
|
@ -101,6 +101,9 @@ public:
|
|||
bool HasTessellationStages() const noexcept {
|
||||
return static_cast<bool>(spv_modules[1]) || static_cast<bool>(spv_modules[2]);
|
||||
}
|
||||
bool HasGeometryStage() const noexcept {
|
||||
return static_cast<bool>(spv_modules[3]);
|
||||
}
|
||||
GraphicsPipeline& operator=(GraphicsPipeline&&) noexcept = delete;
|
||||
GraphicsPipeline(GraphicsPipeline&&) noexcept = delete;
|
||||
|
||||
|
|
|
|||
|
|
@ -70,6 +70,47 @@ struct DrawParams {
|
|||
return topology == Maxwell::PrimitiveTopology::LineLoop;
|
||||
}
|
||||
|
||||
[[nodiscard]] bool IsUnsupportedAreaTopology(Maxwell::PrimitiveTopology topology) {
|
||||
switch (topology) {
|
||||
case Maxwell::PrimitiveTopology::Quads:
|
||||
case Maxwell::PrimitiveTopology::QuadStrip:
|
||||
case Maxwell::PrimitiveTopology::Polygon:
|
||||
return true;
|
||||
default:
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
[[nodiscard]] bool AllowPolygonModeTopologyEmulation(const Maxwell& regs,
|
||||
const GraphicsPipeline* pipeline) {
|
||||
if (regs.transform_feedback_enabled != 0 || pipeline == nullptr) {
|
||||
return false;
|
||||
}
|
||||
return !pipeline->HasTessellationStages() && !pipeline->HasGeometryStage();
|
||||
}
|
||||
|
||||
[[nodiscard]] bool NeedsPolygonClosure(const MaxwellDrawState& draw_state, const Maxwell& regs,
|
||||
const GraphicsPipeline* pipeline) {
|
||||
return AllowPolygonModeTopologyEmulation(regs, pipeline) &&
|
||||
draw_state.topology == Maxwell::PrimitiveTopology::Polygon &&
|
||||
regs.polygon_mode_front == Maxwell::PolygonMode::Line;
|
||||
}
|
||||
|
||||
[[nodiscard]] bool NeedsLineClosure(const MaxwellDrawState& draw_state, const Maxwell& regs,
|
||||
const GraphicsPipeline* pipeline) {
|
||||
return IsLineLoop(draw_state.topology) || NeedsPolygonClosure(draw_state, regs, pipeline);
|
||||
}
|
||||
|
||||
[[nodiscard]] Maxwell::PolygonMode EffectivePolygonMode(const MaxwellDrawState& draw_state,
|
||||
const Maxwell& regs,
|
||||
const GraphicsPipeline* pipeline) {
|
||||
if (!AllowPolygonModeTopologyEmulation(regs, pipeline) ||
|
||||
!IsUnsupportedAreaTopology(draw_state.topology)) {
|
||||
return Maxwell::PolygonMode::Fill;
|
||||
}
|
||||
return regs.polygon_mode_front;
|
||||
}
|
||||
|
||||
[[nodiscard]] u32 PrimitiveRestartIndex(Maxwell::IndexFormat format) {
|
||||
switch (format) {
|
||||
case Maxwell::IndexFormat::UnsignedByte:
|
||||
|
|
@ -293,7 +334,8 @@ VkRect2D GetScissorState(const Maxwell& regs, size_t index, u32 up_scale = 1, u3
|
|||
return scissor;
|
||||
}
|
||||
|
||||
DrawParams MakeDrawParams(const MaxwellDrawState& draw_state, u32 num_instances, bool is_indexed) {
|
||||
DrawParams MakeDrawParams(const MaxwellDrawState& draw_state, u32 num_instances, bool is_indexed,
|
||||
Maxwell::PolygonMode polygon_mode) {
|
||||
DrawParams params{
|
||||
.base_instance = draw_state.base_instance,
|
||||
.num_instances = num_instances,
|
||||
|
|
@ -302,13 +344,28 @@ DrawParams MakeDrawParams(const MaxwellDrawState& draw_state, u32 num_instances,
|
|||
.first_index = is_indexed ? draw_state.index_buffer.first : 0,
|
||||
.is_indexed = is_indexed,
|
||||
};
|
||||
if (polygon_mode == Maxwell::PolygonMode::Point) {
|
||||
return params;
|
||||
}
|
||||
// 6 triangle vertices per quad, base vertex is part of the index
|
||||
// See BindQuadIndexBuffer for more details
|
||||
if (draw_state.topology == Maxwell::PrimitiveTopology::Quads) {
|
||||
if (polygon_mode == Maxwell::PolygonMode::Line) {
|
||||
params.num_vertices = (params.num_vertices / 4) * 8;
|
||||
params.base_vertex = 0;
|
||||
params.is_indexed = true;
|
||||
return params;
|
||||
}
|
||||
params.num_vertices = (params.num_vertices / 4) * 6;
|
||||
params.base_vertex = 0;
|
||||
params.is_indexed = true;
|
||||
} else if (draw_state.topology == Maxwell::PrimitiveTopology::QuadStrip) {
|
||||
if (polygon_mode == Maxwell::PolygonMode::Line) {
|
||||
params.num_vertices = params.num_vertices >= 4 ? ((params.num_vertices - 2) / 2) * 8 : 0;
|
||||
params.base_vertex = 0;
|
||||
params.is_indexed = true;
|
||||
return params;
|
||||
}
|
||||
params.num_vertices = (params.num_vertices - 2) / 2 * 6;
|
||||
params.base_vertex = 0;
|
||||
params.is_indexed = true;
|
||||
|
|
@ -421,9 +478,12 @@ void RasterizerVulkan::PrepareDraw(bool is_indexed, Func&& draw_func) {
|
|||
|
||||
void RasterizerVulkan::Draw(bool is_indexed, u32 instance_count) {
|
||||
PrepareDraw(is_indexed, [this, is_indexed, instance_count] {
|
||||
GraphicsPipeline* const pipeline{pipeline_cache.CurrentGraphicsPipeline()};
|
||||
const auto& draw_state = maxwell3d->draw_manager->GetDrawState();
|
||||
const auto polygon_mode = EffectivePolygonMode(draw_state, maxwell3d->regs, pipeline);
|
||||
const u32 num_instances{instance_count};
|
||||
const DrawParams draw_params{MakeDrawParams(draw_state, num_instances, is_indexed)};
|
||||
const DrawParams draw_params{
|
||||
MakeDrawParams(draw_state, num_instances, is_indexed, polygon_mode)};
|
||||
|
||||
if (IsLineLoop(draw_state.topology) && maxwell3d->regs.transform_feedback_enabled == 0) {
|
||||
if (EmulateLineLoopDraw(draw_state, draw_params.base_instance, draw_params.num_instances,
|
||||
|
|
@ -486,7 +546,8 @@ void RasterizerVulkan::Draw(bool is_indexed, u32 instance_count) {
|
|||
is_indexed ? "vkCmdDrawIndexed" : "vkCmdDraw", params, VK_SUCCESS);
|
||||
}
|
||||
|
||||
if (IsLineLoop(draw_state.topology) && draw_params.num_vertices >= 2) {
|
||||
if (NeedsLineClosure(draw_state, maxwell3d->regs, pipeline) &&
|
||||
draw_params.num_vertices >= 2) {
|
||||
if (maxwell3d->regs.transform_feedback_enabled != 0) {
|
||||
query_cache.CounterEnable(VideoCommon::QueryType::StreamingByteCount, false);
|
||||
}
|
||||
|
|
@ -502,6 +563,7 @@ void RasterizerVulkan::DrawIndirect() {
|
|||
const auto& params = maxwell3d->draw_manager->GetIndirectParams();
|
||||
buffer_cache.SetDrawIndirect(¶ms);
|
||||
PrepareDraw(params.is_indexed, [this, ¶ms] {
|
||||
GraphicsPipeline* const pipeline{pipeline_cache.CurrentGraphicsPipeline()};
|
||||
const auto& draw_state = maxwell3d->draw_manager->GetDrawState();
|
||||
if (IsLineLoop(draw_state.topology) &&
|
||||
(maxwell3d->regs.transform_feedback_enabled == 0 || params.is_byte_count)) {
|
||||
|
|
@ -544,7 +606,7 @@ void RasterizerVulkan::DrawIndirect() {
|
|||
static_cast<u32>(params.stride));
|
||||
}
|
||||
});
|
||||
if (IsLineLoop(draw_state.topology)) {
|
||||
if (NeedsLineClosure(draw_state, maxwell3d->regs, pipeline)) {
|
||||
DrawIndirectLineLoopClosures(draw_state, params);
|
||||
}
|
||||
return;
|
||||
|
|
@ -570,7 +632,7 @@ void RasterizerVulkan::DrawIndirect() {
|
|||
log_params, VK_SUCCESS);
|
||||
}
|
||||
|
||||
if (IsLineLoop(draw_state.topology)) {
|
||||
if (NeedsLineClosure(draw_state, maxwell3d->regs, pipeline)) {
|
||||
DrawIndirectLineLoopClosures(draw_state, params);
|
||||
}
|
||||
});
|
||||
|
|
@ -581,7 +643,9 @@ bool RasterizerVulkan::DrawLineLoopClosure(const MaxwellDrawState& draw_state, u
|
|||
u32 num_instances, s32 base_vertex,
|
||||
u32 num_vertices, u32 first_index,
|
||||
bool is_indexed) {
|
||||
if (!IsLineLoop(draw_state.topology) || num_instances == 0 || num_vertices < 2) {
|
||||
GraphicsPipeline* const pipeline{pipeline_cache.CurrentGraphicsPipeline()};
|
||||
if (!NeedsLineClosure(draw_state, maxwell3d->regs, pipeline) || num_instances == 0 ||
|
||||
num_vertices < 2) {
|
||||
return false;
|
||||
}
|
||||
|
||||
|
|
@ -754,7 +818,8 @@ bool RasterizerVulkan::EmulateIndirectLineLoopDraw(
|
|||
|
||||
void RasterizerVulkan::DrawIndirectLineLoopClosures(
|
||||
const MaxwellDrawState& draw_state, const Tegra::Engines::DrawManager::IndirectParams& params) {
|
||||
if (!IsLineLoop(draw_state.topology) || params.is_byte_count) {
|
||||
GraphicsPipeline* const pipeline{pipeline_cache.CurrentGraphicsPipeline()};
|
||||
if (!NeedsLineClosure(draw_state, maxwell3d->regs, pipeline) || params.is_byte_count) {
|
||||
return;
|
||||
}
|
||||
|
||||
|
|
@ -1880,8 +1945,14 @@ void RasterizerVulkan::UpdateLineStipple(Tegra::Engines::Maxwell3D::Regs& regs)
|
|||
return;
|
||||
}
|
||||
|
||||
const auto topology = maxwell3d->draw_manager->GetDrawState().topology;
|
||||
if (!IsLineRasterizationTopology(device, topology)) {
|
||||
GraphicsPipeline* const pipeline = pipeline_cache.CurrentGraphicsPipeline();
|
||||
const auto& draw_state = maxwell3d->draw_manager->GetDrawState();
|
||||
const auto effective_polygon_mode = EffectivePolygonMode(draw_state, regs, pipeline);
|
||||
const auto vk_topology = MaxwellToVK::PrimitiveTopology(
|
||||
device, draw_state.topology, effective_polygon_mode,
|
||||
AllowPolygonModeTopologyEmulation(regs, pipeline));
|
||||
if (vk_topology != VK_PRIMITIVE_TOPOLOGY_LINE_LIST &&
|
||||
vk_topology != VK_PRIMITIVE_TOPOLOGY_LINE_STRIP) {
|
||||
return;
|
||||
}
|
||||
|
||||
|
|
@ -2006,12 +2077,16 @@ void RasterizerVulkan::UpdateDepthCompareOp(Tegra::Engines::Maxwell3D::Regs& reg
|
|||
void RasterizerVulkan::UpdatePrimitiveTopology([[maybe_unused]] Tegra::Engines::Maxwell3D::Regs& regs) {
|
||||
GraphicsPipeline* pipeline = pipeline_cache.CurrentGraphicsPipeline();
|
||||
const auto topology = maxwell3d->draw_manager->GetDrawState().topology;
|
||||
if (!state_tracker.ChangePrimitiveTopology(topology)) {
|
||||
return;
|
||||
}
|
||||
const auto polygon_mode =
|
||||
EffectivePolygonMode(maxwell3d->draw_manager->GetDrawState(), regs, pipeline);
|
||||
const auto vk_topology = pipeline && pipeline->HasTessellationStages()
|
||||
? VK_PRIMITIVE_TOPOLOGY_PATCH_LIST
|
||||
: MaxwellToVK::PrimitiveTopology(device, topology);
|
||||
: MaxwellToVK::PrimitiveTopology(
|
||||
device, topology, polygon_mode,
|
||||
AllowPolygonModeTopologyEmulation(regs, pipeline));
|
||||
if (!state_tracker.ChangePrimitiveTopology(static_cast<u32>(vk_topology))) {
|
||||
return;
|
||||
}
|
||||
scheduler.Record([vk_topology](vk::CommandBuffer cmdbuf) {
|
||||
cmdbuf.SetPrimitiveTopologyEXT(vk_topology);
|
||||
});
|
||||
|
|
|
|||
|
|
@ -271,7 +271,7 @@ public:
|
|||
return Exchange(Dirty::LogicOp, false);
|
||||
}
|
||||
|
||||
bool ChangePrimitiveTopology(Maxwell::PrimitiveTopology new_topology) {
|
||||
bool ChangePrimitiveTopology(u32 new_topology) {
|
||||
const bool has_changed = current_topology != new_topology;
|
||||
current_topology = new_topology;
|
||||
return has_changed;
|
||||
|
|
@ -284,7 +284,7 @@ public:
|
|||
void InvalidateState();
|
||||
|
||||
private:
|
||||
static constexpr auto INVALID_TOPOLOGY = static_cast<Maxwell::PrimitiveTopology>(~0u);
|
||||
static constexpr u32 INVALID_TOPOLOGY = ~0u;
|
||||
|
||||
bool Exchange(std::size_t id, bool new_value) const noexcept {
|
||||
const bool is_dirty = (*flags)[id];
|
||||
|
|
@ -301,7 +301,7 @@ private:
|
|||
Tegra::Engines::Maxwell3D::DirtyState::Flags* flags;
|
||||
Tegra::Engines::Maxwell3D::DirtyState::Flags default_flags;
|
||||
Tegra::Engines::Maxwell3D::DirtyState::Flags invalidation_flags;
|
||||
Maxwell::PrimitiveTopology current_topology = INVALID_TOPOLOGY;
|
||||
u32 current_topology = INVALID_TOPOLOGY;
|
||||
bool two_sided_stencil = false;
|
||||
StencilProperties front{};
|
||||
StencilProperties back{};
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue