mirror of
https://github.com/shadps4-emu/shadPS4.git
synced 2025-06-09 04:03:14 +00:00
Tessellation (#1528)
* shader_recompiler: Tessellation WIP * fix compiler errors after merge DONT MERGE set log file to /dev/null DONT MERGE linux pthread bb fix save work DONT MERGE dump ir save more work fix mistake with ES shader skip list add input patch control points dynamic state random stuff * WIP Tessellation partial implementation. Squash commits * test: make local/tcs use attr arrays * attr arrays in TCS/TES * dont define empty attr arrays * switch to special opcodes for tess tcs/tes reads and tcs writes * impl tcs/tes read attr insts * rebase fix * save some work * save work probably broken and slow * put Vertex LogicalStage after TCS and TES to fix bindings * more refactors * refactor pattern matching and optimize modulos (disabled) * enable modulo opt * copyright * rebase fixes * remove some prints * remove some stuff * Add TCS/TES support for shader patching and use LogicalStage * refactor and handle wider DS instructions * get rid of GetAttributes for special tess constants reads. Immediately replace some upon seeing readconstbuffer. Gets rid of some extra passes over IR * stop relying on GNMX HsConstants struct. Change runtime_info.hs_info and some regs * delete some more stuff * update comments for current implementation * some cleanup * uint error * more cleanup * remove patch control points dynamic state (because runtime_info already depends on it) * fix potential problem with determining passthrough --------- Co-authored-by: IndecisiveTurtle <47210458+raphaelthegreat@users.noreply.github.com>
This commit is contained in:
parent
3e22622508
commit
3c0c921ef5
54 changed files with 2146 additions and 189 deletions
|
@ -143,6 +143,13 @@ struct Liverpool {
|
|||
}
|
||||
};
|
||||
|
||||
struct HsTessFactorClamp {
|
||||
// I've only seen min=0.0, max=1.0 so far.
|
||||
// TODO why is max set to 1.0? Makes no sense
|
||||
float hs_max_tess;
|
||||
float hs_min_tess;
|
||||
};
|
||||
|
||||
struct ComputeProgram {
|
||||
u32 dispatch_initiator;
|
||||
u32 dim_x;
|
||||
|
@ -956,6 +963,7 @@ struct Liverpool {
|
|||
enum VgtStages : u32 {
|
||||
Vs = 0u, // always enabled
|
||||
EsGs = 0xB0u,
|
||||
LsHs = 0x45u,
|
||||
};
|
||||
|
||||
VgtStages raw;
|
||||
|
@ -963,7 +971,8 @@ struct Liverpool {
|
|||
BitField<2, 1, u32> hs_en;
|
||||
BitField<3, 2, u32> es_en;
|
||||
BitField<5, 1, u32> gs_en;
|
||||
BitField<6, 1, u32> vs_en;
|
||||
BitField<6, 2, u32> vs_en;
|
||||
BitField<8, 1, u32> dynamic_hs;
|
||||
|
||||
bool IsStageEnabled(u32 stage) const {
|
||||
switch (stage) {
|
||||
|
@ -1059,6 +1068,28 @@ struct Liverpool {
|
|||
};
|
||||
};
|
||||
|
||||
union LsHsConfig {
|
||||
u32 raw;
|
||||
BitField<0, 8, u32> num_patches;
|
||||
BitField<8, 6, u32> hs_input_control_points;
|
||||
BitField<14, 6, u32> hs_output_control_points;
|
||||
};
|
||||
|
||||
union TessellationConfig {
|
||||
u32 raw;
|
||||
BitField<0, 2, TessellationType> type;
|
||||
BitField<2, 3, TessellationPartitioning> partitioning;
|
||||
BitField<5, 3, TessellationTopology> topology;
|
||||
};
|
||||
|
||||
union TessFactorMemoryBase {
|
||||
u32 base;
|
||||
|
||||
u64 MemoryBase() const {
|
||||
return static_cast<u64>(base) << 8;
|
||||
}
|
||||
};
|
||||
|
||||
union Eqaa {
|
||||
u32 raw;
|
||||
BitField<0, 1, u32> max_anchor_samples;
|
||||
|
@ -1109,7 +1140,7 @@ struct Liverpool {
|
|||
ShaderProgram es_program;
|
||||
INSERT_PADDING_WORDS(0x2C);
|
||||
ShaderProgram hs_program;
|
||||
INSERT_PADDING_WORDS(0x2C);
|
||||
INSERT_PADDING_WORDS(0x2D48 - 0x2d08 - 20);
|
||||
ShaderProgram ls_program;
|
||||
INSERT_PADDING_WORDS(0xA4);
|
||||
ComputeProgram cs_program;
|
||||
|
@ -1176,7 +1207,9 @@ struct Liverpool {
|
|||
PolygonControl polygon_control;
|
||||
ViewportControl viewport_control;
|
||||
VsOutputControl vs_output_control;
|
||||
INSERT_PADDING_WORDS(0xA290 - 0xA207 - 1);
|
||||
INSERT_PADDING_WORDS(0xA287 - 0xA207 - 1);
|
||||
HsTessFactorClamp hs_clamp;
|
||||
INSERT_PADDING_WORDS(0xA290 - 0xA287 - 2);
|
||||
GsMode vgt_gs_mode;
|
||||
INSERT_PADDING_WORDS(1);
|
||||
ModeControl mode_control;
|
||||
|
@ -1200,9 +1233,10 @@ struct Liverpool {
|
|||
BitField<0, 11, u32> vgt_gs_max_vert_out;
|
||||
INSERT_PADDING_WORDS(0xA2D5 - 0xA2CE - 1);
|
||||
ShaderStageEnable stage_enable;
|
||||
INSERT_PADDING_WORDS(1);
|
||||
LsHsConfig ls_hs_config;
|
||||
u32 vgt_gs_vert_itemsize[4];
|
||||
INSERT_PADDING_WORDS(4);
|
||||
TessellationConfig tess_config;
|
||||
INSERT_PADDING_WORDS(3);
|
||||
PolygonOffset poly_offset;
|
||||
GsInstances vgt_gs_instance_cnt;
|
||||
StreamOutConfig vgt_strmout_config;
|
||||
|
@ -1216,6 +1250,8 @@ struct Liverpool {
|
|||
INSERT_PADDING_WORDS(0xC24C - 0xC243);
|
||||
u32 num_indices;
|
||||
VgtNumInstances num_instances;
|
||||
INSERT_PADDING_WORDS(0xC250 - 0xC24D - 1);
|
||||
TessFactorMemoryBase vgt_tf_memory_base;
|
||||
};
|
||||
std::array<u32, NumRegs> reg_array{};
|
||||
|
||||
|
@ -1431,6 +1467,7 @@ 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(hs_clamp) == 0xA287);
|
||||
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);
|
||||
|
@ -1445,6 +1482,7 @@ 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(tess_config) == 0xA2DB);
|
||||
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);
|
||||
|
@ -1456,6 +1494,7 @@ static_assert(GFX6_3D_REG_INDEX(color_buffers[0].slice) == 0xA31A);
|
|||
static_assert(GFX6_3D_REG_INDEX(color_buffers[7].base_address) == 0xA381);
|
||||
static_assert(GFX6_3D_REG_INDEX(primitive_type) == 0xC242);
|
||||
static_assert(GFX6_3D_REG_INDEX(num_instances) == 0xC24D);
|
||||
static_assert(GFX6_3D_REG_INDEX(vgt_tf_memory_base) == 0xc250);
|
||||
|
||||
#undef GFX6_3D_REG_INDEX
|
||||
|
||||
|
|
|
@ -3,6 +3,8 @@
|
|||
|
||||
#pragma once
|
||||
|
||||
#include <string_view>
|
||||
#include <fmt/format.h>
|
||||
#include "common/types.h"
|
||||
|
||||
namespace AmdGpu {
|
||||
|
@ -21,6 +23,69 @@ enum class FpDenormMode : u32 {
|
|||
InOutAllow = 3,
|
||||
};
|
||||
|
||||
enum class TessellationType : u32 {
|
||||
Isoline = 0,
|
||||
Triangle = 1,
|
||||
Quad = 2,
|
||||
};
|
||||
|
||||
constexpr std::string_view NameOf(TessellationType type) {
|
||||
switch (type) {
|
||||
case TessellationType::Isoline:
|
||||
return "Isoline";
|
||||
case TessellationType::Triangle:
|
||||
return "Triangle";
|
||||
case TessellationType::Quad:
|
||||
return "Quad";
|
||||
default:
|
||||
return "Unknown";
|
||||
}
|
||||
}
|
||||
|
||||
enum class TessellationPartitioning : u32 {
|
||||
Integer = 0,
|
||||
Pow2 = 1,
|
||||
FracOdd = 2,
|
||||
FracEven = 3,
|
||||
};
|
||||
|
||||
constexpr std::string_view NameOf(TessellationPartitioning partitioning) {
|
||||
switch (partitioning) {
|
||||
case TessellationPartitioning::Integer:
|
||||
return "Integer";
|
||||
case TessellationPartitioning::Pow2:
|
||||
return "Pow2";
|
||||
case TessellationPartitioning::FracOdd:
|
||||
return "FracOdd";
|
||||
case TessellationPartitioning::FracEven:
|
||||
return "FracEven";
|
||||
default:
|
||||
return "Unknown";
|
||||
}
|
||||
}
|
||||
|
||||
enum class TessellationTopology : u32 {
|
||||
Point = 0,
|
||||
Line = 1,
|
||||
TriangleCw = 2,
|
||||
TriangleCcw = 3,
|
||||
};
|
||||
|
||||
constexpr std::string_view NameOf(TessellationTopology topology) {
|
||||
switch (topology) {
|
||||
case TessellationTopology::Point:
|
||||
return "Point";
|
||||
case TessellationTopology::Line:
|
||||
return "Line";
|
||||
case TessellationTopology::TriangleCw:
|
||||
return "TriangleCw";
|
||||
case TessellationTopology::TriangleCcw:
|
||||
return "TriangleCcw";
|
||||
default:
|
||||
return "Unknown";
|
||||
}
|
||||
}
|
||||
|
||||
// See `VGT_PRIMITIVE_TYPE` description in [Radeon Sea Islands 3D/Compute Register Reference Guide]
|
||||
enum class PrimitiveType : u32 {
|
||||
None = 0,
|
||||
|
@ -118,3 +183,33 @@ enum class NumberFormat : u32 {
|
|||
};
|
||||
|
||||
} // namespace AmdGpu
|
||||
|
||||
template <>
|
||||
struct fmt::formatter<AmdGpu::TessellationType> {
|
||||
constexpr auto parse(format_parse_context& ctx) {
|
||||
return ctx.begin();
|
||||
}
|
||||
auto format(AmdGpu::TessellationType type, format_context& ctx) const {
|
||||
return fmt::format_to(ctx.out(), "{}", AmdGpu::NameOf(type));
|
||||
}
|
||||
};
|
||||
|
||||
template <>
|
||||
struct fmt::formatter<AmdGpu::TessellationPartitioning> {
|
||||
constexpr auto parse(format_parse_context& ctx) {
|
||||
return ctx.begin();
|
||||
}
|
||||
auto format(AmdGpu::TessellationPartitioning type, format_context& ctx) const {
|
||||
return fmt::format_to(ctx.out(), "{}", AmdGpu::NameOf(type));
|
||||
}
|
||||
};
|
||||
|
||||
template <>
|
||||
struct fmt::formatter<AmdGpu::TessellationTopology> {
|
||||
constexpr auto parse(format_parse_context& ctx) {
|
||||
return ctx.begin();
|
||||
}
|
||||
auto format(AmdGpu::TessellationTopology type, format_context& ctx) const {
|
||||
return fmt::format_to(ctx.out(), "{}", AmdGpu::NameOf(type));
|
||||
}
|
||||
};
|
||||
|
|
|
@ -16,7 +16,7 @@ ComputePipeline::ComputePipeline(const Instance& instance_, Scheduler& scheduler
|
|||
ComputePipelineKey compute_key_, const Shader::Info& info_,
|
||||
vk::ShaderModule module)
|
||||
: Pipeline{instance_, scheduler_, desc_heap_, pipeline_cache, true}, compute_key{compute_key_} {
|
||||
auto& info = stages[int(Shader::Stage::Compute)];
|
||||
auto& info = stages[int(Shader::LogicalStage::Compute)];
|
||||
info = &info_;
|
||||
|
||||
const vk::PipelineShaderStageCreateInfo shader_ci = {
|
||||
|
|
|
@ -8,6 +8,7 @@
|
|||
|
||||
#include "common/assert.h"
|
||||
#include "common/scope_exit.h"
|
||||
#include "shader_recompiler/runtime_info.h"
|
||||
#include "video_core/amdgpu/resource.h"
|
||||
#include "video_core/buffer_cache/buffer_cache.h"
|
||||
#include "video_core/renderer_vulkan/vk_graphics_pipeline.h"
|
||||
|
@ -52,7 +53,7 @@ GraphicsPipeline::GraphicsPipeline(const Instance& instance_, Scheduler& schedul
|
|||
boost::container::static_vector<vk::VertexInputBindingDescription, 32> vertex_bindings;
|
||||
boost::container::static_vector<vk::VertexInputAttributeDescription, 32> vertex_attributes;
|
||||
if (fetch_shader && !instance.IsVertexInputDynamicState()) {
|
||||
const auto& vs_info = GetStage(Shader::Stage::Vertex);
|
||||
const auto& vs_info = GetStage(Shader::LogicalStage::Vertex);
|
||||
for (const auto& attrib : fetch_shader->attributes) {
|
||||
if (attrib.UsesStepRates()) {
|
||||
// Skip attribute binding as the data will be pulled by shader
|
||||
|
@ -106,6 +107,10 @@ GraphicsPipeline::GraphicsPipeline(const Instance& instance_, Scheduler& schedul
|
|||
key.primitive_restart_index == 0xFFFFFFFF,
|
||||
"Primitive restart index other than -1 is not supported yet");
|
||||
|
||||
const vk::PipelineTessellationStateCreateInfo tessellation_state = {
|
||||
.patchControlPoints = key.patch_control_points,
|
||||
};
|
||||
|
||||
const vk::PipelineRasterizationStateCreateInfo raster_state = {
|
||||
.depthClampEnable = false,
|
||||
.rasterizerDiscardEnable = false,
|
||||
|
@ -204,7 +209,7 @@ GraphicsPipeline::GraphicsPipeline(const Instance& instance_, Scheduler& schedul
|
|||
|
||||
boost::container::static_vector<vk::PipelineShaderStageCreateInfo, MaxShaderStages>
|
||||
shader_stages;
|
||||
auto stage = u32(Shader::Stage::Vertex);
|
||||
auto stage = u32(Shader::LogicalStage::Vertex);
|
||||
if (infos[stage]) {
|
||||
shader_stages.emplace_back(vk::PipelineShaderStageCreateInfo{
|
||||
.stage = vk::ShaderStageFlagBits::eVertex,
|
||||
|
@ -212,7 +217,7 @@ GraphicsPipeline::GraphicsPipeline(const Instance& instance_, Scheduler& schedul
|
|||
.pName = "main",
|
||||
});
|
||||
}
|
||||
stage = u32(Shader::Stage::Geometry);
|
||||
stage = u32(Shader::LogicalStage::Geometry);
|
||||
if (infos[stage]) {
|
||||
shader_stages.emplace_back(vk::PipelineShaderStageCreateInfo{
|
||||
.stage = vk::ShaderStageFlagBits::eGeometry,
|
||||
|
@ -220,7 +225,23 @@ GraphicsPipeline::GraphicsPipeline(const Instance& instance_, Scheduler& schedul
|
|||
.pName = "main",
|
||||
});
|
||||
}
|
||||
stage = u32(Shader::Stage::Fragment);
|
||||
stage = u32(Shader::LogicalStage::TessellationControl);
|
||||
if (infos[stage]) {
|
||||
shader_stages.emplace_back(vk::PipelineShaderStageCreateInfo{
|
||||
.stage = vk::ShaderStageFlagBits::eTessellationControl,
|
||||
.module = modules[stage],
|
||||
.pName = "main",
|
||||
});
|
||||
}
|
||||
stage = u32(Shader::LogicalStage::TessellationEval);
|
||||
if (infos[stage]) {
|
||||
shader_stages.emplace_back(vk::PipelineShaderStageCreateInfo{
|
||||
.stage = vk::ShaderStageFlagBits::eTessellationEvaluation,
|
||||
.module = modules[stage],
|
||||
.pName = "main",
|
||||
});
|
||||
}
|
||||
stage = u32(Shader::LogicalStage::Fragment);
|
||||
if (infos[stage]) {
|
||||
shader_stages.emplace_back(vk::PipelineShaderStageCreateInfo{
|
||||
.stage = vk::ShaderStageFlagBits::eFragment,
|
||||
|
@ -301,6 +322,8 @@ GraphicsPipeline::GraphicsPipeline(const Instance& instance_, Scheduler& schedul
|
|||
.pStages = shader_stages.data(),
|
||||
.pVertexInputState = !instance.IsVertexInputDynamicState() ? &vertex_input_info : nullptr,
|
||||
.pInputAssemblyState = &input_assembly,
|
||||
.pTessellationState =
|
||||
stages[u32(Shader::LogicalStage::TessellationControl)] ? &tessellation_state : nullptr,
|
||||
.pViewportState = &viewport_info,
|
||||
.pRasterizationState = &raster_state,
|
||||
.pMultisampleState = &multisampling,
|
||||
|
@ -327,7 +350,6 @@ void GraphicsPipeline::BuildDescSetLayout() {
|
|||
if (!stage) {
|
||||
continue;
|
||||
}
|
||||
|
||||
if (stage->has_readconst) {
|
||||
bindings.push_back({
|
||||
.binding = binding++,
|
||||
|
|
|
@ -52,6 +52,7 @@ struct GraphicsPipelineKey {
|
|||
std::array<Liverpool::BlendControl, Liverpool::NumColorBuffers> blend_controls;
|
||||
std::array<vk::ColorComponentFlags, Liverpool::NumColorBuffers> write_masks;
|
||||
std::array<vk::Format, MaxVertexBufferCount> vertex_buffer_formats;
|
||||
u32 patch_control_points;
|
||||
|
||||
bool operator==(const GraphicsPipelineKey& key) const noexcept {
|
||||
return std::memcmp(this, &key, sizeof(key)) == 0;
|
||||
|
@ -73,7 +74,7 @@ public:
|
|||
|
||||
bool IsEmbeddedVs() const noexcept {
|
||||
static constexpr size_t EmbeddedVsHash = 0x9b2da5cf47f8c29f;
|
||||
return key.stage_hashes[u32(Shader::Stage::Vertex)] == EmbeddedVsHash;
|
||||
return key.stage_hashes[u32(Shader::LogicalStage::Vertex)] == EmbeddedVsHash;
|
||||
}
|
||||
|
||||
auto GetWriteMasks() const {
|
||||
|
|
|
@ -327,6 +327,7 @@ bool Instance::CreateDevice() {
|
|||
.imageCubeArray = features.imageCubeArray,
|
||||
.independentBlend = features.independentBlend,
|
||||
.geometryShader = features.geometryShader,
|
||||
.tessellationShader = features.tessellationShader,
|
||||
.logicOp = features.logicOp,
|
||||
.depthBiasClamp = features.depthBiasClamp,
|
||||
.fillModeNonSolid = features.fillModeNonSolid,
|
||||
|
@ -378,6 +379,7 @@ bool Instance::CreateDevice() {
|
|||
vk::PhysicalDeviceExtendedDynamicStateFeaturesEXT{
|
||||
.extendedDynamicState = true,
|
||||
},
|
||||
vk::PhysicalDeviceExtendedDynamicState2FeaturesEXT{},
|
||||
vk::PhysicalDeviceExtendedDynamicState3FeaturesEXT{
|
||||
.extendedDynamicState3ColorWriteMask = true,
|
||||
},
|
||||
|
|
|
@ -22,6 +22,8 @@ extern std::unique_ptr<Vulkan::Presenter> presenter;
|
|||
|
||||
namespace Vulkan {
|
||||
|
||||
using Shader::LogicalStage;
|
||||
using Shader::Stage;
|
||||
using Shader::VsOutput;
|
||||
|
||||
constexpr static std::array DescriptorHeapSizes = {
|
||||
|
@ -78,7 +80,7 @@ void GatherVertexOutputs(Shader::VertexRuntimeInfo& info,
|
|||
: (ctl.IsCullDistEnabled(7) ? VsOutput::CullDist7 : VsOutput::None));
|
||||
}
|
||||
|
||||
Shader::RuntimeInfo PipelineCache::BuildRuntimeInfo(Shader::Stage stage) {
|
||||
Shader::RuntimeInfo PipelineCache::BuildRuntimeInfo(Stage stage, LogicalStage l_stage) {
|
||||
auto info = Shader::RuntimeInfo{stage};
|
||||
const auto& regs = liverpool->regs;
|
||||
const auto BuildCommon = [&](const auto& program) {
|
||||
|
@ -89,20 +91,47 @@ Shader::RuntimeInfo PipelineCache::BuildRuntimeInfo(Shader::Stage stage) {
|
|||
info.fp_round_mode32 = program.settings.fp_round_mode32;
|
||||
};
|
||||
switch (stage) {
|
||||
case Shader::Stage::Export: {
|
||||
case Stage::Local: {
|
||||
BuildCommon(regs.ls_program);
|
||||
if (regs.stage_enable.IsStageEnabled(static_cast<u32>(Stage::Hull))) {
|
||||
info.ls_info.links_with_tcs = true;
|
||||
Shader::TessellationDataConstantBuffer tess_constants;
|
||||
const auto* pgm = regs.ProgramForStage(static_cast<u32>(Stage::Hull));
|
||||
const auto params = Liverpool::GetParams(*pgm);
|
||||
const auto& hull_info = program_cache.at(params.hash)->info;
|
||||
hull_info.ReadTessConstantBuffer(tess_constants);
|
||||
info.ls_info.ls_stride = tess_constants.ls_stride;
|
||||
}
|
||||
break;
|
||||
}
|
||||
case Stage::Hull: {
|
||||
BuildCommon(regs.hs_program);
|
||||
info.hs_info.num_input_control_points = regs.ls_hs_config.hs_input_control_points.Value();
|
||||
info.hs_info.num_threads = regs.ls_hs_config.hs_output_control_points.Value();
|
||||
info.hs_info.tess_type = regs.tess_config.type;
|
||||
|
||||
// We need to initialize most hs_info fields after finding the V# with tess constants
|
||||
break;
|
||||
}
|
||||
case Stage::Export: {
|
||||
BuildCommon(regs.es_program);
|
||||
info.es_info.vertex_data_size = regs.vgt_esgs_ring_itemsize;
|
||||
break;
|
||||
}
|
||||
case Shader::Stage::Vertex: {
|
||||
case Stage::Vertex: {
|
||||
BuildCommon(regs.vs_program);
|
||||
GatherVertexOutputs(info.vs_info, regs.vs_output_control);
|
||||
info.vs_info.emulate_depth_negative_one_to_one =
|
||||
!instance.IsDepthClipControlSupported() &&
|
||||
regs.clipper_control.clip_space == Liverpool::ClipSpace::MinusWToW;
|
||||
if (l_stage == LogicalStage::TessellationEval) {
|
||||
info.vs_info.tess_type = regs.tess_config.type;
|
||||
info.vs_info.tess_topology = regs.tess_config.topology;
|
||||
info.vs_info.tess_partitioning = regs.tess_config.partitioning;
|
||||
}
|
||||
break;
|
||||
}
|
||||
case Shader::Stage::Geometry: {
|
||||
case Stage::Geometry: {
|
||||
BuildCommon(regs.gs_program);
|
||||
auto& gs_info = info.gs_info;
|
||||
gs_info.output_vertices = regs.vgt_gs_max_vert_out;
|
||||
|
@ -121,7 +150,7 @@ Shader::RuntimeInfo PipelineCache::BuildRuntimeInfo(Shader::Stage stage) {
|
|||
DumpShader(gs_info.vs_copy, gs_info.vs_copy_hash, Shader::Stage::Vertex, 0, "copy.bin");
|
||||
break;
|
||||
}
|
||||
case Shader::Stage::Fragment: {
|
||||
case Stage::Fragment: {
|
||||
BuildCommon(regs.ps_program);
|
||||
info.fs_info.en_flags = regs.ps_input_ena;
|
||||
info.fs_info.addr_flags = regs.ps_input_addr;
|
||||
|
@ -143,7 +172,7 @@ Shader::RuntimeInfo PipelineCache::BuildRuntimeInfo(Shader::Stage stage) {
|
|||
}
|
||||
break;
|
||||
}
|
||||
case Shader::Stage::Compute: {
|
||||
case Stage::Compute: {
|
||||
const auto& cs_pgm = regs.cs_program;
|
||||
info.num_user_data = cs_pgm.settings.num_user_regs;
|
||||
info.num_allocated_vgprs = regs.cs_program.settings.num_vgprs * 4;
|
||||
|
@ -277,6 +306,11 @@ bool PipelineCache::RefreshGraphicsKey() {
|
|||
key.mrt_swizzles.fill(Liverpool::ColorBuffer::SwapMode::Standard);
|
||||
key.vertex_buffer_formats.fill(vk::Format::eUndefined);
|
||||
|
||||
key.patch_control_points = 0;
|
||||
if (regs.stage_enable.hs_en.Value()) {
|
||||
key.patch_control_points = regs.ls_hs_config.hs_input_control_points.Value();
|
||||
}
|
||||
|
||||
// First pass of bindings check to idenitfy formats and swizzles and pass them to rhe shader
|
||||
// recompiler.
|
||||
for (auto cb = 0u; cb < Liverpool::NumColorBuffers; ++cb) {
|
||||
|
@ -305,7 +339,7 @@ bool PipelineCache::RefreshGraphicsKey() {
|
|||
fetch_shader = std::nullopt;
|
||||
|
||||
Shader::Backend::Bindings binding{};
|
||||
const auto& TryBindStageRemap = [&](Shader::Stage stage_in, Shader::Stage stage_out) -> bool {
|
||||
const auto& TryBindStage = [&](Shader::Stage stage_in, Shader::LogicalStage 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)) {
|
||||
|
@ -332,23 +366,23 @@ bool PipelineCache::RefreshGraphicsKey() {
|
|||
auto params = Liverpool::GetParams(*pgm);
|
||||
std::optional<Shader::Gcn::FetchShaderData> fetch_shader_;
|
||||
std::tie(infos[stage_out_idx], modules[stage_out_idx], fetch_shader_,
|
||||
key.stage_hashes[stage_out_idx]) = GetProgram(stage_in, params, binding);
|
||||
key.stage_hashes[stage_out_idx]) =
|
||||
GetProgram(stage_in, stage_out, params, binding);
|
||||
if (fetch_shader_) {
|
||||
fetch_shader = fetch_shader_;
|
||||
}
|
||||
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);
|
||||
infos.fill(nullptr);
|
||||
TryBindStage(Stage::Fragment, LogicalStage::Fragment);
|
||||
|
||||
const auto* fs_info = infos[static_cast<u32>(Shader::Stage::Fragment)];
|
||||
const auto* fs_info = infos[static_cast<u32>(LogicalStage::Fragment)];
|
||||
key.mrt_mask = fs_info ? fs_info->mrt_mask : 0u;
|
||||
|
||||
switch (regs.stage_enable.raw) {
|
||||
|
@ -356,22 +390,36 @@ bool PipelineCache::RefreshGraphicsKey() {
|
|||
if (!instance.IsGeometryStageSupported() || !IsGsFeaturesSupported()) {
|
||||
return false;
|
||||
}
|
||||
if (!TryBindStageRemap(Shader::Stage::Export, Shader::Stage::Vertex)) {
|
||||
if (!TryBindStage(Stage::Export, LogicalStage::Vertex)) {
|
||||
return false;
|
||||
}
|
||||
if (!TryBindStage(Shader::Stage::Geometry)) {
|
||||
if (!TryBindStage(Stage::Geometry, LogicalStage::Geometry)) {
|
||||
return false;
|
||||
}
|
||||
break;
|
||||
}
|
||||
case Liverpool::ShaderStageEnable::VgtStages::LsHs: {
|
||||
if (!instance.IsTessellationSupported()) {
|
||||
break;
|
||||
}
|
||||
if (!TryBindStage(Stage::Hull, LogicalStage::TessellationControl)) {
|
||||
return false;
|
||||
}
|
||||
if (!TryBindStage(Stage::Vertex, LogicalStage::TessellationEval)) {
|
||||
return false;
|
||||
}
|
||||
if (!TryBindStage(Stage::Local, LogicalStage::Vertex)) {
|
||||
return false;
|
||||
}
|
||||
break;
|
||||
}
|
||||
default: {
|
||||
TryBindStage(Shader::Stage::Vertex);
|
||||
infos[static_cast<u32>(Shader::Stage::Geometry)] = nullptr;
|
||||
TryBindStage(Stage::Vertex, LogicalStage::Vertex);
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
const auto vs_info = infos[static_cast<u32>(Shader::Stage::Vertex)];
|
||||
const auto vs_info = infos[static_cast<u32>(Shader::LogicalStage::Vertex)];
|
||||
if (vs_info && fetch_shader && !instance.IsVertexInputDynamicState()) {
|
||||
u32 vertex_binding = 0;
|
||||
for (const auto& attrib : fetch_shader->attributes) {
|
||||
|
@ -424,19 +472,18 @@ bool PipelineCache::RefreshGraphicsKey() {
|
|||
key.num_samples = num_samples;
|
||||
|
||||
return true;
|
||||
}
|
||||
} // namespace Vulkan
|
||||
|
||||
bool PipelineCache::RefreshComputeKey() {
|
||||
Shader::Backend::Bindings binding{};
|
||||
const auto* cs_pgm = &liverpool->regs.cs_program;
|
||||
const auto cs_params = Liverpool::GetParams(*cs_pgm);
|
||||
std::tie(infos[0], modules[0], fetch_shader, compute_key.value) =
|
||||
GetProgram(Shader::Stage::Compute, cs_params, binding);
|
||||
GetProgram(Shader::Stage::Compute, LogicalStage::Compute, cs_params, binding);
|
||||
return true;
|
||||
}
|
||||
|
||||
vk::ShaderModule PipelineCache::CompileModule(Shader::Info& info,
|
||||
const Shader::RuntimeInfo& runtime_info,
|
||||
vk::ShaderModule PipelineCache::CompileModule(Shader::Info& info, Shader::RuntimeInfo& runtime_info,
|
||||
std::span<const u32> code, size_t perm_idx,
|
||||
Shader::Backend::Bindings& binding) {
|
||||
LOG_INFO(Render_Vulkan, "Compiling {} shader {:#x} {}", info.stage, info.pgm_hash,
|
||||
|
@ -461,19 +508,19 @@ vk::ShaderModule PipelineCache::CompileModule(Shader::Info& info,
|
|||
const auto name = fmt::format("{}_{:#018x}_{}", info.stage, info.pgm_hash, perm_idx);
|
||||
Vulkan::SetObjectName(instance.GetDevice(), module, name);
|
||||
if (Config::collectShadersForDebug()) {
|
||||
DebugState.CollectShader(name, module, spv, code, patch ? *patch : std::span<const u32>{},
|
||||
is_patched);
|
||||
DebugState.CollectShader(name, info.l_stage, module, spv, code,
|
||||
patch ? *patch : std::span<const u32>{}, is_patched);
|
||||
}
|
||||
return module;
|
||||
}
|
||||
|
||||
std::tuple<const Shader::Info*, vk::ShaderModule, std::optional<Shader::Gcn::FetchShaderData>, u64>
|
||||
PipelineCache::GetProgram(Shader::Stage stage, Shader::ShaderParams params,
|
||||
Shader::Backend::Bindings& binding) {
|
||||
const auto runtime_info = BuildRuntimeInfo(stage);
|
||||
PipelineCache::Result PipelineCache::GetProgram(Stage stage, LogicalStage l_stage,
|
||||
Shader::ShaderParams params,
|
||||
Shader::Backend::Bindings& binding) {
|
||||
auto runtime_info = BuildRuntimeInfo(stage, l_stage);
|
||||
auto [it_pgm, new_program] = program_cache.try_emplace(params.hash);
|
||||
if (new_program) {
|
||||
it_pgm.value() = std::make_unique<Program>(stage, params);
|
||||
it_pgm.value() = std::make_unique<Program>(stage, l_stage, params);
|
||||
auto& program = it_pgm.value();
|
||||
auto start = binding;
|
||||
const auto module = CompileModule(program->info, runtime_info, params.code, 0, binding);
|
||||
|
@ -492,7 +539,7 @@ PipelineCache::GetProgram(Shader::Stage stage, Shader::ShaderParams params,
|
|||
|
||||
const auto it = std::ranges::find(program->modules, spec, &Program::Module::spec);
|
||||
if (it == program->modules.end()) {
|
||||
auto new_info = Shader::Info(stage, params);
|
||||
auto new_info = Shader::Info(stage, l_stage, params);
|
||||
module = CompileModule(new_info, runtime_info, params.code, perm_idx, binding);
|
||||
program->AddPermut(module, std::move(spec));
|
||||
} else {
|
||||
|
|
|
@ -34,11 +34,13 @@ struct Program {
|
|||
vk::ShaderModule module;
|
||||
Shader::StageSpecialization spec;
|
||||
};
|
||||
using ModuleList = boost::container::small_vector<Module, 8>;
|
||||
|
||||
Shader::Info info;
|
||||
boost::container::small_vector<Module, 8> modules;
|
||||
ModuleList modules;
|
||||
|
||||
explicit Program(Shader::Stage stage, Shader::ShaderParams params) : info{stage, params} {}
|
||||
explicit Program(Shader::Stage stage, Shader::LogicalStage l_stage, Shader::ShaderParams params)
|
||||
: info{stage, l_stage, params} {}
|
||||
|
||||
void AddPermut(vk::ShaderModule module, const Shader::StageSpecialization&& spec) {
|
||||
modules.emplace_back(module, std::move(spec));
|
||||
|
@ -55,10 +57,10 @@ public:
|
|||
|
||||
const ComputePipeline* GetComputePipeline();
|
||||
|
||||
std::tuple<const Shader::Info*, vk::ShaderModule, std::optional<Shader::Gcn::FetchShaderData>,
|
||||
u64>
|
||||
GetProgram(Shader::Stage stage, Shader::ShaderParams params,
|
||||
Shader::Backend::Bindings& binding);
|
||||
using Result = std::tuple<const Shader::Info*, vk::ShaderModule,
|
||||
std::optional<Shader::Gcn::FetchShaderData>, u64>;
|
||||
Result GetProgram(Shader::Stage stage, Shader::LogicalStage l_stage,
|
||||
Shader::ShaderParams params, Shader::Backend::Bindings& binding);
|
||||
|
||||
std::optional<vk::ShaderModule> ReplaceShader(vk::ShaderModule module,
|
||||
std::span<const u32> spv_code);
|
||||
|
@ -71,10 +73,10 @@ private:
|
|||
std::string_view ext);
|
||||
std::optional<std::vector<u32>> GetShaderPatch(u64 hash, Shader::Stage stage, size_t perm_idx,
|
||||
std::string_view ext);
|
||||
vk::ShaderModule CompileModule(Shader::Info& info, const Shader::RuntimeInfo& runtime_info,
|
||||
vk::ShaderModule CompileModule(Shader::Info& info, Shader::RuntimeInfo& runtime_info,
|
||||
std::span<const u32> code, size_t perm_idx,
|
||||
Shader::Backend::Bindings& binding);
|
||||
Shader::RuntimeInfo BuildRuntimeInfo(Shader::Stage stage);
|
||||
Shader::RuntimeInfo BuildRuntimeInfo(Shader::Stage stage, Shader::LogicalStage l_stage);
|
||||
|
||||
private:
|
||||
const Instance& instance;
|
||||
|
|
|
@ -14,9 +14,10 @@ class BufferCache;
|
|||
|
||||
namespace Vulkan {
|
||||
|
||||
static constexpr auto gp_stage_flags = vk::ShaderStageFlagBits::eVertex |
|
||||
vk::ShaderStageFlagBits::eGeometry |
|
||||
vk::ShaderStageFlagBits::eFragment;
|
||||
static constexpr auto gp_stage_flags =
|
||||
vk::ShaderStageFlagBits::eVertex | vk::ShaderStageFlagBits::eTessellationControl |
|
||||
vk::ShaderStageFlagBits::eTessellationEvaluation | vk::ShaderStageFlagBits::eGeometry |
|
||||
vk::ShaderStageFlagBits::eFragment;
|
||||
|
||||
class Instance;
|
||||
class Scheduler;
|
||||
|
@ -37,6 +38,7 @@ public:
|
|||
}
|
||||
|
||||
auto GetStages() const {
|
||||
static_assert(static_cast<u32>(Shader::LogicalStage::Compute) == Shader::MaxStageTypes - 1);
|
||||
if (is_compute) {
|
||||
return std::span{stages.cend() - 1, stages.cend()};
|
||||
} else {
|
||||
|
@ -44,7 +46,7 @@ public:
|
|||
}
|
||||
}
|
||||
|
||||
const Shader::Info& GetStage(Shader::Stage stage) const noexcept {
|
||||
const Shader::Info& GetStage(Shader::LogicalStage stage) const noexcept {
|
||||
return *stages[u32(stage)];
|
||||
}
|
||||
|
||||
|
|
|
@ -4,6 +4,7 @@
|
|||
#include "common/config.h"
|
||||
#include "common/debug.h"
|
||||
#include "core/memory.h"
|
||||
#include "shader_recompiler/runtime_info.h"
|
||||
#include "video_core/amdgpu/liverpool.h"
|
||||
#include "video_core/renderer_vulkan/vk_instance.h"
|
||||
#include "video_core/renderer_vulkan/vk_rasterizer.h"
|
||||
|
@ -48,10 +49,6 @@ void Rasterizer::CpSync() {
|
|||
|
||||
bool Rasterizer::FilterDraw() {
|
||||
const auto& regs = liverpool->regs;
|
||||
// Tessellation is unsupported so skip the draw to avoid locking up the driver.
|
||||
if (regs.primitive_type == AmdGpu::PrimitiveType::PatchPrimitive) {
|
||||
return false;
|
||||
}
|
||||
// There are several cases (e.g. FCE, FMask/HTile decompression) where we don't need to do an
|
||||
// actual draw hence can skip pipeline creation.
|
||||
if (regs.color_control.mode == Liverpool::ColorControl::OperationMode::EliminateFastClear) {
|
||||
|
@ -214,7 +211,7 @@ void Rasterizer::Draw(bool is_indexed, u32 index_offset) {
|
|||
return;
|
||||
}
|
||||
|
||||
const auto& vs_info = pipeline->GetStage(Shader::Stage::Vertex);
|
||||
const auto& vs_info = pipeline->GetStage(Shader::LogicalStage::Vertex);
|
||||
const auto& fetch_shader = pipeline->GetFetchShader();
|
||||
buffer_cache.BindVertexBuffers(vs_info, fetch_shader);
|
||||
const u32 num_indices = buffer_cache.BindIndexBuffer(is_indexed, index_offset);
|
||||
|
@ -271,7 +268,7 @@ void Rasterizer::DrawIndirect(bool is_indexed, VAddr arg_address, u32 offset, u3
|
|||
return;
|
||||
}
|
||||
|
||||
const auto& vs_info = pipeline->GetStage(Shader::Stage::Vertex);
|
||||
const auto& vs_info = pipeline->GetStage(Shader::LogicalStage::Vertex);
|
||||
const auto& fetch_shader = pipeline->GetFetchShader();
|
||||
buffer_cache.BindVertexBuffers(vs_info, fetch_shader);
|
||||
buffer_cache.BindIndexBuffer(is_indexed, 0);
|
||||
|
@ -326,7 +323,7 @@ void Rasterizer::DispatchDirect() {
|
|||
return;
|
||||
}
|
||||
|
||||
const auto& cs = pipeline->GetStage(Shader::Stage::Compute);
|
||||
const auto& cs = pipeline->GetStage(Shader::LogicalStage::Compute);
|
||||
if (ExecuteShaderHLE(cs, liverpool->regs, *this)) {
|
||||
return;
|
||||
}
|
||||
|
@ -387,7 +384,7 @@ bool Rasterizer::BindResources(const Pipeline* pipeline) {
|
|||
const auto& regs = liverpool->regs;
|
||||
|
||||
if (pipeline->IsCompute()) {
|
||||
const auto& info = pipeline->GetStage(Shader::Stage::Compute);
|
||||
const auto& info = pipeline->GetStage(Shader::LogicalStage::Compute);
|
||||
|
||||
// Most of the time when a metadata is updated with a shader it gets cleared. It means
|
||||
// we can skip the whole dispatch and update the tracked state instead. Also, it is not
|
||||
|
|
Loading…
Add table
Add a link
Reference in a new issue