mirror of
https://github.com/shadps4-emu/shadPS4.git
synced 2025-08-06 17:32:40 +00:00
renderer_vulkan: Use tessellation for quad primitive as well
This commit is contained in:
parent
0925d58924
commit
cff8124943
@ -130,21 +130,37 @@ struct QuadRectListEmitter : public Sirit::Module {
|
|||||||
}
|
}
|
||||||
|
|
||||||
/// Emits a passthrough quad tessellation control shader that outputs 4 control points.
|
/// Emits a passthrough quad tessellation control shader that outputs 4 control points.
|
||||||
void EmitPassthroughTCS() {
|
void EmitQuadListTCS() {
|
||||||
DefineEntry(spv::ExecutionModel::TessellationControl);
|
DefineEntry(spv::ExecutionModel::TessellationControl);
|
||||||
|
const Id array_type{TypeArray(int_id, Int(4))};
|
||||||
|
const Id values{ConstantComposite(array_type, Int(1), Int(2), Int(0), Int(3))};
|
||||||
|
const Id indices{AddLocalVariable(TypePointer(spv::StorageClass::Function, array_type),
|
||||||
|
spv::StorageClass::Function, values)};
|
||||||
|
|
||||||
|
// Set passthrough tessellation factors
|
||||||
|
const Id output_float{TypePointer(spv::StorageClass::Output, float_id)};
|
||||||
|
for (int i = 0; i < 4; i++) {
|
||||||
|
const Id ptr{OpAccessChain(output_float, gl_tess_level_outer, Int(i))};
|
||||||
|
OpStore(ptr, float_one);
|
||||||
|
}
|
||||||
|
for (int i = 0; i < 2; i++) {
|
||||||
|
const Id ptr{OpAccessChain(output_float, gl_tess_level_inner, Int(i))};
|
||||||
|
OpStore(ptr, float_one);
|
||||||
|
}
|
||||||
|
|
||||||
const Id input_vec4{TypePointer(spv::StorageClass::Input, vec4_id)};
|
const Id input_vec4{TypePointer(spv::StorageClass::Input, vec4_id)};
|
||||||
const Id output_vec4{TypePointer(spv::StorageClass::Output, vec4_id)};
|
const Id output_vec4{TypePointer(spv::StorageClass::Output, vec4_id)};
|
||||||
|
const Id func_int{TypePointer(spv::StorageClass::Function, int_id)};
|
||||||
const Id invocation_id{OpLoad(int_id, gl_invocation_id)};
|
const Id invocation_id{OpLoad(int_id, gl_invocation_id)};
|
||||||
|
const Id index{OpLoad(int_id, OpAccessChain(func_int, indices, invocation_id))};
|
||||||
|
|
||||||
// gl_out[gl_InvocationID].gl_Position = gl_in[gl_InvocationID].gl_Position;
|
// gl_out[gl_InvocationID].gl_Position = gl_in[gl_InvocationID].gl_Position;
|
||||||
const Id in_position{
|
const Id in_position{OpLoad(vec4_id, OpAccessChain(input_vec4, gl_in, index, Int(0)))};
|
||||||
OpLoad(vec4_id, OpAccessChain(input_vec4, gl_in, invocation_id, Int(0)))};
|
|
||||||
OpStore(OpAccessChain(output_vec4, gl_out, invocation_id, Int(0)), in_position);
|
OpStore(OpAccessChain(output_vec4, gl_out, invocation_id, Int(0)), in_position);
|
||||||
|
|
||||||
for (int i = 0; i < num_attribs; i++) {
|
for (int i = 0; i < num_attribs; i++) {
|
||||||
// out_paramN[gl_InvocationID] = in_paramN[gl_InvocationID];
|
// out_paramN[gl_InvocationID] = in_paramN[gl_InvocationID];
|
||||||
const Id in_param{OpLoad(vec4_id, OpAccessChain(input_vec4, inputs[i], invocation_id))};
|
const Id in_param{OpLoad(vec4_id, OpAccessChain(input_vec4, inputs[i], index))};
|
||||||
OpStore(OpAccessChain(output_vec4, outputs[i], invocation_id), in_param);
|
OpStore(OpAccessChain(output_vec4, outputs[i], invocation_id), in_param);
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -296,8 +312,8 @@ std::vector<u32> EmitAuxilaryTessShader(AuxShaderType type, size_t num_attribs)
|
|||||||
case AuxShaderType::RectListTCS:
|
case AuxShaderType::RectListTCS:
|
||||||
ctx.EmitRectListTCS();
|
ctx.EmitRectListTCS();
|
||||||
break;
|
break;
|
||||||
case AuxShaderType::PassthoughTCS:
|
case AuxShaderType::QuadListTCS:
|
||||||
ctx.EmitPassthroughTCS();
|
ctx.EmitQuadListTCS();
|
||||||
break;
|
break;
|
||||||
case AuxShaderType::PassthroughTES:
|
case AuxShaderType::PassthroughTES:
|
||||||
ctx.EmitPassthroughTES();
|
ctx.EmitPassthroughTES();
|
||||||
|
@ -10,7 +10,7 @@ namespace Shader::Backend::SPIRV {
|
|||||||
|
|
||||||
enum class AuxShaderType : u32 {
|
enum class AuxShaderType : u32 {
|
||||||
RectListTCS,
|
RectListTCS,
|
||||||
PassthoughTCS,
|
QuadListTCS,
|
||||||
PassthroughTES,
|
PassthroughTES,
|
||||||
};
|
};
|
||||||
|
|
||||||
|
@ -238,32 +238,14 @@ u32 BufferCache::BindIndexBuffer(bool& is_indexed, u32 index_offset) {
|
|||||||
// Emulate QuadList and Polygon primitive types with CPU made index buffer.
|
// Emulate QuadList and Polygon primitive types with CPU made index buffer.
|
||||||
const auto& regs = liverpool->regs;
|
const auto& regs = liverpool->regs;
|
||||||
if (!is_indexed) {
|
if (!is_indexed) {
|
||||||
bool needs_index_buffer = false;
|
if (regs.primitive_type != AmdGpu::PrimitiveType::Polygon) {
|
||||||
if (regs.primitive_type == AmdGpu::PrimitiveType::QuadList ||
|
|
||||||
regs.primitive_type == AmdGpu::PrimitiveType::Polygon) {
|
|
||||||
needs_index_buffer = true;
|
|
||||||
}
|
|
||||||
|
|
||||||
if (!needs_index_buffer) {
|
|
||||||
return regs.num_indices;
|
return regs.num_indices;
|
||||||
}
|
}
|
||||||
|
|
||||||
// Emit indices.
|
// Emit indices.
|
||||||
const u32 index_size = 3 * regs.num_indices;
|
const u32 index_size = 3 * regs.num_indices;
|
||||||
const auto [data, offset] = stream_buffer.Map(index_size);
|
const auto [data, offset] = stream_buffer.Map(index_size);
|
||||||
|
|
||||||
switch (regs.primitive_type) {
|
|
||||||
case AmdGpu::PrimitiveType::QuadList:
|
|
||||||
Vulkan::LiverpoolToVK::EmitQuadToTriangleListIndices(data, regs.num_indices);
|
|
||||||
break;
|
|
||||||
case AmdGpu::PrimitiveType::Polygon:
|
|
||||||
Vulkan::LiverpoolToVK::EmitPolygonToTriangleListIndices(data, regs.num_indices);
|
Vulkan::LiverpoolToVK::EmitPolygonToTriangleListIndices(data, regs.num_indices);
|
||||||
break;
|
|
||||||
default:
|
|
||||||
UNREACHABLE();
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
|
|
||||||
stream_buffer.Commit();
|
stream_buffer.Commit();
|
||||||
|
|
||||||
// Bind index buffer.
|
// Bind index buffer.
|
||||||
@ -282,31 +264,6 @@ u32 BufferCache::BindIndexBuffer(bool& is_indexed, u32 index_offset) {
|
|||||||
VAddr index_address = regs.index_base_address.Address<VAddr>();
|
VAddr index_address = regs.index_base_address.Address<VAddr>();
|
||||||
index_address += index_offset * index_size;
|
index_address += index_offset * index_size;
|
||||||
|
|
||||||
if (regs.primitive_type == AmdGpu::PrimitiveType::QuadList) {
|
|
||||||
// Convert indices.
|
|
||||||
const u32 new_index_size = regs.num_indices * index_size * 6 / 4;
|
|
||||||
const auto [data, offset] = stream_buffer.Map(new_index_size);
|
|
||||||
const auto index_ptr = reinterpret_cast<u8*>(index_address);
|
|
||||||
switch (index_type) {
|
|
||||||
case vk::IndexType::eUint16:
|
|
||||||
Vulkan::LiverpoolToVK::ConvertQuadToTriangleListIndices<u16>(data, index_ptr,
|
|
||||||
regs.num_indices);
|
|
||||||
break;
|
|
||||||
case vk::IndexType::eUint32:
|
|
||||||
Vulkan::LiverpoolToVK::ConvertQuadToTriangleListIndices<u32>(data, index_ptr,
|
|
||||||
regs.num_indices);
|
|
||||||
break;
|
|
||||||
default:
|
|
||||||
UNREACHABLE_MSG("Unsupported QuadList index type {}", vk::to_string(index_type));
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
stream_buffer.Commit();
|
|
||||||
|
|
||||||
// Bind index buffer.
|
|
||||||
const auto cmdbuf = scheduler.CommandBuffer();
|
|
||||||
cmdbuf.bindIndexBuffer(stream_buffer.Handle(), offset, index_type);
|
|
||||||
return new_index_size / index_size;
|
|
||||||
}
|
|
||||||
if (regs.primitive_type == AmdGpu::PrimitiveType::Polygon) {
|
if (regs.primitive_type == AmdGpu::PrimitiveType::Polygon) {
|
||||||
UNREACHABLE();
|
UNREACHABLE();
|
||||||
}
|
}
|
||||||
|
@ -116,10 +116,10 @@ vk::PrimitiveTopology PrimitiveType(AmdGpu::PrimitiveType type) {
|
|||||||
return vk::PrimitiveTopology::eTriangleStripWithAdjacency;
|
return vk::PrimitiveTopology::eTriangleStripWithAdjacency;
|
||||||
case AmdGpu::PrimitiveType::PatchPrimitive:
|
case AmdGpu::PrimitiveType::PatchPrimitive:
|
||||||
return vk::PrimitiveTopology::ePatchList;
|
return vk::PrimitiveTopology::ePatchList;
|
||||||
case AmdGpu::PrimitiveType::QuadList:
|
|
||||||
case AmdGpu::PrimitiveType::Polygon:
|
case AmdGpu::PrimitiveType::Polygon:
|
||||||
// Needs to generate index buffer on the fly.
|
// Needs to generate index buffer on the fly.
|
||||||
return vk::PrimitiveTopology::eTriangleList;
|
return vk::PrimitiveTopology::eTriangleList;
|
||||||
|
case AmdGpu::PrimitiveType::QuadList:
|
||||||
case AmdGpu::PrimitiveType::RectList:
|
case AmdGpu::PrimitiveType::RectList:
|
||||||
return vk::PrimitiveTopology::ePatchList;
|
return vk::PrimitiveTopology::ePatchList;
|
||||||
default:
|
default:
|
||||||
|
@ -70,34 +70,6 @@ vk::ClearValue ColorBufferClearValue(const AmdGpu::Liverpool::ColorBuffer& color
|
|||||||
|
|
||||||
vk::SampleCountFlagBits NumSamples(u32 num_samples, vk::SampleCountFlags supported_flags);
|
vk::SampleCountFlagBits NumSamples(u32 num_samples, vk::SampleCountFlags supported_flags);
|
||||||
|
|
||||||
static constexpr u16 NumVerticesPerQuad = 4;
|
|
||||||
|
|
||||||
inline void EmitQuadToTriangleListIndices(u8* out_ptr, u32 num_vertices) {
|
|
||||||
u16* out_data = reinterpret_cast<u16*>(out_ptr);
|
|
||||||
for (u16 i = 0; i < num_vertices; i += NumVerticesPerQuad) {
|
|
||||||
*out_data++ = i;
|
|
||||||
*out_data++ = i + 1;
|
|
||||||
*out_data++ = i + 2;
|
|
||||||
*out_data++ = i;
|
|
||||||
*out_data++ = i + 2;
|
|
||||||
*out_data++ = i + 3;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
template <typename T>
|
|
||||||
void ConvertQuadToTriangleListIndices(u8* out_ptr, const u8* in_ptr, u32 num_vertices) {
|
|
||||||
T* out_data = reinterpret_cast<T*>(out_ptr);
|
|
||||||
const T* in_data = reinterpret_cast<const T*>(in_ptr);
|
|
||||||
for (u16 i = 0; i < num_vertices; i += NumVerticesPerQuad) {
|
|
||||||
*out_data++ = in_data[i];
|
|
||||||
*out_data++ = in_data[i + 1];
|
|
||||||
*out_data++ = in_data[i + 2];
|
|
||||||
*out_data++ = in_data[i];
|
|
||||||
*out_data++ = in_data[i + 2];
|
|
||||||
*out_data++ = in_data[i + 3];
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
inline void EmitPolygonToTriangleListIndices(u8* out_ptr, u32 num_vertices) {
|
inline void EmitPolygonToTriangleListIndices(u8* out_ptr, u32 num_vertices) {
|
||||||
u16* out_data = reinterpret_cast<u16*>(out_ptr);
|
u16* out_data = reinterpret_cast<u16*>(out_ptr);
|
||||||
for (u16 i = 1; i < num_vertices - 1; i++) {
|
for (u16 i = 1; i < num_vertices - 1; i++) {
|
||||||
|
@ -107,10 +107,11 @@ GraphicsPipeline::GraphicsPipeline(
|
|||||||
key.primitive_restart_index == 0xFFFFFFFF,
|
key.primitive_restart_index == 0xFFFFFFFF,
|
||||||
"Primitive restart index other than -1 is not supported yet");
|
"Primitive restart index other than -1 is not supported yet");
|
||||||
const bool is_rect_list = key.prim_type == AmdGpu::PrimitiveType::RectList;
|
const bool is_rect_list = key.prim_type == AmdGpu::PrimitiveType::RectList;
|
||||||
|
const bool is_quad_list = key.prim_type == AmdGpu::PrimitiveType::QuadList;
|
||||||
const size_t num_fs_inputs =
|
const size_t num_fs_inputs =
|
||||||
runtime_infos[u32(Shader::LogicalStage::Fragment)].fs_info.num_inputs;
|
runtime_infos[u32(Shader::LogicalStage::Fragment)].fs_info.num_inputs;
|
||||||
const vk::PipelineTessellationStateCreateInfo tessellation_state = {
|
const vk::PipelineTessellationStateCreateInfo tessellation_state = {
|
||||||
.patchControlPoints = is_rect_list ? 3U : key.patch_control_points,
|
.patchControlPoints = is_rect_list ? 3U : (is_quad_list ? 4U : key.patch_control_points),
|
||||||
};
|
};
|
||||||
|
|
||||||
const vk::PipelineRasterizationStateCreateInfo raster_state = {
|
const vk::PipelineRasterizationStateCreateInfo raster_state = {
|
||||||
@ -234,9 +235,9 @@ GraphicsPipeline::GraphicsPipeline(
|
|||||||
.module = modules[stage],
|
.module = modules[stage],
|
||||||
.pName = "main",
|
.pName = "main",
|
||||||
});
|
});
|
||||||
} else if (is_rect_list) {
|
} else if (is_rect_list || is_quad_list) {
|
||||||
auto tcs = Shader::Backend::SPIRV::EmitAuxilaryTessShader(AuxShaderType::RectListTCS,
|
const auto type = is_quad_list ? AuxShaderType::QuadListTCS : AuxShaderType::RectListTCS;
|
||||||
num_fs_inputs);
|
auto tcs = Shader::Backend::SPIRV::EmitAuxilaryTessShader(type, num_fs_inputs);
|
||||||
shader_stages.emplace_back(vk::PipelineShaderStageCreateInfo{
|
shader_stages.emplace_back(vk::PipelineShaderStageCreateInfo{
|
||||||
.stage = vk::ShaderStageFlagBits::eTessellationControl,
|
.stage = vk::ShaderStageFlagBits::eTessellationControl,
|
||||||
.module = CompileSPV(tcs, instance.GetDevice()),
|
.module = CompileSPV(tcs, instance.GetDevice()),
|
||||||
@ -250,7 +251,7 @@ GraphicsPipeline::GraphicsPipeline(
|
|||||||
.module = modules[stage],
|
.module = modules[stage],
|
||||||
.pName = "main",
|
.pName = "main",
|
||||||
});
|
});
|
||||||
} else if (is_rect_list) {
|
} else if (is_rect_list || is_quad_list) {
|
||||||
auto tes = Shader::Backend::SPIRV::EmitAuxilaryTessShader(AuxShaderType::PassthroughTES,
|
auto tes = Shader::Backend::SPIRV::EmitAuxilaryTessShader(AuxShaderType::PassthroughTES,
|
||||||
num_fs_inputs);
|
num_fs_inputs);
|
||||||
shader_stages.emplace_back(vk::PipelineShaderStageCreateInfo{
|
shader_stages.emplace_back(vk::PipelineShaderStageCreateInfo{
|
||||||
|
@ -18,7 +18,7 @@ class TextureCache;
|
|||||||
|
|
||||||
namespace Vulkan {
|
namespace Vulkan {
|
||||||
|
|
||||||
static constexpr u32 MaxShaderStages = 5;
|
static constexpr u32 MaxShaderStages = static_cast<u32>(Shader::LogicalStage::NumLogicalStages);
|
||||||
static constexpr u32 MaxVertexBufferCount = 32;
|
static constexpr u32 MaxVertexBufferCount = 32;
|
||||||
|
|
||||||
class Instance;
|
class Instance;
|
||||||
|
@ -237,9 +237,7 @@ void Rasterizer::Draw(bool is_indexed, u32 index_offset) {
|
|||||||
cmdbuf.drawIndexed(num_indices, regs.num_instances.NumInstances(), 0, s32(vertex_offset),
|
cmdbuf.drawIndexed(num_indices, regs.num_instances.NumInstances(), 0, s32(vertex_offset),
|
||||||
instance_offset);
|
instance_offset);
|
||||||
} else {
|
} else {
|
||||||
const u32 num_vertices =
|
cmdbuf.draw(num_indices, regs.num_instances.NumInstances(), vertex_offset,
|
||||||
regs.primitive_type == AmdGpu::PrimitiveType::RectList ? 3 : regs.num_indices;
|
|
||||||
cmdbuf.draw(num_vertices, regs.num_instances.NumInstances(), vertex_offset,
|
|
||||||
instance_offset);
|
instance_offset);
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -255,18 +253,14 @@ void Rasterizer::DrawIndirect(bool is_indexed, VAddr arg_address, u32 offset, u3
|
|||||||
}
|
}
|
||||||
|
|
||||||
const auto& regs = liverpool->regs;
|
const auto& regs = liverpool->regs;
|
||||||
if (regs.primitive_type == AmdGpu::PrimitiveType::QuadList ||
|
if (regs.primitive_type == AmdGpu::PrimitiveType::Polygon) {
|
||||||
regs.primitive_type == AmdGpu::PrimitiveType::Polygon) {
|
// We use a generated index buffer to convert polygons to triangles. Since it
|
||||||
// We use a generated index buffer to convert quad lists and polygons to triangles. Since it
|
|
||||||
// changes type of the draw, arguments are not valid for this case. We need to run a
|
// changes type of the draw, arguments are not valid for this case. We need to run a
|
||||||
// conversion pass to repack the indirect arguments buffer first.
|
// conversion pass to repack the indirect arguments buffer first.
|
||||||
LOG_WARNING(Render_Vulkan, "Primitive type is not supported for indirect draw");
|
LOG_WARNING(Render_Vulkan, "Primitive type is not supported for indirect draw");
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
ASSERT_MSG(regs.primitive_type != AmdGpu::PrimitiveType::RectList,
|
|
||||||
"Unsupported primitive type for indirect draw");
|
|
||||||
|
|
||||||
const GraphicsPipeline* pipeline = pipeline_cache.GetGraphicsPipeline();
|
const GraphicsPipeline* pipeline = pipeline_cache.GetGraphicsPipeline();
|
||||||
if (!pipeline) {
|
if (!pipeline) {
|
||||||
return;
|
return;
|
||||||
|
Loading…
Reference in New Issue
Block a user