mirror of
https://github.com/shadps4-emu/shadPS4.git
synced 2025-05-31 15:53:17 +00:00
Initial support of Geometry shaders (#1244)
* video_core: initial GS support * fix for components mapping; missing prim type
This commit is contained in:
parent
5bb45dc7ba
commit
927bb0c175
40 changed files with 944 additions and 268 deletions
|
@ -19,6 +19,7 @@
|
|||
#include "common/types.h"
|
||||
#include "common/unique_function.h"
|
||||
#include "shader_recompiler/params.h"
|
||||
#include "types.h"
|
||||
#include "video_core/amdgpu/pixel_format.h"
|
||||
#include "video_core/amdgpu/resource.h"
|
||||
|
||||
|
@ -842,26 +843,6 @@ struct Liverpool {
|
|||
}
|
||||
};
|
||||
|
||||
enum class PrimitiveType : u32 {
|
||||
None = 0,
|
||||
PointList = 1,
|
||||
LineList = 2,
|
||||
LineStrip = 3,
|
||||
TriangleList = 4,
|
||||
TriangleFan = 5,
|
||||
TriangleStrip = 6,
|
||||
PatchPrimitive = 9,
|
||||
AdjLineList = 10,
|
||||
AdjLineStrip = 11,
|
||||
AdjTriangleList = 12,
|
||||
AdjTriangleStrip = 13,
|
||||
RectList = 17,
|
||||
LineLoop = 18,
|
||||
QuadList = 19,
|
||||
QuadStrip = 20,
|
||||
Polygon = 21,
|
||||
};
|
||||
|
||||
enum ContextRegs : u32 {
|
||||
DbZInfo = 0xA010,
|
||||
CbColor0Base = 0xA318,
|
||||
|
@ -936,7 +917,12 @@ struct Liverpool {
|
|||
};
|
||||
|
||||
union ShaderStageEnable {
|
||||
u32 raw;
|
||||
enum VgtStages : u32 {
|
||||
Vs = 0u, // always enabled
|
||||
EsGs = 0xB0u,
|
||||
};
|
||||
|
||||
VgtStages raw;
|
||||
BitField<0, 2, u32> ls_en;
|
||||
BitField<2, 1, u32> hs_en;
|
||||
BitField<3, 2, u32> es_en;
|
||||
|
@ -962,6 +948,81 @@ struct Liverpool {
|
|||
}
|
||||
};
|
||||
|
||||
union GsInstances {
|
||||
u32 raw;
|
||||
struct {
|
||||
u32 enable : 2;
|
||||
u32 count : 6;
|
||||
};
|
||||
|
||||
bool IsEnabled() const {
|
||||
return enable && count > 0;
|
||||
}
|
||||
};
|
||||
|
||||
union GsOutPrimitiveType {
|
||||
u32 raw;
|
||||
struct {
|
||||
GsOutputPrimitiveType outprim_type : 6;
|
||||
GsOutputPrimitiveType outprim_type1 : 6;
|
||||
GsOutputPrimitiveType outprim_type2 : 6;
|
||||
GsOutputPrimitiveType outprim_type3 : 6;
|
||||
u32 reserved : 3;
|
||||
u32 unique_type_per_stream : 1;
|
||||
};
|
||||
|
||||
GsOutputPrimitiveType GetPrimitiveType(u32 stream) const {
|
||||
if (unique_type_per_stream == 0) {
|
||||
return outprim_type;
|
||||
}
|
||||
|
||||
switch (stream) {
|
||||
case 0:
|
||||
return outprim_type;
|
||||
case 1:
|
||||
return outprim_type1;
|
||||
case 2:
|
||||
return outprim_type2;
|
||||
case 3:
|
||||
return outprim_type3;
|
||||
default:
|
||||
UNREACHABLE();
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
union GsMode {
|
||||
u32 raw;
|
||||
BitField<0, 3, u32> mode;
|
||||
BitField<3, 2, u32> cut_mode;
|
||||
BitField<22, 2, u32> onchip;
|
||||
};
|
||||
|
||||
union StreamOutConfig {
|
||||
u32 raw;
|
||||
struct {
|
||||
u32 streamout_0_en : 1;
|
||||
u32 streamout_1_en : 1;
|
||||
u32 streamout_2_en : 1;
|
||||
u32 streamout_3_en : 1;
|
||||
u32 rast_stream : 3;
|
||||
u32 : 1;
|
||||
u32 rast_stream_mask : 4;
|
||||
u32 : 19;
|
||||
u32 use_rast_stream_mask : 1;
|
||||
};
|
||||
};
|
||||
|
||||
union StreamOutBufferConfig {
|
||||
u32 raw;
|
||||
struct {
|
||||
u32 stream_0_buf_en : 4;
|
||||
u32 stream_1_buf_en : 4;
|
||||
u32 stream_2_buf_en : 4;
|
||||
u32 stream_3_buf_en : 4;
|
||||
};
|
||||
};
|
||||
|
||||
union Eqaa {
|
||||
u32 raw;
|
||||
BitField<0, 1, u32> max_anchor_samples;
|
||||
|
@ -1053,9 +1114,13 @@ struct Liverpool {
|
|||
PolygonControl polygon_control;
|
||||
ViewportControl viewport_control;
|
||||
VsOutputControl vs_output_control;
|
||||
INSERT_PADDING_WORDS(0xA292 - 0xA207 - 1);
|
||||
INSERT_PADDING_WORDS(0xA290 - 0xA207 - 1);
|
||||
GsMode vgt_gs_mode;
|
||||
INSERT_PADDING_WORDS(1);
|
||||
ModeControl mode_control;
|
||||
INSERT_PADDING_WORDS(0xA29D - 0xA292 - 1);
|
||||
INSERT_PADDING_WORDS(8);
|
||||
GsOutPrimitiveType vgt_gs_out_prim_type;
|
||||
INSERT_PADDING_WORDS(1);
|
||||
u32 index_size;
|
||||
u32 max_index_size;
|
||||
IndexBufferType index_buffer_type;
|
||||
|
@ -1066,11 +1131,21 @@ struct Liverpool {
|
|||
INSERT_PADDING_WORDS(0xA2A8 - 0xA2A5 - 1);
|
||||
u32 vgt_instance_step_rate_0;
|
||||
u32 vgt_instance_step_rate_1;
|
||||
INSERT_PADDING_WORDS(0xA2D5 - 0xA2A9 - 1);
|
||||
INSERT_PADDING_WORDS(0xA2AB - 0xA2A9 - 1);
|
||||
u32 vgt_esgs_ring_itemsize;
|
||||
u32 vgt_gsvs_ring_itemsize;
|
||||
INSERT_PADDING_WORDS(0xA2CE - 0xA2AC - 1);
|
||||
BitField<0, 11, u32> vgt_gs_max_vert_out;
|
||||
INSERT_PADDING_WORDS(0xA2D5 - 0xA2CE - 1);
|
||||
ShaderStageEnable stage_enable;
|
||||
INSERT_PADDING_WORDS(9);
|
||||
INSERT_PADDING_WORDS(1);
|
||||
u32 vgt_gs_vert_itemsize[4];
|
||||
INSERT_PADDING_WORDS(4);
|
||||
PolygonOffset poly_offset;
|
||||
INSERT_PADDING_WORDS(0xA2F8 - 0xA2DF - 5);
|
||||
GsInstances vgt_gs_instance_cnt;
|
||||
StreamOutConfig vgt_strmout_config;
|
||||
StreamOutBufferConfig vgt_strmout_buffer_config;
|
||||
INSERT_PADDING_WORDS(0xA2F8 - 0xA2E6 - 1);
|
||||
AaConfig aa_config;
|
||||
INSERT_PADDING_WORDS(0xA318 - 0xA2F8 - 1);
|
||||
ColorBuffer color_buffers[NumColorBuffers];
|
||||
|
@ -1291,15 +1366,24 @@ static_assert(GFX6_3D_REG_INDEX(color_control) == 0xA202);
|
|||
static_assert(GFX6_3D_REG_INDEX(clipper_control) == 0xA204);
|
||||
static_assert(GFX6_3D_REG_INDEX(viewport_control) == 0xA206);
|
||||
static_assert(GFX6_3D_REG_INDEX(vs_output_control) == 0xA207);
|
||||
static_assert(GFX6_3D_REG_INDEX(vgt_gs_mode) == 0xA290);
|
||||
static_assert(GFX6_3D_REG_INDEX(mode_control) == 0xA292);
|
||||
static_assert(GFX6_3D_REG_INDEX(vgt_gs_out_prim_type) == 0xA29B);
|
||||
static_assert(GFX6_3D_REG_INDEX(index_size) == 0xA29D);
|
||||
static_assert(GFX6_3D_REG_INDEX(index_buffer_type) == 0xA29F);
|
||||
static_assert(GFX6_3D_REG_INDEX(enable_primitive_id) == 0xA2A1);
|
||||
static_assert(GFX6_3D_REG_INDEX(enable_primitive_restart) == 0xA2A5);
|
||||
static_assert(GFX6_3D_REG_INDEX(vgt_instance_step_rate_0) == 0xA2A8);
|
||||
static_assert(GFX6_3D_REG_INDEX(vgt_instance_step_rate_1) == 0xA2A9);
|
||||
static_assert(GFX6_3D_REG_INDEX(vgt_esgs_ring_itemsize) == 0xA2AB);
|
||||
static_assert(GFX6_3D_REG_INDEX(vgt_gsvs_ring_itemsize) == 0xA2AC);
|
||||
static_assert(GFX6_3D_REG_INDEX(vgt_gs_max_vert_out) == 0xA2CE);
|
||||
static_assert(GFX6_3D_REG_INDEX(stage_enable) == 0xA2D5);
|
||||
static_assert(GFX6_3D_REG_INDEX(vgt_gs_vert_itemsize[0]) == 0xA2D7);
|
||||
static_assert(GFX6_3D_REG_INDEX(poly_offset) == 0xA2DF);
|
||||
static_assert(GFX6_3D_REG_INDEX(vgt_gs_instance_cnt) == 0xA2E4);
|
||||
static_assert(GFX6_3D_REG_INDEX(vgt_strmout_config) == 0xA2E5);
|
||||
static_assert(GFX6_3D_REG_INDEX(vgt_strmout_buffer_config) == 0xA2E6);
|
||||
static_assert(GFX6_3D_REG_INDEX(aa_config) == 0xA2F8);
|
||||
static_assert(GFX6_3D_REG_INDEX(color_buffers[0].base_address) == 0xA318);
|
||||
static_assert(GFX6_3D_REG_INDEX(color_buffers[0].pitch) == 0xA319);
|
||||
|
|
|
@ -6,78 +6,10 @@
|
|||
#include <string_view>
|
||||
#include <fmt/format.h>
|
||||
#include "common/types.h"
|
||||
#include "video_core/amdgpu/types.h"
|
||||
|
||||
namespace AmdGpu {
|
||||
|
||||
// Table 8.13 Data and Image Formats [Sea Islands Series Instruction Set Architecture]
|
||||
enum class DataFormat : u32 {
|
||||
FormatInvalid = 0,
|
||||
Format8 = 1,
|
||||
Format16 = 2,
|
||||
Format8_8 = 3,
|
||||
Format32 = 4,
|
||||
Format16_16 = 5,
|
||||
Format10_11_11 = 6,
|
||||
Format11_11_10 = 7,
|
||||
Format10_10_10_2 = 8,
|
||||
Format2_10_10_10 = 9,
|
||||
Format8_8_8_8 = 10,
|
||||
Format32_32 = 11,
|
||||
Format16_16_16_16 = 12,
|
||||
Format32_32_32 = 13,
|
||||
Format32_32_32_32 = 14,
|
||||
Format5_6_5 = 16,
|
||||
Format1_5_5_5 = 17,
|
||||
Format5_5_5_1 = 18,
|
||||
Format4_4_4_4 = 19,
|
||||
Format8_24 = 20,
|
||||
Format24_8 = 21,
|
||||
FormatX24_8_32 = 22,
|
||||
FormatGB_GR = 32,
|
||||
FormatBG_RG = 33,
|
||||
Format5_9_9_9 = 34,
|
||||
FormatBc1 = 35,
|
||||
FormatBc2 = 36,
|
||||
FormatBc3 = 37,
|
||||
FormatBc4 = 38,
|
||||
FormatBc5 = 39,
|
||||
FormatBc6 = 40,
|
||||
FormatBc7 = 41,
|
||||
FormatFmask8_1 = 47,
|
||||
FormatFmask8_2 = 48,
|
||||
FormatFmask8_4 = 49,
|
||||
FormatFmask16_1 = 50,
|
||||
FormatFmask16_2 = 51,
|
||||
FormatFmask32_2 = 52,
|
||||
FormatFmask32_4 = 53,
|
||||
FormatFmask32_8 = 54,
|
||||
FormatFmask64_4 = 55,
|
||||
FormatFmask64_8 = 56,
|
||||
Format4_4 = 57,
|
||||
Format6_5_5 = 58,
|
||||
Format1 = 59,
|
||||
Format1_Reversed = 60,
|
||||
Format32_As_8 = 61,
|
||||
Format32_As_8_8 = 62,
|
||||
Format32_As_32_32_32_32 = 63,
|
||||
};
|
||||
|
||||
enum class NumberFormat : u32 {
|
||||
Unorm = 0,
|
||||
Snorm = 1,
|
||||
Uscaled = 2,
|
||||
Sscaled = 3,
|
||||
Uint = 4,
|
||||
Sint = 5,
|
||||
SnormNz = 6,
|
||||
Float = 7,
|
||||
Srgb = 9,
|
||||
Ubnorm = 10,
|
||||
UbnromNz = 11,
|
||||
Ubint = 12,
|
||||
Ubscaled = 13,
|
||||
};
|
||||
|
||||
[[nodiscard]] constexpr bool IsInteger(NumberFormat nfmt) {
|
||||
return nfmt == AmdGpu::NumberFormat::Sint || nfmt == AmdGpu::NumberFormat::Uint;
|
||||
}
|
||||
|
|
106
src/video_core/amdgpu/types.h
Normal file
106
src/video_core/amdgpu/types.h
Normal file
|
@ -0,0 +1,106 @@
|
|||
// SPDX-FileCopyrightText: Copyright 2024 shadPS4 Emulator Project
|
||||
// SPDX-License-Identifier: GPL-2.0-or-later
|
||||
|
||||
#pragma once
|
||||
|
||||
#include "common/types.h"
|
||||
|
||||
namespace AmdGpu {
|
||||
|
||||
// See `VGT_PRIMITIVE_TYPE` description in [Radeon Sea Islands 3D/Compute Register Reference Guide]
|
||||
enum class PrimitiveType : u32 {
|
||||
None = 0,
|
||||
PointList = 1,
|
||||
LineList = 2,
|
||||
LineStrip = 3,
|
||||
TriangleList = 4,
|
||||
TriangleFan = 5,
|
||||
TriangleStrip = 6,
|
||||
PatchPrimitive = 9,
|
||||
AdjLineList = 10,
|
||||
AdjLineStrip = 11,
|
||||
AdjTriangleList = 12,
|
||||
AdjTriangleStrip = 13,
|
||||
RectList = 17,
|
||||
LineLoop = 18,
|
||||
QuadList = 19,
|
||||
QuadStrip = 20,
|
||||
Polygon = 21,
|
||||
};
|
||||
|
||||
enum class GsOutputPrimitiveType : u32 {
|
||||
PointList = 0,
|
||||
LineStrip = 1,
|
||||
TriangleStrip = 2,
|
||||
};
|
||||
|
||||
// Table 8.13 Data and Image Formats [Sea Islands Series Instruction Set Architecture]
|
||||
enum class DataFormat : u32 {
|
||||
FormatInvalid = 0,
|
||||
Format8 = 1,
|
||||
Format16 = 2,
|
||||
Format8_8 = 3,
|
||||
Format32 = 4,
|
||||
Format16_16 = 5,
|
||||
Format10_11_11 = 6,
|
||||
Format11_11_10 = 7,
|
||||
Format10_10_10_2 = 8,
|
||||
Format2_10_10_10 = 9,
|
||||
Format8_8_8_8 = 10,
|
||||
Format32_32 = 11,
|
||||
Format16_16_16_16 = 12,
|
||||
Format32_32_32 = 13,
|
||||
Format32_32_32_32 = 14,
|
||||
Format5_6_5 = 16,
|
||||
Format1_5_5_5 = 17,
|
||||
Format5_5_5_1 = 18,
|
||||
Format4_4_4_4 = 19,
|
||||
Format8_24 = 20,
|
||||
Format24_8 = 21,
|
||||
FormatX24_8_32 = 22,
|
||||
FormatGB_GR = 32,
|
||||
FormatBG_RG = 33,
|
||||
Format5_9_9_9 = 34,
|
||||
FormatBc1 = 35,
|
||||
FormatBc2 = 36,
|
||||
FormatBc3 = 37,
|
||||
FormatBc4 = 38,
|
||||
FormatBc5 = 39,
|
||||
FormatBc6 = 40,
|
||||
FormatBc7 = 41,
|
||||
FormatFmask8_1 = 47,
|
||||
FormatFmask8_2 = 48,
|
||||
FormatFmask8_4 = 49,
|
||||
FormatFmask16_1 = 50,
|
||||
FormatFmask16_2 = 51,
|
||||
FormatFmask32_2 = 52,
|
||||
FormatFmask32_4 = 53,
|
||||
FormatFmask32_8 = 54,
|
||||
FormatFmask64_4 = 55,
|
||||
FormatFmask64_8 = 56,
|
||||
Format4_4 = 57,
|
||||
Format6_5_5 = 58,
|
||||
Format1 = 59,
|
||||
Format1_Reversed = 60,
|
||||
Format32_As_8 = 61,
|
||||
Format32_As_8_8 = 62,
|
||||
Format32_As_32_32_32_32 = 63,
|
||||
};
|
||||
|
||||
enum class NumberFormat : u32 {
|
||||
Unorm = 0,
|
||||
Snorm = 1,
|
||||
Uscaled = 2,
|
||||
Sscaled = 3,
|
||||
Uint = 4,
|
||||
Sint = 5,
|
||||
SnormNz = 6,
|
||||
Float = 7,
|
||||
Srgb = 9,
|
||||
Ubnorm = 10,
|
||||
UbnromNz = 11,
|
||||
Ubint = 12,
|
||||
Ubscaled = 13,
|
||||
};
|
||||
|
||||
} // namespace AmdGpu
|
|
@ -224,7 +224,7 @@ bool BufferCache::BindVertexBuffers(const Shader::Info& vs_info) {
|
|||
u32 BufferCache::BindIndexBuffer(bool& is_indexed, u32 index_offset) {
|
||||
// Emulate QuadList primitive type with CPU made index buffer.
|
||||
const auto& regs = liverpool->regs;
|
||||
if (regs.primitive_type == AmdGpu::Liverpool::PrimitiveType::QuadList) {
|
||||
if (regs.primitive_type == AmdGpu::PrimitiveType::QuadList) {
|
||||
is_indexed = true;
|
||||
|
||||
// Emit indices.
|
||||
|
|
|
@ -61,34 +61,34 @@ vk::CompareOp CompareOp(Liverpool::CompareFunc func) {
|
|||
}
|
||||
}
|
||||
|
||||
vk::PrimitiveTopology PrimitiveType(Liverpool::PrimitiveType type) {
|
||||
vk::PrimitiveTopology PrimitiveType(AmdGpu::PrimitiveType type) {
|
||||
switch (type) {
|
||||
case Liverpool::PrimitiveType::PointList:
|
||||
case AmdGpu::PrimitiveType::PointList:
|
||||
return vk::PrimitiveTopology::ePointList;
|
||||
case Liverpool::PrimitiveType::LineList:
|
||||
case AmdGpu::PrimitiveType::LineList:
|
||||
return vk::PrimitiveTopology::eLineList;
|
||||
case Liverpool::PrimitiveType::LineStrip:
|
||||
case AmdGpu::PrimitiveType::LineStrip:
|
||||
return vk::PrimitiveTopology::eLineStrip;
|
||||
case Liverpool::PrimitiveType::TriangleList:
|
||||
case AmdGpu::PrimitiveType::TriangleList:
|
||||
return vk::PrimitiveTopology::eTriangleList;
|
||||
case Liverpool::PrimitiveType::TriangleFan:
|
||||
case AmdGpu::PrimitiveType::TriangleFan:
|
||||
return vk::PrimitiveTopology::eTriangleFan;
|
||||
case Liverpool::PrimitiveType::TriangleStrip:
|
||||
case AmdGpu::PrimitiveType::TriangleStrip:
|
||||
return vk::PrimitiveTopology::eTriangleStrip;
|
||||
case Liverpool::PrimitiveType::AdjLineList:
|
||||
case AmdGpu::PrimitiveType::AdjLineList:
|
||||
return vk::PrimitiveTopology::eLineListWithAdjacency;
|
||||
case Liverpool::PrimitiveType::AdjLineStrip:
|
||||
case AmdGpu::PrimitiveType::AdjLineStrip:
|
||||
return vk::PrimitiveTopology::eLineStripWithAdjacency;
|
||||
case Liverpool::PrimitiveType::AdjTriangleList:
|
||||
case AmdGpu::PrimitiveType::AdjTriangleList:
|
||||
return vk::PrimitiveTopology::eTriangleListWithAdjacency;
|
||||
case Liverpool::PrimitiveType::AdjTriangleStrip:
|
||||
case AmdGpu::PrimitiveType::AdjTriangleStrip:
|
||||
return vk::PrimitiveTopology::eTriangleStripWithAdjacency;
|
||||
case Liverpool::PrimitiveType::PatchPrimitive:
|
||||
case AmdGpu::PrimitiveType::PatchPrimitive:
|
||||
return vk::PrimitiveTopology::ePatchList;
|
||||
case Liverpool::PrimitiveType::QuadList:
|
||||
case AmdGpu::PrimitiveType::QuadList:
|
||||
// Needs to generate index buffer on the fly.
|
||||
return vk::PrimitiveTopology::eTriangleList;
|
||||
case Liverpool::PrimitiveType::RectList:
|
||||
case AmdGpu::PrimitiveType::RectList:
|
||||
return vk::PrimitiveTopology::eTriangleStrip;
|
||||
default:
|
||||
UNREACHABLE();
|
||||
|
|
|
@ -18,7 +18,7 @@ vk::StencilOp StencilOp(Liverpool::StencilFunc op);
|
|||
|
||||
vk::CompareOp CompareOp(Liverpool::CompareFunc func);
|
||||
|
||||
vk::PrimitiveTopology PrimitiveType(Liverpool::PrimitiveType type);
|
||||
vk::PrimitiveTopology PrimitiveType(AmdGpu::PrimitiveType type);
|
||||
|
||||
vk::PolygonMode PolygonMode(Liverpool::PolygonMode mode);
|
||||
|
||||
|
|
|
@ -16,6 +16,10 @@
|
|||
|
||||
namespace Vulkan {
|
||||
|
||||
static constexpr auto gp_stage_flags = vk::ShaderStageFlagBits::eVertex |
|
||||
vk::ShaderStageFlagBits::eGeometry |
|
||||
vk::ShaderStageFlagBits::eFragment;
|
||||
|
||||
GraphicsPipeline::GraphicsPipeline(const Instance& instance_, Scheduler& scheduler_,
|
||||
DescriptorHeap& desc_heap_, const GraphicsPipelineKey& key_,
|
||||
vk::PipelineCache pipeline_cache,
|
||||
|
@ -27,7 +31,7 @@ GraphicsPipeline::GraphicsPipeline(const Instance& instance_, Scheduler& schedul
|
|||
BuildDescSetLayout();
|
||||
|
||||
const vk::PushConstantRange push_constants = {
|
||||
.stageFlags = vk::ShaderStageFlagBits::eVertex | vk::ShaderStageFlagBits::eFragment,
|
||||
.stageFlags = gp_stage_flags,
|
||||
.offset = 0,
|
||||
.size = sizeof(Shader::PushData),
|
||||
};
|
||||
|
@ -83,7 +87,7 @@ GraphicsPipeline::GraphicsPipeline(const Instance& instance_, Scheduler& schedul
|
|||
.pVertexAttributeDescriptions = vertex_attributes.data(),
|
||||
};
|
||||
|
||||
if (key.prim_type == Liverpool::PrimitiveType::RectList && !IsEmbeddedVs()) {
|
||||
if (key.prim_type == AmdGpu::PrimitiveType::RectList && !IsEmbeddedVs()) {
|
||||
LOG_WARNING(Render_Vulkan,
|
||||
"Rectangle List primitive type is only supported for embedded VS");
|
||||
}
|
||||
|
@ -196,9 +200,9 @@ GraphicsPipeline::GraphicsPipeline(const Instance& instance_, Scheduler& schedul
|
|||
},
|
||||
};
|
||||
|
||||
auto stage = u32(Shader::Stage::Vertex);
|
||||
boost::container::static_vector<vk::PipelineShaderStageCreateInfo, MaxShaderStages>
|
||||
shader_stages;
|
||||
auto stage = u32(Shader::Stage::Vertex);
|
||||
if (infos[stage]) {
|
||||
shader_stages.emplace_back(vk::PipelineShaderStageCreateInfo{
|
||||
.stage = vk::ShaderStageFlagBits::eVertex,
|
||||
|
@ -206,6 +210,14 @@ GraphicsPipeline::GraphicsPipeline(const Instance& instance_, Scheduler& schedul
|
|||
.pName = "main",
|
||||
});
|
||||
}
|
||||
stage = u32(Shader::Stage::Geometry);
|
||||
if (infos[stage]) {
|
||||
shader_stages.emplace_back(vk::PipelineShaderStageCreateInfo{
|
||||
.stage = vk::ShaderStageFlagBits::eGeometry,
|
||||
.module = modules[stage],
|
||||
.pName = "main",
|
||||
});
|
||||
}
|
||||
stage = u32(Shader::Stage::Fragment);
|
||||
if (infos[stage]) {
|
||||
shader_stages.emplace_back(vk::PipelineShaderStageCreateInfo{
|
||||
|
@ -322,7 +334,7 @@ void GraphicsPipeline::BuildDescSetLayout() {
|
|||
.descriptorType = buffer.IsStorage(sharp) ? vk::DescriptorType::eStorageBuffer
|
||||
: vk::DescriptorType::eUniformBuffer,
|
||||
.descriptorCount = 1,
|
||||
.stageFlags = vk::ShaderStageFlagBits::eVertex | vk::ShaderStageFlagBits::eFragment,
|
||||
.stageFlags = gp_stage_flags,
|
||||
});
|
||||
}
|
||||
for (const auto& tex_buffer : stage->texture_buffers) {
|
||||
|
@ -331,7 +343,7 @@ void GraphicsPipeline::BuildDescSetLayout() {
|
|||
.descriptorType = tex_buffer.is_written ? vk::DescriptorType::eStorageTexelBuffer
|
||||
: vk::DescriptorType::eUniformTexelBuffer,
|
||||
.descriptorCount = 1,
|
||||
.stageFlags = vk::ShaderStageFlagBits::eVertex | vk::ShaderStageFlagBits::eFragment,
|
||||
.stageFlags = gp_stage_flags,
|
||||
});
|
||||
}
|
||||
for (const auto& image : stage->images) {
|
||||
|
@ -340,7 +352,7 @@ void GraphicsPipeline::BuildDescSetLayout() {
|
|||
.descriptorType = image.is_storage ? vk::DescriptorType::eStorageImage
|
||||
: vk::DescriptorType::eSampledImage,
|
||||
.descriptorCount = 1,
|
||||
.stageFlags = vk::ShaderStageFlagBits::eVertex | vk::ShaderStageFlagBits::eFragment,
|
||||
.stageFlags = gp_stage_flags,
|
||||
});
|
||||
}
|
||||
for (const auto& sampler : stage->samplers) {
|
||||
|
@ -348,7 +360,7 @@ void GraphicsPipeline::BuildDescSetLayout() {
|
|||
.binding = binding++,
|
||||
.descriptorType = vk::DescriptorType::eSampler,
|
||||
.descriptorCount = 1,
|
||||
.stageFlags = vk::ShaderStageFlagBits::eVertex | vk::ShaderStageFlagBits::eFragment,
|
||||
.stageFlags = gp_stage_flags,
|
||||
});
|
||||
}
|
||||
}
|
||||
|
@ -518,9 +530,7 @@ void GraphicsPipeline::BindResources(const Liverpool::Regs& regs,
|
|||
desc_set, {});
|
||||
}
|
||||
}
|
||||
cmdbuf.pushConstants(*pipeline_layout,
|
||||
vk::ShaderStageFlagBits::eVertex | vk::ShaderStageFlagBits::eFragment, 0U,
|
||||
sizeof(push_data), &push_data);
|
||||
cmdbuf.pushConstants(*pipeline_layout, gp_stage_flags, 0U, sizeof(push_data), &push_data);
|
||||
cmdbuf.bindPipeline(vk::PipelineBindPoint::eGraphics, Handle());
|
||||
}
|
||||
|
||||
|
|
|
@ -36,7 +36,7 @@ struct GraphicsPipelineKey {
|
|||
u32 num_samples;
|
||||
u32 mrt_mask;
|
||||
Liverpool::StencilControl stencil;
|
||||
Liverpool::PrimitiveType prim_type;
|
||||
AmdGpu::PrimitiveType prim_type;
|
||||
u32 enable_primitive_restart;
|
||||
u32 primitive_restart_index;
|
||||
Liverpool::PolygonMode polygon_mode;
|
||||
|
@ -86,13 +86,13 @@ public:
|
|||
}
|
||||
|
||||
[[nodiscard]] bool IsPrimitiveListTopology() const {
|
||||
return key.prim_type == Liverpool::PrimitiveType::PointList ||
|
||||
key.prim_type == Liverpool::PrimitiveType::LineList ||
|
||||
key.prim_type == Liverpool::PrimitiveType::TriangleList ||
|
||||
key.prim_type == Liverpool::PrimitiveType::AdjLineList ||
|
||||
key.prim_type == Liverpool::PrimitiveType::AdjTriangleList ||
|
||||
key.prim_type == Liverpool::PrimitiveType::RectList ||
|
||||
key.prim_type == Liverpool::PrimitiveType::QuadList;
|
||||
return key.prim_type == AmdGpu::PrimitiveType::PointList ||
|
||||
key.prim_type == AmdGpu::PrimitiveType::LineList ||
|
||||
key.prim_type == AmdGpu::PrimitiveType::TriangleList ||
|
||||
key.prim_type == AmdGpu::PrimitiveType::AdjLineList ||
|
||||
key.prim_type == AmdGpu::PrimitiveType::AdjTriangleList ||
|
||||
key.prim_type == AmdGpu::PrimitiveType::RectList ||
|
||||
key.prim_type == AmdGpu::PrimitiveType::QuadList;
|
||||
}
|
||||
|
||||
private:
|
||||
|
|
|
@ -322,6 +322,7 @@ bool Instance::CreateDevice() {
|
|||
.geometryShader = features.geometryShader,
|
||||
.logicOp = features.logicOp,
|
||||
.depthBiasClamp = features.depthBiasClamp,
|
||||
.fillModeNonSolid = features.fillModeNonSolid,
|
||||
.multiViewport = features.multiViewport,
|
||||
.samplerAnisotropy = features.samplerAnisotropy,
|
||||
.vertexPipelineStoresAndAtomics = features.vertexPipelineStoresAndAtomics,
|
||||
|
|
|
@ -147,6 +147,16 @@ public:
|
|||
return list_restart;
|
||||
}
|
||||
|
||||
/// Returns true when geometry shaders are supported by the device
|
||||
bool IsGeometryStageSupported() const {
|
||||
return features.geometryShader;
|
||||
}
|
||||
|
||||
/// Returns true when tessellation is supported by the device
|
||||
bool IsTessellationSupported() const {
|
||||
return features.tessellationShader;
|
||||
}
|
||||
|
||||
/// Returns the vendor ID of the physical device
|
||||
u32 GetVendorID() const {
|
||||
return properties.vendorID;
|
||||
|
|
|
@ -7,7 +7,10 @@
|
|||
#include "common/io_file.h"
|
||||
#include "common/path_util.h"
|
||||
#include "shader_recompiler/backend/spirv/emit_spirv.h"
|
||||
#include "shader_recompiler/frontend/copy_shader.h"
|
||||
#include "shader_recompiler/info.h"
|
||||
#include "shader_recompiler/recompiler.h"
|
||||
#include "shader_recompiler/runtime_info.h"
|
||||
#include "video_core/renderer_vulkan/renderer_vulkan.h"
|
||||
#include "video_core/renderer_vulkan/vk_instance.h"
|
||||
#include "video_core/renderer_vulkan/vk_pipeline_cache.h"
|
||||
|
@ -82,6 +85,13 @@ Shader::RuntimeInfo PipelineCache::BuildRuntimeInfo(Shader::Stage stage) {
|
|||
auto info = Shader::RuntimeInfo{stage};
|
||||
const auto& regs = liverpool->regs;
|
||||
switch (stage) {
|
||||
case Shader::Stage::Export: {
|
||||
info.num_user_data = regs.es_program.settings.num_user_regs;
|
||||
info.num_input_vgprs = regs.es_program.settings.vgpr_comp_cnt;
|
||||
info.num_allocated_vgprs = regs.es_program.settings.num_vgprs * 4;
|
||||
info.es_info.vertex_data_size = regs.vgt_esgs_ring_itemsize;
|
||||
break;
|
||||
}
|
||||
case Shader::Stage::Vertex: {
|
||||
info.num_user_data = regs.vs_program.settings.num_user_regs;
|
||||
info.num_input_vgprs = regs.vs_program.settings.vgpr_comp_cnt;
|
||||
|
@ -92,6 +102,29 @@ Shader::RuntimeInfo PipelineCache::BuildRuntimeInfo(Shader::Stage stage) {
|
|||
regs.clipper_control.clip_space == Liverpool::ClipSpace::MinusWToW;
|
||||
break;
|
||||
}
|
||||
case Shader::Stage::Geometry: {
|
||||
info.num_user_data = regs.gs_program.settings.num_user_regs;
|
||||
info.num_input_vgprs = regs.gs_program.settings.vgpr_comp_cnt;
|
||||
info.num_allocated_vgprs = regs.gs_program.settings.num_vgprs * 4;
|
||||
info.gs_info.output_vertices = regs.vgt_gs_max_vert_out;
|
||||
info.gs_info.num_invocations =
|
||||
regs.vgt_gs_instance_cnt.IsEnabled() ? regs.vgt_gs_instance_cnt.count : 1;
|
||||
info.gs_info.in_primitive = regs.primitive_type;
|
||||
for (u32 stream_id = 0; stream_id < Shader::GsMaxOutputStreams; ++stream_id) {
|
||||
info.gs_info.out_primitive[stream_id] =
|
||||
regs.vgt_gs_out_prim_type.GetPrimitiveType(stream_id);
|
||||
}
|
||||
info.gs_info.in_vertex_data_size = regs.vgt_esgs_ring_itemsize;
|
||||
info.gs_info.out_vertex_data_size = regs.vgt_gs_vert_itemsize[0];
|
||||
|
||||
// Extract semantics offsets from a copy shader
|
||||
const auto vc_stage = Shader::Stage::Vertex;
|
||||
const auto* pgm_vc = regs.ProgramForStage(static_cast<u32>(vc_stage));
|
||||
const auto params_vc = Liverpool::GetParams(*pgm_vc);
|
||||
DumpShader(params_vc.code, params_vc.hash, Shader::Stage::Vertex, 0, "copy.bin");
|
||||
info.gs_info.copy_data = Shader::ParseCopyShader(params_vc.code);
|
||||
break;
|
||||
}
|
||||
case Shader::Stage::Fragment: {
|
||||
info.num_user_data = regs.ps_program.settings.num_user_regs;
|
||||
info.num_allocated_vgprs = regs.ps_program.settings.num_vgprs * 4;
|
||||
|
@ -149,7 +182,7 @@ PipelineCache::~PipelineCache() = default;
|
|||
const GraphicsPipeline* PipelineCache::GetGraphicsPipeline() {
|
||||
const auto& regs = liverpool->regs;
|
||||
// Tessellation is unsupported so skip the draw to avoid locking up the driver.
|
||||
if (regs.primitive_type == Liverpool::PrimitiveType::PatchPrimitive) {
|
||||
if (regs.primitive_type == AmdGpu::PrimitiveType::PatchPrimitive) {
|
||||
return nullptr;
|
||||
}
|
||||
// There are several cases (e.g. FCE, FMask/HTile decompression) where we don't need to do an
|
||||
|
@ -163,7 +196,7 @@ const GraphicsPipeline* PipelineCache::GetGraphicsPipeline() {
|
|||
LOG_TRACE(Render_Vulkan, "FMask decompression pass skipped");
|
||||
return nullptr;
|
||||
}
|
||||
if (regs.primitive_type == Liverpool::PrimitiveType::None) {
|
||||
if (regs.primitive_type == AmdGpu::PrimitiveType::None) {
|
||||
LOG_TRACE(Render_Vulkan, "Primitive type 'None' skipped");
|
||||
return nullptr;
|
||||
}
|
||||
|
@ -190,15 +223,6 @@ const ComputePipeline* PipelineCache::GetComputePipeline() {
|
|||
return it->second;
|
||||
}
|
||||
|
||||
bool ShouldSkipShader(u64 shader_hash, const char* shader_type) {
|
||||
static constexpr std::array<u64, 0> skip_hashes = {};
|
||||
if (std::ranges::contains(skip_hashes, shader_hash)) {
|
||||
LOG_WARNING(Render_Vulkan, "Skipped {} shader hash {:#x}.", shader_type, shader_hash);
|
||||
return true;
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
bool PipelineCache::RefreshGraphicsKey() {
|
||||
std::memset(&graphics_key, 0, sizeof(GraphicsPipelineKey));
|
||||
|
||||
|
@ -275,46 +299,66 @@ bool PipelineCache::RefreshGraphicsKey() {
|
|||
}
|
||||
|
||||
Shader::Backend::Bindings binding{};
|
||||
for (u32 i = 0; i < MaxShaderStages; i++) {
|
||||
if (!regs.stage_enable.IsStageEnabled(i)) {
|
||||
key.stage_hashes[i] = 0;
|
||||
infos[i] = nullptr;
|
||||
continue;
|
||||
const auto& TryBindStageRemap = [&](Shader::Stage stage_in, Shader::Stage stage_out) -> bool {
|
||||
const auto stage_in_idx = static_cast<u32>(stage_in);
|
||||
const auto stage_out_idx = static_cast<u32>(stage_out);
|
||||
if (!regs.stage_enable.IsStageEnabled(stage_in_idx)) {
|
||||
key.stage_hashes[stage_out_idx] = 0;
|
||||
infos[stage_out_idx] = nullptr;
|
||||
return false;
|
||||
}
|
||||
auto* pgm = regs.ProgramForStage(i);
|
||||
|
||||
const auto* pgm = regs.ProgramForStage(stage_in_idx);
|
||||
if (!pgm || !pgm->Address<u32*>()) {
|
||||
key.stage_hashes[i] = 0;
|
||||
infos[i] = nullptr;
|
||||
continue;
|
||||
key.stage_hashes[stage_out_idx] = 0;
|
||||
infos[stage_out_idx] = nullptr;
|
||||
return false;
|
||||
}
|
||||
|
||||
const auto* bininfo = Liverpool::GetBinaryInfo(*pgm);
|
||||
if (!bininfo->Valid()) {
|
||||
LOG_WARNING(Render_Vulkan, "Invalid binary info structure!");
|
||||
key.stage_hashes[i] = 0;
|
||||
infos[i] = nullptr;
|
||||
continue;
|
||||
}
|
||||
if (ShouldSkipShader(bininfo->shader_hash, "graphics")) {
|
||||
return false;
|
||||
}
|
||||
const auto stage = Shader::StageFromIndex(i);
|
||||
const auto params = Liverpool::GetParams(*pgm);
|
||||
|
||||
if (stage != Shader::Stage::Vertex && stage != Shader::Stage::Fragment) {
|
||||
key.stage_hashes[stage_out_idx] = 0;
|
||||
infos[stage_out_idx] = nullptr;
|
||||
return false;
|
||||
}
|
||||
|
||||
static bool TessMissingLogged = false;
|
||||
if (auto* pgm = regs.ProgramForStage(3);
|
||||
regs.stage_enable.IsStageEnabled(3) && pgm->Address() != 0) {
|
||||
if (!TessMissingLogged) {
|
||||
LOG_WARNING(Render_Vulkan, "Tess pipeline compilation skipped");
|
||||
TessMissingLogged = true;
|
||||
}
|
||||
auto params = Liverpool::GetParams(*pgm);
|
||||
std::tie(infos[stage_out_idx], modules[stage_out_idx], key.stage_hashes[stage_out_idx]) =
|
||||
GetProgram(stage_in, params, binding);
|
||||
return true;
|
||||
};
|
||||
|
||||
const auto& TryBindStage = [&](Shader::Stage stage) { return TryBindStageRemap(stage, stage); };
|
||||
|
||||
const auto& IsGsFeaturesSupported = [&]() -> bool {
|
||||
// These checks are temporary until all functionality is implemented.
|
||||
return !regs.vgt_gs_mode.onchip && !regs.vgt_strmout_config.raw;
|
||||
};
|
||||
|
||||
TryBindStage(Shader::Stage::Fragment);
|
||||
|
||||
const auto* fs_info = infos[static_cast<u32>(Shader::Stage::Fragment)];
|
||||
key.mrt_mask = fs_info ? fs_info->mrt_mask : 0u;
|
||||
|
||||
switch (regs.stage_enable.raw) {
|
||||
case Liverpool::ShaderStageEnable::VgtStages::EsGs: {
|
||||
if (!instance.IsGeometryStageSupported() || !IsGsFeaturesSupported()) {
|
||||
break;
|
||||
}
|
||||
if (!TryBindStageRemap(Shader::Stage::Export, Shader::Stage::Vertex)) {
|
||||
return false;
|
||||
}
|
||||
|
||||
std::tie(infos[i], modules[i], key.stage_hashes[i]) = GetProgram(stage, params, binding);
|
||||
if (!TryBindStage(Shader::Stage::Geometry)) {
|
||||
return false;
|
||||
}
|
||||
break;
|
||||
}
|
||||
default: {
|
||||
TryBindStage(Shader::Stage::Vertex);
|
||||
infos[static_cast<u32>(Shader::Stage::Geometry)] = nullptr;
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
const auto* vs_info = infos[static_cast<u32>(Shader::Stage::Vertex)];
|
||||
|
@ -336,9 +380,6 @@ bool PipelineCache::RefreshGraphicsKey() {
|
|||
}
|
||||
}
|
||||
|
||||
const auto* fs_info = infos[static_cast<u32>(Shader::Stage::Fragment)];
|
||||
key.mrt_mask = fs_info ? fs_info->mrt_mask : 0u;
|
||||
|
||||
// Second pass to fill remain CB pipeline key data
|
||||
for (auto cb = 0u, remapped_cb = 0u; cb < Liverpool::NumColorBuffers; ++cb) {
|
||||
auto const& col_buf = regs.color_buffers[cb];
|
||||
|
@ -364,9 +405,6 @@ bool PipelineCache::RefreshComputeKey() {
|
|||
Shader::Backend::Bindings binding{};
|
||||
const auto* cs_pgm = &liverpool->regs.cs_program;
|
||||
const auto cs_params = Liverpool::GetParams(*cs_pgm);
|
||||
if (ShouldSkipShader(cs_params.hash, "compute")) {
|
||||
return false;
|
||||
}
|
||||
std::tie(infos[0], modules[0], compute_key) =
|
||||
GetProgram(Shader::Stage::Compute, cs_params, binding);
|
||||
return true;
|
||||
|
@ -378,15 +416,11 @@ vk::ShaderModule PipelineCache::CompileModule(Shader::Info& info,
|
|||
Shader::Backend::Bindings& binding) {
|
||||
LOG_INFO(Render_Vulkan, "Compiling {} shader {:#x} {}", info.stage, info.pgm_hash,
|
||||
perm_idx != 0 ? "(permutation)" : "");
|
||||
if (Config::dumpShaders()) {
|
||||
DumpShader(code, info.pgm_hash, info.stage, perm_idx, "bin");
|
||||
}
|
||||
DumpShader(code, info.pgm_hash, info.stage, perm_idx, "bin");
|
||||
|
||||
const auto ir_program = Shader::TranslateProgram(code, pools, info, runtime_info, profile);
|
||||
const auto spv = Shader::Backend::SPIRV::EmitSPIRV(profile, runtime_info, ir_program, binding);
|
||||
if (Config::dumpShaders()) {
|
||||
DumpShader(spv, info.pgm_hash, info.stage, perm_idx, "spv");
|
||||
}
|
||||
DumpShader(spv, info.pgm_hash, info.stage, perm_idx, "spv");
|
||||
|
||||
const auto module = CompileSPV(spv, instance.GetDevice());
|
||||
const auto name = fmt::format("{}_{:#x}_{}", info.stage, info.pgm_hash, perm_idx);
|
||||
|
@ -429,6 +463,10 @@ std::tuple<const Shader::Info*, vk::ShaderModule, u64> PipelineCache::GetProgram
|
|||
|
||||
void PipelineCache::DumpShader(std::span<const u32> code, u64 hash, Shader::Stage stage,
|
||||
size_t perm_idx, std::string_view ext) {
|
||||
if (!Config::dumpShaders()) {
|
||||
return;
|
||||
}
|
||||
|
||||
using namespace Common::FS;
|
||||
const auto dump_dir = GetUserPath(PathType::ShaderDir) / "dumps";
|
||||
if (!std::filesystem::exists(dump_dir)) {
|
||||
|
|
|
@ -70,9 +70,8 @@ void Rasterizer::Draw(bool is_indexed, u32 index_offset) {
|
|||
cmdbuf.drawIndexed(num_indices, regs.num_instances.NumInstances(), 0, s32(vertex_offset),
|
||||
instance_offset);
|
||||
} else {
|
||||
const u32 num_vertices = regs.primitive_type == AmdGpu::Liverpool::PrimitiveType::RectList
|
||||
? 4
|
||||
: regs.num_indices;
|
||||
const u32 num_vertices =
|
||||
regs.primitive_type == AmdGpu::PrimitiveType::RectList ? 4 : regs.num_indices;
|
||||
cmdbuf.draw(num_vertices, regs.num_instances.NumInstances(), vertex_offset,
|
||||
instance_offset);
|
||||
}
|
||||
|
@ -88,7 +87,7 @@ void Rasterizer::DrawIndirect(bool is_indexed, VAddr address, u32 offset, u32 si
|
|||
return;
|
||||
}
|
||||
|
||||
ASSERT_MSG(regs.primitive_type != AmdGpu::Liverpool::PrimitiveType::RectList,
|
||||
ASSERT_MSG(regs.primitive_type != AmdGpu::PrimitiveType::RectList,
|
||||
"Unsupported primitive type for indirect draw");
|
||||
|
||||
try {
|
||||
|
|
Loading…
Add table
Add a link
Reference in a new issue