mirror of
https://github.com/KytyPS5/KytyPS5.git
synced 2026-10-28 22:06:59 +01:00
renderer: expand rectangle lists with tessellation
Remove the legacy NGG rectangle workaround and its configuration toggle.
This commit is contained in:
@@ -89,10 +89,6 @@ bool RenderDocEnabled() {
|
|||||||
return g_config->renderdoc_enabled;
|
return g_config->renderdoc_enabled;
|
||||||
}
|
}
|
||||||
|
|
||||||
bool NggRectlistDrawEnabled() {
|
|
||||||
return g_config->ngg_rectlist_draw_enabled;
|
|
||||||
}
|
|
||||||
|
|
||||||
bool ReadbackLinearImagesEnabled() {
|
bool ReadbackLinearImagesEnabled() {
|
||||||
return g_config->readback_linear_images;
|
return g_config->readback_linear_images;
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -35,7 +35,6 @@ struct ConfigOptions {
|
|||||||
ProfilerDirection profiler_direction = ProfilerDirection::None;
|
ProfilerDirection profiler_direction = ProfilerDirection::None;
|
||||||
bool spirv_debug_printf_enabled = false;
|
bool spirv_debug_printf_enabled = false;
|
||||||
bool renderdoc_enabled = false;
|
bool renderdoc_enabled = false;
|
||||||
bool ngg_rectlist_draw_enabled = true;
|
|
||||||
bool readback_linear_images = false;
|
bool readback_linear_images = false;
|
||||||
};
|
};
|
||||||
|
|
||||||
@@ -64,7 +63,6 @@ ProfilerDirection GetProfilerDirection();
|
|||||||
bool SpirvDebugPrintfEnabled();
|
bool SpirvDebugPrintfEnabled();
|
||||||
|
|
||||||
bool RenderDocEnabled();
|
bool RenderDocEnabled();
|
||||||
bool NggRectlistDrawEnabled();
|
|
||||||
bool ReadbackLinearImagesEnabled();
|
bool ReadbackLinearImagesEnabled();
|
||||||
|
|
||||||
} // namespace Config
|
} // namespace Config
|
||||||
|
|||||||
@@ -154,8 +154,9 @@ PipelineCache::GraphicsPipeline& PipelineCache::CreateGraphicsPipeline(
|
|||||||
for (uint32_t i = 0; i < RENDER_COLOR_ATTACHMENTS_MAX; i++) {
|
for (uint32_t i = 0; i < RENDER_COLOR_ATTACHMENTS_MAX; i++) {
|
||||||
static_params.color_mask[i] = color_mask[i];
|
static_params.color_mask[i] = color_mask[i];
|
||||||
}
|
}
|
||||||
static_params.cull_back = mc.cull_back;
|
const bool rect_list = topology == vk::PrimitiveTopology::ePatchList;
|
||||||
static_params.cull_front = mc.cull_front;
|
static_params.cull_back = !rect_list && mc.cull_back;
|
||||||
|
static_params.cull_front = !rect_list && mc.cull_front;
|
||||||
static_params.face = mc.face;
|
static_params.face = mc.face;
|
||||||
|
|
||||||
for (uint32_t i = 0; i < color_count; i++) {
|
for (uint32_t i = 0; i < color_count; i++) {
|
||||||
|
|||||||
@@ -14,6 +14,7 @@
|
|||||||
#include "graphics/host_gpu/renderer/renderTarget.h"
|
#include "graphics/host_gpu/renderer/renderTarget.h"
|
||||||
#include "graphics/host_gpu/vulkanCommon.h"
|
#include "graphics/host_gpu/vulkanCommon.h"
|
||||||
#include "graphics/shader/recompiler/ir/ShaderIR.h"
|
#include "graphics/shader/recompiler/ir/ShaderIR.h"
|
||||||
|
#include "graphics/shader/rectListShader.h"
|
||||||
#include "graphics/shader/shader.h"
|
#include "graphics/shader/shader.h"
|
||||||
|
|
||||||
#include <algorithm>
|
#include <algorithm>
|
||||||
@@ -463,7 +464,11 @@ void CreatePipelineInternal(
|
|||||||
uint32_t ps_hash0, uint32_t ps_crc32, bool ps_active) {
|
uint32_t ps_hash0, uint32_t ps_crc32, bool ps_active) {
|
||||||
EXIT_IF(ps_active && ps_input_info == nullptr);
|
EXIT_IF(ps_active && ps_input_info == nullptr);
|
||||||
|
|
||||||
|
const bool rect_list = static_params.topology == vk::PrimitiveTopology::ePatchList;
|
||||||
|
|
||||||
vk::ShaderModule vert_shader_module = nullptr;
|
vk::ShaderModule vert_shader_module = nullptr;
|
||||||
|
vk::ShaderModule tess_control_shader_module = nullptr;
|
||||||
|
vk::ShaderModule tess_eval_shader_module = nullptr;
|
||||||
vk::ShaderModule frag_shader_module = nullptr;
|
vk::ShaderModule frag_shader_module = nullptr;
|
||||||
|
|
||||||
vk::ShaderModuleCreateInfo create_info {};
|
vk::ShaderModuleCreateInfo create_info {};
|
||||||
@@ -491,8 +496,33 @@ void CreatePipelineInternal(
|
|||||||
}
|
}
|
||||||
EXIT_NOT_IMPLEMENTED(result != vk::Result::eSuccess);
|
EXIT_NOT_IMPLEMENTED(result != vk::Result::eSuccess);
|
||||||
}
|
}
|
||||||
|
if (rect_list) {
|
||||||
|
const auto shaders =
|
||||||
|
BuildRectListShaders(vs_input_info, ps_active ? ps_input_info : nullptr);
|
||||||
|
create_info.codeSize = shaders.control.size() * 4;
|
||||||
|
create_info.pCode = shaders.control.data();
|
||||||
|
result =
|
||||||
|
graphics.device.createShaderModule(&create_info, nullptr, &tess_control_shader_module);
|
||||||
|
if (graphics_debug_dump_enabled()) {
|
||||||
|
LOGF("PipelineTrace: vkCreateShaderModule RectList TCS done result=%s module=%p\n",
|
||||||
|
VulkanToString(result).c_str(), static_cast<void*>(tess_control_shader_module));
|
||||||
|
}
|
||||||
|
EXIT_NOT_IMPLEMENTED(result != vk::Result::eSuccess);
|
||||||
|
|
||||||
|
create_info.codeSize = shaders.evaluation.size() * 4;
|
||||||
|
create_info.pCode = shaders.evaluation.data();
|
||||||
|
result =
|
||||||
|
graphics.device.createShaderModule(&create_info, nullptr, &tess_eval_shader_module);
|
||||||
|
if (graphics_debug_dump_enabled()) {
|
||||||
|
LOGF("PipelineTrace: vkCreateShaderModule RectList TES done result=%s module=%p\n",
|
||||||
|
VulkanToString(result).c_str(), static_cast<void*>(tess_eval_shader_module));
|
||||||
|
}
|
||||||
|
EXIT_NOT_IMPLEMENTED(result != vk::Result::eSuccess);
|
||||||
|
}
|
||||||
|
|
||||||
EXIT_NOT_IMPLEMENTED(vert_shader_module == nullptr);
|
EXIT_NOT_IMPLEMENTED(vert_shader_module == nullptr);
|
||||||
|
EXIT_NOT_IMPLEMENTED(
|
||||||
|
rect_list && (tess_control_shader_module == nullptr || tess_eval_shader_module == nullptr));
|
||||||
EXIT_NOT_IMPLEMENTED(ps_active && frag_shader_module == nullptr);
|
EXIT_NOT_IMPLEMENTED(ps_active && frag_shader_module == nullptr);
|
||||||
|
|
||||||
vk::PipelineShaderStageCreateInfo vert_shader_stage_info {};
|
vk::PipelineShaderStageCreateInfo vert_shader_stage_info {};
|
||||||
@@ -525,9 +555,28 @@ void CreatePipelineInternal(
|
|||||||
frag_shader_stage_info);
|
frag_shader_stage_info);
|
||||||
}
|
}
|
||||||
|
|
||||||
vk::PipelineShaderStageCreateInfo shader_stages[] = {vert_shader_stage_info,
|
vk::PipelineShaderStageCreateInfo tess_control_shader_stage_info {};
|
||||||
frag_shader_stage_info};
|
tess_control_shader_stage_info.sType = vk::StructureType::ePipelineShaderStageCreateInfo;
|
||||||
const uint32_t shader_stage_count = ps_active ? 2u : 1u;
|
tess_control_shader_stage_info.stage = vk::ShaderStageFlagBits::eTessellationControl;
|
||||||
|
tess_control_shader_stage_info.module = tess_control_shader_module;
|
||||||
|
tess_control_shader_stage_info.pName = "main";
|
||||||
|
|
||||||
|
vk::PipelineShaderStageCreateInfo tess_eval_shader_stage_info {};
|
||||||
|
tess_eval_shader_stage_info.sType = vk::StructureType::ePipelineShaderStageCreateInfo;
|
||||||
|
tess_eval_shader_stage_info.stage = vk::ShaderStageFlagBits::eTessellationEvaluation;
|
||||||
|
tess_eval_shader_stage_info.module = tess_eval_shader_module;
|
||||||
|
tess_eval_shader_stage_info.pName = "main";
|
||||||
|
|
||||||
|
vk::PipelineShaderStageCreateInfo shader_stages[4] = {};
|
||||||
|
uint32_t shader_stage_count = 0;
|
||||||
|
shader_stages[shader_stage_count++] = vert_shader_stage_info;
|
||||||
|
if (rect_list) {
|
||||||
|
shader_stages[shader_stage_count++] = tess_control_shader_stage_info;
|
||||||
|
shader_stages[shader_stage_count++] = tess_eval_shader_stage_info;
|
||||||
|
}
|
||||||
|
if (ps_active) {
|
||||||
|
shader_stages[shader_stage_count++] = frag_shader_stage_info;
|
||||||
|
}
|
||||||
|
|
||||||
vk::VertexInputAttributeDescription input_attr[ShaderVertexInputInfo::RES_MAX];
|
vk::VertexInputAttributeDescription input_attr[ShaderVertexInputInfo::RES_MAX];
|
||||||
vk::VertexInputBindingDescription input_desc[ShaderVertexInputInfo::RES_MAX];
|
vk::VertexInputBindingDescription input_desc[ShaderVertexInputInfo::RES_MAX];
|
||||||
@@ -929,7 +978,10 @@ void CreatePipelineInternal(
|
|||||||
pipeline_info.pStages = shader_stages;
|
pipeline_info.pStages = shader_stages;
|
||||||
pipeline_info.pVertexInputState = &vertex_input_info;
|
pipeline_info.pVertexInputState = &vertex_input_info;
|
||||||
pipeline_info.pInputAssemblyState = &input_assembly;
|
pipeline_info.pInputAssemblyState = &input_assembly;
|
||||||
pipeline_info.pTessellationState = nullptr;
|
vk::PipelineTessellationStateCreateInfo tessellation_state {};
|
||||||
|
tessellation_state.sType = vk::StructureType::ePipelineTessellationStateCreateInfo;
|
||||||
|
tessellation_state.patchControlPoints = 3;
|
||||||
|
pipeline_info.pTessellationState = (rect_list ? &tessellation_state : nullptr);
|
||||||
pipeline_info.pViewportState = &viewport_state;
|
pipeline_info.pViewportState = &viewport_state;
|
||||||
pipeline_info.pRasterizationState = &rasterizer;
|
pipeline_info.pRasterizationState = &rasterizer;
|
||||||
pipeline_info.pMultisampleState = &multisampling;
|
pipeline_info.pMultisampleState = &multisampling;
|
||||||
@@ -968,6 +1020,12 @@ void CreatePipelineInternal(
|
|||||||
if (frag_shader_module != nullptr) {
|
if (frag_shader_module != nullptr) {
|
||||||
graphics.device.destroyShaderModule(frag_shader_module, nullptr);
|
graphics.device.destroyShaderModule(frag_shader_module, nullptr);
|
||||||
}
|
}
|
||||||
|
if (tess_control_shader_module != nullptr) {
|
||||||
|
graphics.device.destroyShaderModule(tess_control_shader_module, nullptr);
|
||||||
|
}
|
||||||
|
if (tess_eval_shader_module != nullptr) {
|
||||||
|
graphics.device.destroyShaderModule(tess_eval_shader_module, nullptr);
|
||||||
|
}
|
||||||
graphics.device.destroyShaderModule(vert_shader_module, nullptr);
|
graphics.device.destroyShaderModule(vert_shader_module, nullptr);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -2,7 +2,6 @@
|
|||||||
|
|
||||||
#include "common/assert.h"
|
#include "common/assert.h"
|
||||||
#include "common/common.h"
|
#include "common/common.h"
|
||||||
#include "common/emulatorConfig.h"
|
|
||||||
#include "common/file.h"
|
#include "common/file.h"
|
||||||
#include "common/logging/log.h"
|
#include "common/logging/log.h"
|
||||||
#include "common/profiler.h"
|
#include "common/profiler.h"
|
||||||
@@ -651,8 +650,6 @@ static bool ConsumeMetadataColorOperation(const RenderCommandBuffer& buffer) {
|
|||||||
|
|
||||||
struct DrawEmitInfo {
|
struct DrawEmitInfo {
|
||||||
bool indexed = false;
|
bool indexed = false;
|
||||||
bool draw_prim7_as_ngg = false;
|
|
||||||
uint32_t draw_vertex_count = 0;
|
|
||||||
int32_t vertex_offset = 0;
|
int32_t vertex_offset = 0;
|
||||||
uint32_t first_vertex = 0;
|
uint32_t first_vertex = 0;
|
||||||
};
|
};
|
||||||
@@ -767,7 +764,7 @@ static void SetDrawDebugPhase(RenderCommandBuffer& buffer, uint64_t submit_id,
|
|||||||
draw.flags, draw.instance_count, draw.first_instance);
|
draw.flags, draw.instance_count, draw.first_instance);
|
||||||
}
|
}
|
||||||
|
|
||||||
static bool GetDrawTopology(const HW::UserConfig& ucfg, bool auto_draw, bool use_ngg_rectlist_draw,
|
static bool GetDrawTopology(const HW::UserConfig& ucfg, bool auto_draw,
|
||||||
vk::PrimitiveTopology& topology) {
|
vk::PrimitiveTopology& topology) {
|
||||||
|
|
||||||
topology = vk::PrimitiveTopology::ePointList;
|
topology = vk::PrimitiveTopology::ePointList;
|
||||||
@@ -791,8 +788,7 @@ static bool GetDrawTopology(const HW::UserConfig& ucfg, bool auto_draw, bool use
|
|||||||
topology = vk::PrimitiveTopology::eTriangleStrip;
|
topology = vk::PrimitiveTopology::eTriangleStrip;
|
||||||
break;
|
break;
|
||||||
case Prospero::PrimitiveType::kRectList:
|
case Prospero::PrimitiveType::kRectList:
|
||||||
topology = (auto_draw && use_ngg_rectlist_draw ? vk::PrimitiveTopology::eTriangleStrip
|
topology = vk::PrimitiveTopology::ePatchList;
|
||||||
: vk::PrimitiveTopology::eTriangleList);
|
|
||||||
break;
|
break;
|
||||||
case Prospero::PrimitiveType::kRectListLegacy:
|
case Prospero::PrimitiveType::kRectListLegacy:
|
||||||
if (!auto_draw) {
|
if (!auto_draw) {
|
||||||
@@ -991,20 +987,6 @@ static void LogDrawStateIfNeeded(const RenderCommandBuffer& buffer, const DrawCa
|
|||||||
// LogDrawTextureState(draw.name, state.color_info[0], state.ps_input_info);
|
// LogDrawTextureState(draw.name, state.color_info[0], state.ps_input_info);
|
||||||
}
|
}
|
||||||
|
|
||||||
static bool IsHostExpandedRectListDrawSupported(const ShaderVertexInputInfo& vs_input_info,
|
|
||||||
const DrawCallInfo& draw,
|
|
||||||
const DrawEmitInfo& emit) {
|
|
||||||
if (!emit.draw_prim7_as_ngg) {
|
|
||||||
return true;
|
|
||||||
}
|
|
||||||
|
|
||||||
if (vs_input_info.buffers_num != 0) {
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
|
|
||||||
return draw.index_count == 3 || draw.index_count == emit.draw_vertex_count;
|
|
||||||
}
|
|
||||||
|
|
||||||
static void EmitDrawPrimitives(const HW::UserConfig& ucfg, vk::CommandBuffer vk_buffer,
|
static void EmitDrawPrimitives(const HW::UserConfig& ucfg, vk::CommandBuffer vk_buffer,
|
||||||
const ShaderVertexInputInfo& vs_input_info, const DrawCallInfo& draw,
|
const ShaderVertexInputInfo& vs_input_info, const DrawCallInfo& draw,
|
||||||
const DrawEmitInfo& emit) {
|
const DrawEmitInfo& emit) {
|
||||||
@@ -1017,22 +999,12 @@ static void EmitDrawPrimitives(const HW::UserConfig& ucfg, vk::CommandBuffer vk_
|
|||||||
case Prospero::PrimitiveType::kTriList:
|
case Prospero::PrimitiveType::kTriList:
|
||||||
case Prospero::PrimitiveType::kTriFan:
|
case Prospero::PrimitiveType::kTriFan:
|
||||||
case Prospero::PrimitiveType::kTriStrip:
|
case Prospero::PrimitiveType::kTriStrip:
|
||||||
if (emit.indexed) {
|
|
||||||
vk_buffer.drawIndexed(draw.index_count, draw.instance_count, 0, emit.vertex_offset,
|
|
||||||
draw.first_instance);
|
|
||||||
} else {
|
|
||||||
vk_buffer.draw(draw.index_count, draw.instance_count, emit.first_vertex,
|
|
||||||
draw.first_instance);
|
|
||||||
}
|
|
||||||
break;
|
|
||||||
case Prospero::PrimitiveType::kRectList:
|
case Prospero::PrimitiveType::kRectList:
|
||||||
if (emit.indexed) {
|
if (emit.indexed) {
|
||||||
vk_buffer.drawIndexed(draw.index_count, draw.instance_count, 0, emit.vertex_offset,
|
vk_buffer.drawIndexed(draw.index_count, draw.instance_count, 0, emit.vertex_offset,
|
||||||
draw.first_instance);
|
draw.first_instance);
|
||||||
} else {
|
} else {
|
||||||
EXIT_NOT_IMPLEMENTED(
|
vk_buffer.draw(draw.index_count, draw.instance_count, emit.first_vertex,
|
||||||
!IsHostExpandedRectListDrawSupported(vs_input_info, draw, emit));
|
|
||||||
vk_buffer.draw(emit.draw_vertex_count, draw.instance_count, emit.first_vertex,
|
|
||||||
draw.first_instance);
|
draw.first_instance);
|
||||||
}
|
}
|
||||||
break;
|
break;
|
||||||
@@ -1201,7 +1173,7 @@ void RenderExecutor::DrawIndex(uint64_t submit_id, RenderCommandBuffer& buffer,
|
|||||||
hw_check(buffer);
|
hw_check(buffer);
|
||||||
|
|
||||||
vk::PrimitiveTopology topology = vk::PrimitiveTopology::ePointList;
|
vk::PrimitiveTopology topology = vk::PrimitiveTopology::ePointList;
|
||||||
if (!GetDrawTopology(ucfg, false, false, topology)) {
|
if (!GetDrawTopology(ucfg, false, topology)) {
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -1338,19 +1310,14 @@ void RenderExecutor::DrawAuto(uint64_t submit_id, RenderCommandBuffer& buffer, u
|
|||||||
}
|
}
|
||||||
|
|
||||||
vk::PrimitiveTopology topology = vk::PrimitiveTopology::ePointList;
|
vk::PrimitiveTopology topology = vk::PrimitiveTopology::ePointList;
|
||||||
const bool use_ngg_rectlist_draw = Config::NggRectlistDrawEnabled();
|
if (!GetDrawTopology(ucfg, true, topology)) {
|
||||||
|
|
||||||
if (!GetDrawTopology(ucfg, true, use_ngg_rectlist_draw, topology)) {
|
|
||||||
ResetBindings();
|
ResetBindings();
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
const bool draw_prim7_as_ngg =
|
|
||||||
(use_ngg_rectlist_draw &&
|
|
||||||
ucfg.GetPrimType() == Prospero::GpuEnumValue(Prospero::PrimitiveType::kRectList));
|
|
||||||
|
|
||||||
RefreshShaders(buffer, draw, false, state);
|
RefreshShaders(buffer, draw, false, state);
|
||||||
|
|
||||||
if (draw_prim7_as_ngg && state.vs_input_info.buffers_num == 0 &&
|
const bool rect_list = topology == vk::PrimitiveTopology::ePatchList;
|
||||||
|
if (rect_list && state.vs_input_info.buffers_num == 0 &&
|
||||||
state.vs_input_info.param_export_mask == 0 && state.ps_input_info.input_num != 0) {
|
state.vs_input_info.param_export_mask == 0 && state.ps_input_info.input_num != 0) {
|
||||||
if (graphics_debug_dump_enabled()) {
|
if (graphics_debug_dump_enabled()) {
|
||||||
LOGF("DrawIndexAuto: skipping rect-list draw with no VS param exports and PS inputs: "
|
LOGF("DrawIndexAuto: skipping rect-list draw with no VS param exports and PS inputs: "
|
||||||
@@ -1367,12 +1334,9 @@ void RenderExecutor::DrawAuto(uint64_t submit_id, RenderCommandBuffer& buffer, u
|
|||||||
Prospero::GpuEnumValue(Prospero::PrimitiveType::kRectListLegacy),
|
Prospero::GpuEnumValue(Prospero::PrimitiveType::kRectListLegacy),
|
||||||
0, nullptr);
|
0, nullptr);
|
||||||
|
|
||||||
const uint32_t draw_vertex_count = (draw_prim7_as_ngg ? 4u : index_count);
|
|
||||||
const auto vertex_offset = ResolveVertexOffset(ucfg.GetIndexOffset(), state.vs_input_info) +
|
const auto vertex_offset = ResolveVertexOffset(ucfg.GetIndexOffset(), state.vs_input_info) +
|
||||||
static_cast<int32_t>(first_vertex);
|
static_cast<int32_t>(first_vertex);
|
||||||
DrawEmitInfo emit {};
|
DrawEmitInfo emit {};
|
||||||
emit.draw_prim7_as_ngg = draw_prim7_as_ngg;
|
|
||||||
emit.draw_vertex_count = draw_vertex_count;
|
|
||||||
emit.first_vertex = static_cast<uint32_t>(vertex_offset);
|
emit.first_vertex = static_cast<uint32_t>(vertex_offset);
|
||||||
|
|
||||||
DrawIndexBufferSource index_source {};
|
DrawIndexBufferSource index_source {};
|
||||||
|
|||||||
@@ -19,7 +19,7 @@ static void AppendInstructionWords(std::vector<uint32_t>& section, const uint32_
|
|||||||
section.insert(section.end(), words + 1, words + words_num);
|
section.insert(section.end(), words + 1, words + words_num);
|
||||||
}
|
}
|
||||||
|
|
||||||
Builder::Builder() {
|
Builder::Builder(uint32_t version): m_version(version) {
|
||||||
m_debug.reserve(InitialSpirvSectionReserve);
|
m_debug.reserve(InitialSpirvSectionReserve);
|
||||||
m_annotations.reserve(InitialSpirvSectionReserve);
|
m_annotations.reserve(InitialSpirvSectionReserve);
|
||||||
m_types.reserve(InitialSpirvSectionReserve);
|
m_types.reserve(InitialSpirvSectionReserve);
|
||||||
@@ -138,7 +138,7 @@ std::vector<uint32_t> Builder::Build() const {
|
|||||||
m_debug.size() + m_annotations.size() + m_types.size() + m_functions.size());
|
m_debug.size() + m_annotations.size() + m_types.size() + m_functions.size());
|
||||||
|
|
||||||
module.push_back(0x07230203u);
|
module.push_back(0x07230203u);
|
||||||
module.push_back(0x00010300u);
|
module.push_back(m_version);
|
||||||
module.push_back(0u);
|
module.push_back(0u);
|
||||||
module.push_back(m_next_id);
|
module.push_back(m_next_id);
|
||||||
module.push_back(0u);
|
module.push_back(0u);
|
||||||
|
|||||||
@@ -10,7 +10,7 @@ namespace Libs::Graphics::ShaderRecompiler::Spirv {
|
|||||||
|
|
||||||
class Builder {
|
class Builder {
|
||||||
public:
|
public:
|
||||||
Builder();
|
explicit Builder(uint32_t version = 0x00010300u);
|
||||||
~Builder() = default;
|
~Builder() = default;
|
||||||
KYTY_CLASS_DEFAULT_COPY(Builder);
|
KYTY_CLASS_DEFAULT_COPY(Builder);
|
||||||
|
|
||||||
@@ -39,6 +39,7 @@ private:
|
|||||||
static void AppendString(std::vector<uint32_t>& words, const char* text);
|
static void AppendString(std::vector<uint32_t>& words, const char* text);
|
||||||
|
|
||||||
uint32_t m_next_id = 1;
|
uint32_t m_next_id = 1;
|
||||||
|
uint32_t m_version = 0;
|
||||||
std::vector<uint32_t> m_capabilities;
|
std::vector<uint32_t> m_capabilities;
|
||||||
std::vector<uint32_t> m_extensions;
|
std::vector<uint32_t> m_extensions;
|
||||||
std::vector<uint32_t> m_ext_inst_imports;
|
std::vector<uint32_t> m_ext_inst_imports;
|
||||||
|
|||||||
@@ -7,51 +7,30 @@ namespace Libs::Graphics::ShaderRecompiler::Spirv::Emitter {
|
|||||||
|
|
||||||
uint32_t PixelParameterMappedLocation(const EmitterState& state, uint32_t attr) {
|
uint32_t PixelParameterMappedLocation(const EmitterState& state, uint32_t attr) {
|
||||||
const auto* ps = state.pixel_input_info;
|
const auto* ps = state.pixel_input_info;
|
||||||
if (state.stage != ShaderType::Pixel || ps == nullptr || attr >= ps->input_num) {
|
if (state.stage != ShaderType::Pixel || ps == nullptr) {
|
||||||
return attr;
|
return attr;
|
||||||
}
|
}
|
||||||
// VINTRP ATTR selects the PS input slot. SPI_PS_INPUT_CNTL maps that slot to a
|
return ShaderPixelParameterMappedLocation(*ps, attr);
|
||||||
// VS parameter export, which is the SPIR-V location we must link against.
|
|
||||||
return ps->interpolator_settings[attr] & PsInputOffsetMask;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
uint32_t PixelParameterLocation(const EmitterState& state, uint32_t attr) {
|
uint32_t PixelParameterLocation(const EmitterState& state, uint32_t attr) {
|
||||||
bool used_locations[32] = {};
|
std::array<uint32_t, 32> active_inputs {};
|
||||||
|
uint32_t active_count = 0;
|
||||||
for (const auto& input: state.inputs) {
|
for (const auto& input: state.inputs) {
|
||||||
if (input.kind != IR::StageInputKind::Parameter) {
|
if (input.kind == IR::StageInputKind::Parameter) {
|
||||||
continue;
|
active_inputs[active_count++] = input.location;
|
||||||
}
|
|
||||||
|
|
||||||
auto location = PixelParameterMappedLocation(state, input.location);
|
|
||||||
if (location < std::size(used_locations) && used_locations[location]) {
|
|
||||||
auto fallback_location = input.location;
|
|
||||||
while (fallback_location < std::size(used_locations) &&
|
|
||||||
used_locations[fallback_location]) {
|
|
||||||
fallback_location++;
|
|
||||||
}
|
|
||||||
EXIT_NOT_IMPLEMENTED(fallback_location >= std::size(used_locations));
|
|
||||||
location = fallback_location;
|
|
||||||
}
|
|
||||||
|
|
||||||
if (input.location == attr) {
|
|
||||||
return location;
|
|
||||||
}
|
|
||||||
|
|
||||||
if (location < std::size(used_locations)) {
|
|
||||||
used_locations[location] = true;
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
return state.stage == ShaderType::Pixel && state.pixel_input_info != nullptr
|
||||||
return PixelParameterMappedLocation(state, attr);
|
? ShaderPixelParameterLocation(*state.pixel_input_info,
|
||||||
|
{active_inputs.data(), active_count}, attr)
|
||||||
|
: attr;
|
||||||
}
|
}
|
||||||
|
|
||||||
bool PixelParameterIsFlat(const EmitterState& state, uint32_t attr) {
|
bool PixelParameterIsFlat(const EmitterState& state, uint32_t attr) {
|
||||||
const auto* ps = state.pixel_input_info;
|
const auto* ps = state.pixel_input_info;
|
||||||
if (state.stage != ShaderType::Pixel || ps == nullptr || attr >= ps->input_num) {
|
return state.stage == ShaderType::Pixel && ps != nullptr &&
|
||||||
return false;
|
ShaderPixelParameterIsFlat(*ps, attr);
|
||||||
}
|
|
||||||
return (ps->interpolator_settings[attr] & PsInputFlatShade) != 0;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
void SetError(std::string* error, const char* message) {
|
void SetError(std::string* error, const char* message) {
|
||||||
|
|||||||
@@ -425,9 +425,6 @@ struct EmitterState {
|
|||||||
std::map<uint32_t, uint32_t> float_constants;
|
std::map<uint32_t, uint32_t> float_constants;
|
||||||
};
|
};
|
||||||
|
|
||||||
constexpr uint32_t PsInputOffsetMask = 0x0000001fu;
|
|
||||||
constexpr uint32_t PsInputFlatShade = 0x00000400u;
|
|
||||||
|
|
||||||
enum class VertexInputScalarKind { Float, Sint, Uint };
|
enum class VertexInputScalarKind { Float, Sint, Uint };
|
||||||
|
|
||||||
constexpr uint32_t NoImageComponent = 0xffffffffu;
|
constexpr uint32_t NoImageComponent = 0xffffffffu;
|
||||||
|
|||||||
@@ -0,0 +1,397 @@
|
|||||||
|
#include "graphics/shader/rectListShader.h"
|
||||||
|
|
||||||
|
#include "common/assert.h"
|
||||||
|
#include "graphics/shader/recompiler/emitter/SpirvBuilder.h"
|
||||||
|
#include "graphics/shader/recompiler/ir/ShaderIR.h"
|
||||||
|
#include "graphics/shader/shader.h"
|
||||||
|
#include "spirv/unified1/spirv.hpp11"
|
||||||
|
|
||||||
|
#include <array>
|
||||||
|
#include <bit>
|
||||||
|
#include <cstdint>
|
||||||
|
#include <utility>
|
||||||
|
|
||||||
|
namespace Libs::Graphics {
|
||||||
|
|
||||||
|
namespace {
|
||||||
|
|
||||||
|
using ShaderRecompiler::Spirv::Builder;
|
||||||
|
|
||||||
|
constexpr uint32_t SpirvVersion15 = 0x00010500u;
|
||||||
|
|
||||||
|
template <typename T>
|
||||||
|
constexpr uint32_t Word(T value) {
|
||||||
|
return static_cast<uint32_t>(value);
|
||||||
|
}
|
||||||
|
|
||||||
|
struct Parameter {
|
||||||
|
uint32_t input_location = 0;
|
||||||
|
uint32_t output_location = 0;
|
||||||
|
bool flat = false;
|
||||||
|
};
|
||||||
|
|
||||||
|
std::vector<Parameter> GetParameters(const ShaderVertexInputInfo& vertex_info,
|
||||||
|
const ShaderPixelInputInfo* pixel_info) {
|
||||||
|
if (pixel_info == nullptr) {
|
||||||
|
return {};
|
||||||
|
}
|
||||||
|
EXIT_IF(pixel_info->input_num > ShaderVertexInputInfo::RES_MAX);
|
||||||
|
EXIT_IF(pixel_info->stage.program == nullptr);
|
||||||
|
|
||||||
|
std::vector<uint32_t> active_inputs;
|
||||||
|
for (const auto& input: pixel_info->stage.program->info.inputs) {
|
||||||
|
if (input.kind == ShaderRecompiler::IR::StageInputKind::Parameter) {
|
||||||
|
active_inputs.push_back(input.location);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
std::vector<Parameter> parameters;
|
||||||
|
for (const auto input: active_inputs) {
|
||||||
|
const auto input_location = ShaderPixelParameterMappedLocation(*pixel_info, input);
|
||||||
|
if ((vertex_info.param_export_mask & (1u << input_location)) != 0) {
|
||||||
|
parameters.push_back({input_location,
|
||||||
|
ShaderPixelParameterLocation(*pixel_info, active_inputs, input),
|
||||||
|
ShaderPixelParameterIsFlat(*pixel_info, input)});
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return parameters;
|
||||||
|
}
|
||||||
|
|
||||||
|
class RectListEmitter {
|
||||||
|
public:
|
||||||
|
RectListEmitter(const std::vector<Parameter>& parameters_, spv::ExecutionModel model)
|
||||||
|
: parameters(parameters_) {
|
||||||
|
builder.AddMemoryModel(
|
||||||
|
{Word(spv::AddressingModel::Logical), Word(spv::MemoryModel::GLSL450)});
|
||||||
|
|
||||||
|
void_type = Type(spv::Op::OpTypeVoid);
|
||||||
|
uint_type = Type(spv::Op::OpTypeInt, 32u, 0u);
|
||||||
|
int_type = Type(spv::Op::OpTypeInt, 32u, 1u);
|
||||||
|
float_type = Type(spv::Op::OpTypeFloat, 32u);
|
||||||
|
vec4_float_type = Type(spv::Op::OpTypeVector, float_type, 4u);
|
||||||
|
function_type = Type(spv::Op::OpTypeFunction, void_type);
|
||||||
|
|
||||||
|
per_vertex_type = Type(spv::Op::OpTypeStruct, vec4_float_type);
|
||||||
|
builder.AddAnnotation({Word(spv::Op::OpMemberDecorate), per_vertex_type, 0u,
|
||||||
|
Word(spv::Decoration::BuiltIn), Word(spv::BuiltIn::Position)});
|
||||||
|
builder.AddAnnotation(
|
||||||
|
{Word(spv::Op::OpDecorate), per_vertex_type, Word(spv::Decoration::Block)});
|
||||||
|
|
||||||
|
ptr_input_vec4_float = Pointer(spv::StorageClass::Input, vec4_float_type);
|
||||||
|
ptr_output_vec4_float = Pointer(spv::StorageClass::Output, vec4_float_type);
|
||||||
|
if (model == spv::ExecutionModel::TessellationControl) {
|
||||||
|
bool_type = Type(spv::Op::OpTypeBool);
|
||||||
|
vec2_bool_type = Type(spv::Op::OpTypeVector, bool_type, 2u);
|
||||||
|
vec2_float_type = Type(spv::Op::OpTypeVector, float_type, 2u);
|
||||||
|
ptr_output_float = Pointer(spv::StorageClass::Output, float_type);
|
||||||
|
} else {
|
||||||
|
vec3_float_type = Type(spv::Op::OpTypeVector, float_type, 3u);
|
||||||
|
ptr_input_float = Pointer(spv::StorageClass::Input, float_type);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
std::vector<uint32_t> EmitControl() {
|
||||||
|
DefineEntry(spv::ExecutionModel::TessellationControl);
|
||||||
|
const auto float_one = Constant(float_type, std::bit_cast<uint32_t>(1.0f));
|
||||||
|
|
||||||
|
for (uint32_t i = 0; i < 4; i++) {
|
||||||
|
Store(Access(ptr_output_float, tess_outer, Int(i)), float_one);
|
||||||
|
}
|
||||||
|
for (uint32_t i = 0; i < 2; i++) {
|
||||||
|
Store(Access(ptr_output_float, tess_inner, Int(i)), float_one);
|
||||||
|
}
|
||||||
|
|
||||||
|
std::array<uint32_t, 3> positions {};
|
||||||
|
for (uint32_t i = 0; i < positions.size(); i++) {
|
||||||
|
positions[i] =
|
||||||
|
Load(vec4_float_type, Access(ptr_input_vec4_float, gl_in, Int(i), Int(0)));
|
||||||
|
}
|
||||||
|
|
||||||
|
std::array<uint32_t, 3> coordinate_equal {};
|
||||||
|
for (uint32_t i = 0; i < coordinate_equal.size(); i++) {
|
||||||
|
const auto left = Result(spv::Op::OpVectorShuffle, vec2_float_type, positions[i],
|
||||||
|
positions[i], 0u, 1u);
|
||||||
|
const auto right = Result(spv::Op::OpVectorShuffle, vec2_float_type,
|
||||||
|
positions[(i + 1u) % 3u], positions[(i + 1u) % 3u], 0u, 1u);
|
||||||
|
coordinate_equal[i] = Result(spv::Op::OpFOrdEqual, vec2_bool_type, left, right);
|
||||||
|
}
|
||||||
|
|
||||||
|
std::array<uint32_t, 3> barycentric {};
|
||||||
|
std::array<uint32_t, 3> edge_vertex {};
|
||||||
|
const auto float_minus_one = Constant(float_type, std::bit_cast<uint32_t>(-1.0f));
|
||||||
|
for (uint32_t i = 0; i < edge_vertex.size(); i++) {
|
||||||
|
const auto previous = (i + 2u) % 3u;
|
||||||
|
const auto xy = Result(
|
||||||
|
spv::Op::OpLogicalAnd, bool_type,
|
||||||
|
Result(spv::Op::OpCompositeExtract, bool_type, coordinate_equal[i], 0u),
|
||||||
|
Result(spv::Op::OpCompositeExtract, bool_type, coordinate_equal[previous], 1u));
|
||||||
|
const auto yx = Result(
|
||||||
|
spv::Op::OpLogicalAnd, bool_type,
|
||||||
|
Result(spv::Op::OpCompositeExtract, bool_type, coordinate_equal[i], 1u),
|
||||||
|
Result(spv::Op::OpCompositeExtract, bool_type, coordinate_equal[previous], 0u));
|
||||||
|
edge_vertex[i] = Result(spv::Op::OpLogicalOr, bool_type, xy, yx);
|
||||||
|
barycentric[i] =
|
||||||
|
Result(spv::Op::OpSelect, float_type, edge_vertex[i], float_minus_one, float_one);
|
||||||
|
}
|
||||||
|
|
||||||
|
auto vertex_index = Result(spv::Op::OpSelect, int_type, edge_vertex[2], Int(2), Int(0));
|
||||||
|
vertex_index = Result(spv::Op::OpSelect, int_type, edge_vertex[1], Int(1), vertex_index);
|
||||||
|
const auto invocation = Load(int_type, invocation_id);
|
||||||
|
const auto is_fourth = Result(spv::Op::OpIEqual, bool_type, invocation, Int(3));
|
||||||
|
const auto index =
|
||||||
|
Result(spv::Op::OpSMod, int_type,
|
||||||
|
Result(spv::Op::OpIAdd, int_type, vertex_index, invocation), Int(3));
|
||||||
|
|
||||||
|
const auto position3 = Interpolate(positions[0], positions[1], positions[2], barycentric);
|
||||||
|
const auto position =
|
||||||
|
Result(spv::Op::OpSelect, vec4_float_type, is_fourth, position3,
|
||||||
|
Load(vec4_float_type, Access(ptr_input_vec4_float, gl_in, index, Int(0))));
|
||||||
|
Store(Access(ptr_output_vec4_float, gl_out, invocation, Int(0)), position);
|
||||||
|
|
||||||
|
for (uint32_t i = 0; i < parameters.size(); i++) {
|
||||||
|
const auto input0 =
|
||||||
|
Load(vec4_float_type, Access(ptr_input_vec4_float, inputs[i], Int(0)));
|
||||||
|
if (parameters[i].flat) {
|
||||||
|
Store(Access(ptr_output_vec4_float, outputs[i], invocation), input0);
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
const auto input1 =
|
||||||
|
Load(vec4_float_type, Access(ptr_input_vec4_float, inputs[i], Int(1)));
|
||||||
|
const auto input2 =
|
||||||
|
Load(vec4_float_type, Access(ptr_input_vec4_float, inputs[i], Int(2)));
|
||||||
|
const auto input3 = Interpolate(input0, input1, input2, barycentric);
|
||||||
|
const auto value =
|
||||||
|
Result(spv::Op::OpSelect, vec4_float_type, is_fourth, input3,
|
||||||
|
Load(vec4_float_type, Access(ptr_input_vec4_float, inputs[i], index)));
|
||||||
|
Store(Access(ptr_output_vec4_float, outputs[i], invocation), value);
|
||||||
|
}
|
||||||
|
|
||||||
|
Emit(spv::Op::OpReturn);
|
||||||
|
Emit(spv::Op::OpFunctionEnd);
|
||||||
|
return builder.Build();
|
||||||
|
}
|
||||||
|
|
||||||
|
std::vector<uint32_t> EmitEvaluation() {
|
||||||
|
DefineEntry(spv::ExecutionModel::TessellationEvaluation);
|
||||||
|
|
||||||
|
const auto x = Load(float_type, Access(ptr_input_float, tess_coord, Int(0)));
|
||||||
|
const auto y = Load(float_type, Access(ptr_input_float, tess_coord, Int(1)));
|
||||||
|
const auto index = Result(
|
||||||
|
spv::Op::OpIAdd, int_type,
|
||||||
|
Result(spv::Op::OpIMul, int_type, Result(spv::Op::OpConvertFToS, int_type, y), Int(2)),
|
||||||
|
Result(spv::Op::OpConvertFToS, int_type, x));
|
||||||
|
|
||||||
|
const auto position =
|
||||||
|
Load(vec4_float_type, Access(ptr_input_vec4_float, gl_in, index, Int(0)));
|
||||||
|
Store(Access(ptr_output_vec4_float, gl_out, Int(0)), position);
|
||||||
|
for (uint32_t i = 0; i < parameters.size(); i++) {
|
||||||
|
Store(outputs[i],
|
||||||
|
Load(vec4_float_type, Access(ptr_input_vec4_float, inputs[i], index)));
|
||||||
|
}
|
||||||
|
|
||||||
|
Emit(spv::Op::OpReturn);
|
||||||
|
Emit(spv::Op::OpFunctionEnd);
|
||||||
|
return builder.Build();
|
||||||
|
}
|
||||||
|
|
||||||
|
private:
|
||||||
|
template <typename... Args>
|
||||||
|
uint32_t Type(spv::Op opcode, Args... operands) {
|
||||||
|
const auto id = builder.AllocateId();
|
||||||
|
builder.AddType({Word(opcode), id, Word(operands)...});
|
||||||
|
return id;
|
||||||
|
}
|
||||||
|
|
||||||
|
uint32_t Constant(uint32_t type, uint32_t value) {
|
||||||
|
const auto id = builder.AllocateId();
|
||||||
|
builder.AddType({Word(spv::Op::OpConstant), type, id, value});
|
||||||
|
return id;
|
||||||
|
}
|
||||||
|
|
||||||
|
uint32_t Pointer(spv::StorageClass storage, uint32_t type) {
|
||||||
|
return Type(spv::Op::OpTypePointer, storage, type);
|
||||||
|
}
|
||||||
|
|
||||||
|
uint32_t Array(uint32_t type, uint32_t size) {
|
||||||
|
return Type(spv::Op::OpTypeArray, type, Uint(size));
|
||||||
|
}
|
||||||
|
|
||||||
|
template <typename... Args>
|
||||||
|
uint32_t Result(spv::Op opcode, uint32_t type, Args... operands) {
|
||||||
|
const auto id = builder.AllocateId();
|
||||||
|
builder.AddFunction({Word(opcode), type, id, Word(operands)...});
|
||||||
|
return id;
|
||||||
|
}
|
||||||
|
|
||||||
|
template <typename... Args>
|
||||||
|
uint32_t ResultWithoutType(spv::Op opcode, Args... operands) {
|
||||||
|
const auto id = builder.AllocateId();
|
||||||
|
builder.AddFunction({Word(opcode), id, Word(operands)...});
|
||||||
|
return id;
|
||||||
|
}
|
||||||
|
|
||||||
|
template <typename... Args>
|
||||||
|
void Emit(spv::Op opcode, Args... operands) {
|
||||||
|
builder.AddFunction({Word(opcode), Word(operands)...});
|
||||||
|
}
|
||||||
|
|
||||||
|
template <typename... Args>
|
||||||
|
uint32_t Access(uint32_t pointer_type, uint32_t base, Args... indices) {
|
||||||
|
return Result(spv::Op::OpAccessChain, pointer_type, base, Word(indices)...);
|
||||||
|
}
|
||||||
|
|
||||||
|
uint32_t Load(uint32_t type, uint32_t pointer) {
|
||||||
|
return Result(spv::Op::OpLoad, type, pointer);
|
||||||
|
}
|
||||||
|
|
||||||
|
void Store(uint32_t pointer, uint32_t value) { Emit(spv::Op::OpStore, pointer, value); }
|
||||||
|
|
||||||
|
uint32_t Int(uint32_t value) {
|
||||||
|
auto& id = int_constants[value];
|
||||||
|
if (id == 0) {
|
||||||
|
id = Constant(int_type, value);
|
||||||
|
}
|
||||||
|
return id;
|
||||||
|
}
|
||||||
|
|
||||||
|
uint32_t Uint(uint32_t value) {
|
||||||
|
auto& id = uint_constants[value];
|
||||||
|
if (id == 0) {
|
||||||
|
id = Constant(uint_type, value);
|
||||||
|
}
|
||||||
|
return id;
|
||||||
|
}
|
||||||
|
|
||||||
|
uint32_t AddInterface(spv::StorageClass storage, uint32_t type) {
|
||||||
|
const auto variable = builder.AllocateId();
|
||||||
|
builder.AddType(
|
||||||
|
{Word(spv::Op::OpVariable), Pointer(storage, type), variable, Word(storage)});
|
||||||
|
interfaces.push_back(variable);
|
||||||
|
return variable;
|
||||||
|
}
|
||||||
|
|
||||||
|
void Decorate(uint32_t target, spv::Decoration decoration, uint32_t value) {
|
||||||
|
builder.AddAnnotation({Word(spv::Op::OpDecorate), target, Word(decoration), value});
|
||||||
|
}
|
||||||
|
|
||||||
|
void DefineEntry(spv::ExecutionModel model) {
|
||||||
|
builder.AddCapability({Word(spv::Capability::Shader)});
|
||||||
|
builder.AddCapability({Word(spv::Capability::Tessellation)});
|
||||||
|
main = Result(spv::Op::OpFunction, void_type, spv::FunctionControlMask::MaskNone,
|
||||||
|
function_type);
|
||||||
|
if (model == spv::ExecutionModel::TessellationControl) {
|
||||||
|
builder.AddExecutionMode({main, Word(spv::ExecutionMode::OutputVertices), 4u});
|
||||||
|
} else {
|
||||||
|
builder.AddExecutionMode({main, Word(spv::ExecutionMode::Quads)});
|
||||||
|
builder.AddExecutionMode({main, Word(spv::ExecutionMode::SpacingEqual)});
|
||||||
|
builder.AddExecutionMode({main, Word(spv::ExecutionMode::VertexOrderCw)});
|
||||||
|
}
|
||||||
|
DefineInputs(model);
|
||||||
|
DefineOutputs(model);
|
||||||
|
builder.AddEntryPoint(Word(model), main, "main", interfaces);
|
||||||
|
ResultWithoutType(spv::Op::OpLabel);
|
||||||
|
}
|
||||||
|
|
||||||
|
void DefineInputs(spv::ExecutionModel model) {
|
||||||
|
const auto tess_control = model == spv::ExecutionModel::TessellationControl;
|
||||||
|
if (tess_control) {
|
||||||
|
invocation_id = AddInterface(spv::StorageClass::Input, int_type);
|
||||||
|
Decorate(invocation_id, spv::Decoration::BuiltIn, Word(spv::BuiltIn::InvocationId));
|
||||||
|
} else {
|
||||||
|
tess_coord = AddInterface(spv::StorageClass::Input, vec3_float_type);
|
||||||
|
Decorate(tess_coord, spv::Decoration::BuiltIn, Word(spv::BuiltIn::TessCoord));
|
||||||
|
}
|
||||||
|
gl_in =
|
||||||
|
AddInterface(spv::StorageClass::Input, Array(per_vertex_type, tess_control ? 3u : 4u));
|
||||||
|
|
||||||
|
inputs.resize(parameters.size());
|
||||||
|
std::array<uint32_t, ShaderVertexInputInfo::RES_MAX> locations {};
|
||||||
|
for (uint32_t i = 0; i < parameters.size(); i++) {
|
||||||
|
const auto location =
|
||||||
|
tess_control ? parameters[i].input_location : parameters[i].output_location;
|
||||||
|
if (tess_control && locations[location] != 0) {
|
||||||
|
inputs[i] = locations[location];
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
inputs[i] = AddInterface(spv::StorageClass::Input,
|
||||||
|
Array(vec4_float_type, tess_control ? 3u : 4u));
|
||||||
|
Decorate(inputs[i], spv::Decoration::Location, location);
|
||||||
|
locations[location] = inputs[i];
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
void DefineOutputs(spv::ExecutionModel model) {
|
||||||
|
const auto tess_control = model == spv::ExecutionModel::TessellationControl;
|
||||||
|
if (tess_control) {
|
||||||
|
gl_out = AddInterface(spv::StorageClass::Output, Array(per_vertex_type, 4u));
|
||||||
|
tess_inner = AddInterface(spv::StorageClass::Output, Array(float_type, 2u));
|
||||||
|
Decorate(tess_inner, spv::Decoration::BuiltIn, Word(spv::BuiltIn::TessLevelInner));
|
||||||
|
builder.AddAnnotation(
|
||||||
|
{Word(spv::Op::OpDecorate), tess_inner, Word(spv::Decoration::Patch)});
|
||||||
|
tess_outer = AddInterface(spv::StorageClass::Output, Array(float_type, 4u));
|
||||||
|
Decorate(tess_outer, spv::Decoration::BuiltIn, Word(spv::BuiltIn::TessLevelOuter));
|
||||||
|
builder.AddAnnotation(
|
||||||
|
{Word(spv::Op::OpDecorate), tess_outer, Word(spv::Decoration::Patch)});
|
||||||
|
} else {
|
||||||
|
gl_out = AddInterface(spv::StorageClass::Output, per_vertex_type);
|
||||||
|
}
|
||||||
|
|
||||||
|
outputs.resize(parameters.size());
|
||||||
|
for (uint32_t i = 0; i < parameters.size(); i++) {
|
||||||
|
outputs[i] = AddInterface(spv::StorageClass::Output,
|
||||||
|
tess_control ? Array(vec4_float_type, 4u) : vec4_float_type);
|
||||||
|
Decorate(outputs[i], spv::Decoration::Location, parameters[i].output_location);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
uint32_t Interpolate(uint32_t v0, uint32_t v1, uint32_t v2,
|
||||||
|
const std::array<uint32_t, 3>& barycentric) {
|
||||||
|
const auto p0 = Result(spv::Op::OpVectorTimesScalar, vec4_float_type, v0, barycentric[0]);
|
||||||
|
const auto p1 = Result(spv::Op::OpVectorTimesScalar, vec4_float_type, v1, barycentric[1]);
|
||||||
|
const auto p2 = Result(spv::Op::OpVectorTimesScalar, vec4_float_type, v2, barycentric[2]);
|
||||||
|
return Result(spv::Op::OpFAdd, vec4_float_type, p0,
|
||||||
|
Result(spv::Op::OpFAdd, vec4_float_type, p1, p2));
|
||||||
|
}
|
||||||
|
|
||||||
|
Builder builder {SpirvVersion15};
|
||||||
|
const std::vector<Parameter>& parameters;
|
||||||
|
std::vector<uint32_t> interfaces;
|
||||||
|
std::vector<uint32_t> inputs;
|
||||||
|
std::vector<uint32_t> outputs;
|
||||||
|
std::array<uint32_t, 5> int_constants {};
|
||||||
|
std::array<uint32_t, 5> uint_constants {};
|
||||||
|
uint32_t main = 0;
|
||||||
|
uint32_t void_type = 0;
|
||||||
|
uint32_t bool_type = 0;
|
||||||
|
uint32_t uint_type = 0;
|
||||||
|
uint32_t int_type = 0;
|
||||||
|
uint32_t float_type = 0;
|
||||||
|
uint32_t vec2_bool_type = 0;
|
||||||
|
uint32_t vec2_float_type = 0;
|
||||||
|
uint32_t vec3_float_type = 0;
|
||||||
|
uint32_t vec4_float_type = 0;
|
||||||
|
uint32_t function_type = 0;
|
||||||
|
uint32_t per_vertex_type = 0;
|
||||||
|
uint32_t ptr_input_float = 0;
|
||||||
|
uint32_t ptr_input_vec4_float = 0;
|
||||||
|
uint32_t ptr_output_float = 0;
|
||||||
|
uint32_t ptr_output_vec4_float = 0;
|
||||||
|
uint32_t gl_in = 0;
|
||||||
|
uint32_t gl_out = 0;
|
||||||
|
uint32_t tess_inner = 0;
|
||||||
|
uint32_t tess_outer = 0;
|
||||||
|
uint32_t tess_coord = 0;
|
||||||
|
uint32_t invocation_id = 0;
|
||||||
|
};
|
||||||
|
|
||||||
|
} // namespace
|
||||||
|
|
||||||
|
RectListShaders BuildRectListShaders(const ShaderVertexInputInfo& vertex_info,
|
||||||
|
const ShaderPixelInputInfo* pixel_info) {
|
||||||
|
const auto parameters = GetParameters(vertex_info, pixel_info);
|
||||||
|
RectListEmitter control(parameters, spv::ExecutionModel::TessellationControl);
|
||||||
|
RectListEmitter evaluation(parameters, spv::ExecutionModel::TessellationEvaluation);
|
||||||
|
return {control.EmitControl(), evaluation.EmitEvaluation()};
|
||||||
|
}
|
||||||
|
|
||||||
|
} // namespace Libs::Graphics
|
||||||
@@ -0,0 +1,22 @@
|
|||||||
|
#ifndef EMULATOR_SRC_GRAPHICS_SHADER_RECTLISTSHADER_H_
|
||||||
|
#define EMULATOR_SRC_GRAPHICS_SHADER_RECTLISTSHADER_H_
|
||||||
|
|
||||||
|
#include <cstdint>
|
||||||
|
#include <vector>
|
||||||
|
|
||||||
|
namespace Libs::Graphics {
|
||||||
|
|
||||||
|
struct ShaderPixelInputInfo;
|
||||||
|
struct ShaderVertexInputInfo;
|
||||||
|
|
||||||
|
struct RectListShaders {
|
||||||
|
std::vector<uint32_t> control;
|
||||||
|
std::vector<uint32_t> evaluation;
|
||||||
|
};
|
||||||
|
|
||||||
|
RectListShaders BuildRectListShaders(const ShaderVertexInputInfo& vertex_info,
|
||||||
|
const ShaderPixelInputInfo* pixel_info);
|
||||||
|
|
||||||
|
} // namespace Libs::Graphics
|
||||||
|
|
||||||
|
#endif // EMULATOR_SRC_GRAPHICS_SHADER_RECTLISTSHADER_H_
|
||||||
@@ -45,6 +45,42 @@
|
|||||||
|
|
||||||
namespace Libs::Graphics {
|
namespace Libs::Graphics {
|
||||||
|
|
||||||
|
namespace {
|
||||||
|
|
||||||
|
constexpr uint32_t PsInputOffsetMask = 0x0000001fu;
|
||||||
|
constexpr uint32_t PsInputFlatShade = 0x00000400u;
|
||||||
|
|
||||||
|
} // namespace
|
||||||
|
|
||||||
|
uint32_t ShaderPixelParameterMappedLocation(const ShaderPixelInputInfo& info, uint32_t input) {
|
||||||
|
return input < info.input_num ? info.interpolator_settings[input] & PsInputOffsetMask : input;
|
||||||
|
}
|
||||||
|
|
||||||
|
uint32_t ShaderPixelParameterLocation(const ShaderPixelInputInfo& info,
|
||||||
|
std::span<const uint32_t> active_inputs, uint32_t input) {
|
||||||
|
std::array<bool, 32> used_locations {};
|
||||||
|
for (const auto active_input: active_inputs) {
|
||||||
|
auto location = ShaderPixelParameterMappedLocation(info, active_input);
|
||||||
|
if (location < used_locations.size() && used_locations[location]) {
|
||||||
|
location = active_input;
|
||||||
|
while (location < used_locations.size() && used_locations[location]) {
|
||||||
|
location++;
|
||||||
|
}
|
||||||
|
EXIT_NOT_IMPLEMENTED(location >= used_locations.size());
|
||||||
|
}
|
||||||
|
|
||||||
|
if (active_input == input) {
|
||||||
|
return location;
|
||||||
|
}
|
||||||
|
used_locations[location] = true;
|
||||||
|
}
|
||||||
|
return ShaderPixelParameterMappedLocation(info, input);
|
||||||
|
}
|
||||||
|
|
||||||
|
bool ShaderPixelParameterIsFlat(const ShaderPixelInputInfo& info, uint32_t input) {
|
||||||
|
return input < info.input_num && (info.interpolator_settings[input] & PsInputFlatShade) != 0;
|
||||||
|
}
|
||||||
|
|
||||||
struct ShaderBinaryInfo {
|
struct ShaderBinaryInfo {
|
||||||
uint8_t signature[7];
|
uint8_t signature[7];
|
||||||
uint8_t version;
|
uint8_t version;
|
||||||
@@ -1606,6 +1642,7 @@ ShaderId ShaderGetIdPS(const HW::PixelShaderInfo& regs, const ShaderPixelInputIn
|
|||||||
ret.ids.push_back(static_cast<uint32_t>(input_info.ps_pos_z));
|
ret.ids.push_back(static_cast<uint32_t>(input_info.ps_pos_z));
|
||||||
ret.ids.push_back(static_cast<uint32_t>(input_info.ps_pos_w));
|
ret.ids.push_back(static_cast<uint32_t>(input_info.ps_pos_w));
|
||||||
ret.ids.push_back(static_cast<uint32_t>(input_info.ps_front_face));
|
ret.ids.push_back(static_cast<uint32_t>(input_info.ps_front_face));
|
||||||
|
ret.ids.push_back(static_cast<uint32_t>(input_info.ps_no_perspective));
|
||||||
ret.ids.push_back(static_cast<uint32_t>(input_info.ps_pixel_kill_enable));
|
ret.ids.push_back(static_cast<uint32_t>(input_info.ps_pixel_kill_enable));
|
||||||
ret.ids.push_back(static_cast<uint32_t>(input_info.ps_sample_mask_export_enable));
|
ret.ids.push_back(static_cast<uint32_t>(input_info.ps_sample_mask_export_enable));
|
||||||
ret.ids.push_back(static_cast<uint32_t>(input_info.ps_early_z));
|
ret.ids.push_back(static_cast<uint32_t>(input_info.ps_early_z));
|
||||||
|
|||||||
@@ -122,6 +122,11 @@ struct ShaderPixelInputInfo {
|
|||||||
bool HasPositionInput() const { return ps_pos_x || ps_pos_y || ps_pos_z || ps_pos_w; }
|
bool HasPositionInput() const { return ps_pos_x || ps_pos_y || ps_pos_z || ps_pos_w; }
|
||||||
};
|
};
|
||||||
|
|
||||||
|
uint32_t ShaderPixelParameterMappedLocation(const ShaderPixelInputInfo& info, uint32_t input);
|
||||||
|
uint32_t ShaderPixelParameterLocation(const ShaderPixelInputInfo& info,
|
||||||
|
std::span<const uint32_t> active_inputs, uint32_t input);
|
||||||
|
bool ShaderPixelParameterIsFlat(const ShaderPixelInputInfo& info, uint32_t input);
|
||||||
|
|
||||||
struct ShaderSharp {
|
struct ShaderSharp {
|
||||||
uint16_t offset_dw : 15;
|
uint16_t offset_dw : 15;
|
||||||
uint16_t size : 1;
|
uint16_t size : 1;
|
||||||
|
|||||||
@@ -78,19 +78,6 @@
|
|||||||
</property>
|
</property>
|
||||||
</widget>
|
</widget>
|
||||||
</item>
|
</item>
|
||||||
<item row="2" column="0" colspan="2">
|
|
||||||
<widget class="QCheckBox" name="checkBox_ngg_rectlist_draw">
|
|
||||||
<property name="toolTip">
|
|
||||||
<string>Use the NGG 4-vertex path for rect-list DrawIndexAuto primitive 7</string>
|
|
||||||
</property>
|
|
||||||
<property name="text">
|
|
||||||
<string>Use NGG rect-list draw</string>
|
|
||||||
</property>
|
|
||||||
<property name="checked">
|
|
||||||
<bool>true</bool>
|
|
||||||
</property>
|
|
||||||
</widget>
|
|
||||||
</item>
|
|
||||||
</layout>
|
</layout>
|
||||||
</widget>
|
</widget>
|
||||||
</item>
|
</item>
|
||||||
|
|||||||
@@ -94,7 +94,6 @@ public:
|
|||||||
QString printf_output_file = "_kyty.txt";
|
QString printf_output_file = "_kyty.txt";
|
||||||
ProfilerDirection profiler_direction = ProfilerDirection::None;
|
ProfilerDirection profiler_direction = ProfilerDirection::None;
|
||||||
bool renderdoc_enabled = false;
|
bool renderdoc_enabled = false;
|
||||||
bool ngg_rectlist_draw_enabled = true;
|
|
||||||
|
|
||||||
QString elf = QStringLiteral("eboot.bin");
|
QString elf = QStringLiteral("eboot.bin");
|
||||||
|
|
||||||
@@ -112,7 +111,6 @@ public:
|
|||||||
printf_output_file = other.printf_output_file;
|
printf_output_file = other.printf_output_file;
|
||||||
profiler_direction = other.profiler_direction;
|
profiler_direction = other.profiler_direction;
|
||||||
renderdoc_enabled = other.renderdoc_enabled;
|
renderdoc_enabled = other.renderdoc_enabled;
|
||||||
ngg_rectlist_draw_enabled = other.ngg_rectlist_draw_enabled;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
void CopyFrom(const Configuration& other) {
|
void CopyFrom(const Configuration& other) {
|
||||||
@@ -147,7 +145,6 @@ public:
|
|||||||
KYTY_CFG_SET(printf_output_file);
|
KYTY_CFG_SET(printf_output_file);
|
||||||
KYTY_CFG_SET(profiler_direction);
|
KYTY_CFG_SET(profiler_direction);
|
||||||
KYTY_CFG_SET(renderdoc_enabled);
|
KYTY_CFG_SET(renderdoc_enabled);
|
||||||
KYTY_CFG_SET(ngg_rectlist_draw_enabled);
|
|
||||||
KYTY_CFG_SET(elf);
|
KYTY_CFG_SET(elf);
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -169,8 +166,6 @@ public:
|
|||||||
KYTY_CFG_GET(printf_output_file);
|
KYTY_CFG_GET(printf_output_file);
|
||||||
KYTY_CFG_GET(profiler_direction);
|
KYTY_CFG_GET(profiler_direction);
|
||||||
KYTY_CFG_GET(renderdoc_enabled);
|
KYTY_CFG_GET(renderdoc_enabled);
|
||||||
ngg_rectlist_draw_enabled =
|
|
||||||
s->value("ngg_rectlist_draw_enabled", ngg_rectlist_draw_enabled).toBool();
|
|
||||||
elf = s->value("elf", elf).toString();
|
elf = s->value("elf", elf).toString();
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|||||||
@@ -124,7 +124,6 @@ void ConfigurationEditDialog::Init(const Configuration& info) {
|
|||||||
m_ui->checkBox_shader_validation->setChecked(info.shader_validation_enabled);
|
m_ui->checkBox_shader_validation->setChecked(info.shader_validation_enabled);
|
||||||
m_ui->checkBox_vulkan_validation->setChecked(info.vulkan_validation_enabled);
|
m_ui->checkBox_vulkan_validation->setChecked(info.vulkan_validation_enabled);
|
||||||
m_ui->checkBox_renderdoc_capture->setChecked(info.renderdoc_enabled);
|
m_ui->checkBox_renderdoc_capture->setChecked(info.renderdoc_enabled);
|
||||||
m_ui->checkBox_ngg_rectlist_draw->setChecked(info.ngg_rectlist_draw_enabled);
|
|
||||||
ListInit(m_ui->comboBox_shader_optimization_type, info.shader_optimization_type);
|
ListInit(m_ui->comboBox_shader_optimization_type, info.shader_optimization_type);
|
||||||
ListInit(m_ui->comboBox_shader_log_direction, info.shader_log_direction);
|
ListInit(m_ui->comboBox_shader_log_direction, info.shader_log_direction);
|
||||||
m_ui->lineEdit_shader_log_folder->setText(info.shader_log_folder);
|
m_ui->lineEdit_shader_log_folder->setText(info.shader_log_folder);
|
||||||
@@ -245,7 +244,6 @@ static void UpdateInfo(Configuration& info, Ui::ConfigurationEditDialog& ui) {
|
|||||||
info.vulkan_validation_enabled = ui.checkBox_vulkan_validation->isChecked();
|
info.vulkan_validation_enabled = ui.checkBox_vulkan_validation->isChecked();
|
||||||
info.shader_validation_enabled = ui.checkBox_shader_validation->isChecked();
|
info.shader_validation_enabled = ui.checkBox_shader_validation->isChecked();
|
||||||
info.renderdoc_enabled = ui.checkBox_renderdoc_capture->isChecked();
|
info.renderdoc_enabled = ui.checkBox_renderdoc_capture->isChecked();
|
||||||
info.ngg_rectlist_draw_enabled = ui.checkBox_ngg_rectlist_draw->isChecked();
|
|
||||||
info.shader_optimization_type = TextToEnum<Configuration::ShaderOptimizationType>(
|
info.shader_optimization_type = TextToEnum<Configuration::ShaderOptimizationType>(
|
||||||
ui.comboBox_shader_optimization_type->currentText());
|
ui.comboBox_shader_optimization_type->currentText());
|
||||||
info.shader_log_direction = TextToEnum<Configuration::ShaderLogDirection>(
|
info.shader_log_direction = TextToEnum<Configuration::ShaderLogDirection>(
|
||||||
|
|||||||
@@ -216,7 +216,6 @@ static QStringList CreateEmulatorArgs(const Configuration& info) {
|
|||||||
args << "--printf-output-file" << info.printf_output_file;
|
args << "--printf-output-file" << info.printf_output_file;
|
||||||
args << "--profiler-direction" << EnumToText(info.profiler_direction);
|
args << "--profiler-direction" << EnumToText(info.profiler_direction);
|
||||||
args << "--spirv-debug-printf" << "false";
|
args << "--spirv-debug-printf" << "false";
|
||||||
args << "--ngg-rectlist-draw" << BoolArg(info.ngg_rectlist_draw_enabled);
|
|
||||||
if (info.renderdoc_enabled) {
|
if (info.renderdoc_enabled) {
|
||||||
args << "--rd";
|
args << "--rd";
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -59,8 +59,6 @@ static void PrintUsage() {
|
|||||||
::printf(" --printf-output-file <path> Guest printf output file.\n");
|
::printf(" --printf-output-file <path> Guest printf output file.\n");
|
||||||
::printf(" --profiler-direction <value> None or Network.\n");
|
::printf(" --profiler-direction <value> None or Network.\n");
|
||||||
::printf(" --spirv-debug-printf <true|false> Enable SPIR-V debug printf.\n");
|
::printf(" --spirv-debug-printf <true|false> Enable SPIR-V debug printf.\n");
|
||||||
::printf(" --ngg-rectlist-draw <true|false> Draw rect-list auto draws using the NGG "
|
|
||||||
"4-vertex path.\n");
|
|
||||||
::printf(
|
::printf(
|
||||||
" --readback-linear-images <true|false> Read back writable linear images on submit.\n");
|
" --readback-linear-images <true|false> Read back writable linear images on submit.\n");
|
||||||
::printf(" --rd Enable RenderDoc capture.\n");
|
::printf(" --rd Enable RenderDoc capture.\n");
|
||||||
@@ -220,11 +218,6 @@ static bool ParseArgs(int argc, char* argv[], RunOptions& options, bool& show_he
|
|||||||
::printf("invalid boolean for %s: %s\n", arg.c_str(), value.c_str());
|
::printf("invalid boolean for %s: %s\n", arg.c_str(), value.c_str());
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
} else if (arg == "--ngg-rectlist-draw") {
|
|
||||||
if (!ParseBool(value, options.config.ngg_rectlist_draw_enabled)) {
|
|
||||||
::printf("invalid boolean for %s: %s\n", arg.c_str(), value.c_str());
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
} else if (arg == "--readback-linear-images") {
|
} else if (arg == "--readback-linear-images") {
|
||||||
if (!ParseBool(value, options.config.readback_linear_images)) {
|
if (!ParseBool(value, options.config.readback_linear_images)) {
|
||||||
::printf("invalid boolean for %s: %s\n", arg.c_str(), value.c_str());
|
::printf("invalid boolean for %s: %s\n", arg.c_str(), value.c_str());
|
||||||
|
|||||||
@@ -39,6 +39,7 @@
|
|||||||
#include "graphics/shader/recompiler/emitter/SpirvBuilder.h"
|
#include "graphics/shader/recompiler/emitter/SpirvBuilder.h"
|
||||||
#include "graphics/shader/recompiler/emitter/SpirvEmitter.h"
|
#include "graphics/shader/recompiler/emitter/SpirvEmitter.h"
|
||||||
#include "graphics/shader/recompiler/ir/BindingLayout.h"
|
#include "graphics/shader/recompiler/ir/BindingLayout.h"
|
||||||
|
#include "graphics/shader/rectListShader.h"
|
||||||
#include "graphics/shader/shader.h"
|
#include "graphics/shader/shader.h"
|
||||||
#include "kernel/memory.h"
|
#include "kernel/memory.h"
|
||||||
#include "spirv-tools/libspirv.hpp"
|
#include "spirv-tools/libspirv.hpp"
|
||||||
@@ -771,6 +772,91 @@ void ValidateSpirv(const char* shader_name, const std::vector<u32>& spirv) {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
size_t CountText(const std::string& text, const std::string& needle) {
|
||||||
|
size_t count = 0;
|
||||||
|
for (size_t offset = 0; (offset = text.find(needle, offset)) != std::string::npos;
|
||||||
|
offset += needle.size()) {
|
||||||
|
count++;
|
||||||
|
}
|
||||||
|
return count;
|
||||||
|
}
|
||||||
|
|
||||||
|
void CheckRectListShaders() {
|
||||||
|
constexpr const char* name = "RectListShaders";
|
||||||
|
|
||||||
|
auto program = std::make_shared<ShaderRecompiler::IR::Program>();
|
||||||
|
program->info.inputs.push_back(
|
||||||
|
{ShaderRecompiler::IR::StageInputKind::Parameter, 0, 4, "in_param_0"});
|
||||||
|
program->info.inputs.push_back(
|
||||||
|
{ShaderRecompiler::IR::StageInputKind::Parameter, 1, 4, "in_param_1"});
|
||||||
|
|
||||||
|
ShaderVertexInputInfo vertex {};
|
||||||
|
vertex.param_export_mask = 1u;
|
||||||
|
ShaderPixelInputInfo pixel {};
|
||||||
|
pixel.input_num = 2;
|
||||||
|
pixel.interpolator_settings[0] = 0x400u;
|
||||||
|
pixel.interpolator_settings[1] = 0;
|
||||||
|
pixel.stage.program = program;
|
||||||
|
HW::PixelShaderInfo ps_regs {};
|
||||||
|
const auto perspective_id = ShaderGetIdPS(ps_regs, pixel, false);
|
||||||
|
pixel.ps_no_perspective = true;
|
||||||
|
const auto no_perspective_id = ShaderGetIdPS(ps_regs, pixel, false);
|
||||||
|
pixel.ps_no_perspective = false;
|
||||||
|
Require(name, "pipeline identity", perspective_id != no_perspective_id,
|
||||||
|
"pixel interpolation mode must participate in the shader and pipeline key");
|
||||||
|
|
||||||
|
const std::array<uint32_t, 2> active_inputs = {0, 1};
|
||||||
|
Require(name, "duplicate mapping",
|
||||||
|
ShaderPixelParameterLocation(pixel, active_inputs, 0) == 0 &&
|
||||||
|
ShaderPixelParameterLocation(pixel, active_inputs, 1) == 1,
|
||||||
|
"duplicate pixel mappings must receive distinct effective output locations");
|
||||||
|
|
||||||
|
const auto shaders = BuildRectListShaders(vertex, &pixel);
|
||||||
|
Require(name, "SPIR-V version",
|
||||||
|
shaders.control.size() > 1 && shaders.control[1] == 0x00010500u &&
|
||||||
|
shaders.evaluation.size() > 1 && shaders.evaluation[1] == 0x00010500u,
|
||||||
|
"shadPS4-compatible vector selection requires SPIR-V 1.5");
|
||||||
|
ValidateSpirv(name, shaders.control);
|
||||||
|
ValidateSpirv(name, shaders.evaluation);
|
||||||
|
|
||||||
|
spvtools::SpirvTools tools(SPV_ENV_VULKAN_1_2);
|
||||||
|
std::string control_text;
|
||||||
|
std::string evaluation_text;
|
||||||
|
Require(name, "control disassembly", tools.Disassemble(shaders.control, &control_text),
|
||||||
|
"failed to disassemble rectangle-list tessellation control shader");
|
||||||
|
Require(name, "evaluation disassembly",
|
||||||
|
tools.Disassemble(shaders.evaluation, &evaluation_text),
|
||||||
|
"failed to disassemble rectangle-list tessellation evaluation shader");
|
||||||
|
Require(name, "control execution mode",
|
||||||
|
control_text.find("TessellationControl") != std::string::npos &&
|
||||||
|
control_text.find("OutputVertices 4") != std::string::npos,
|
||||||
|
"rectangle-list control shader must produce four control points");
|
||||||
|
Require(name, "evaluation execution modes",
|
||||||
|
evaluation_text.find("TessellationEvaluation") != std::string::npos &&
|
||||||
|
evaluation_text.find("Quads") != std::string::npos &&
|
||||||
|
evaluation_text.find("SpacingEqual") != std::string::npos &&
|
||||||
|
evaluation_text.find("VertexOrderCw") != std::string::npos,
|
||||||
|
"rectangle-list evaluation shader has the wrong patch modes");
|
||||||
|
Require(name, "no geometry stage",
|
||||||
|
control_text.find("Geometry") == std::string::npos &&
|
||||||
|
evaluation_text.find("Geometry") == std::string::npos,
|
||||||
|
"rectangle-list expansion must not use geometry shaders");
|
||||||
|
Require(name, "flat broadcast",
|
||||||
|
CountText(control_text, "OpVectorTimesScalar") == 6 &&
|
||||||
|
CountText(control_text, "OpSelect %v4float") == 2,
|
||||||
|
"flat parameters must use guest vertex zero instead of reconstructed values");
|
||||||
|
Require(name, "remapped interface",
|
||||||
|
CountText(control_text, " Location 0") == 2 &&
|
||||||
|
CountText(control_text, " Location 1") == 1 &&
|
||||||
|
CountText(evaluation_text, " Location 0") == 2 &&
|
||||||
|
CountText(evaluation_text, " Location 1") == 2,
|
||||||
|
"duplicate pixel mappings must share one vertex input and keep distinct patch outputs");
|
||||||
|
|
||||||
|
const auto position_only = BuildRectListShaders(vertex, nullptr);
|
||||||
|
ValidateSpirv(name, position_only.control);
|
||||||
|
ValidateSpirv(name, position_only.evaluation);
|
||||||
|
}
|
||||||
|
|
||||||
void CheckSpirvText(const TestCase& test, const std::vector<u32>& spirv) {
|
void CheckSpirvText(const TestCase& test, const std::vector<u32>& spirv) {
|
||||||
if (test.required_spirv.empty() && test.forbidden_spirv.empty()) {
|
if (test.required_spirv.empty() && test.forbidden_spirv.empty()) {
|
||||||
return;
|
return;
|
||||||
@@ -17502,6 +17588,7 @@ int main(int argc, char** argv) {
|
|||||||
CheckPm4CeCompletion(vulkan.RuntimeRenderer());
|
CheckPm4CeCompletion(vulkan.RuntimeRenderer());
|
||||||
CheckEmbeddedFetchVertexOffset();
|
CheckEmbeddedFetchVertexOffset();
|
||||||
CheckEmbeddedFetchLaneSpill();
|
CheckEmbeddedFetchLaneSpill();
|
||||||
|
CheckRectListShaders();
|
||||||
CheckPs5GameExampleImageClearRuntimeShape();
|
CheckPs5GameExampleImageClearRuntimeShape();
|
||||||
vulkan.CheckSchedulerTimeline();
|
vulkan.CheckSchedulerTimeline();
|
||||||
vulkan.CheckGpuMappedRangeLifecycle();
|
vulkan.CheckGpuMappedRangeLifecycle();
|
||||||
|
|||||||
Reference in New Issue
Block a user