mirror of
https://github.com/KytyPS5/KytyPS5.git
synced 2026-08-03 11:23:49 +00:00
Compare commits
6
Commits
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
3f21e2ee0f | ||
|
|
86a586025f | ||
|
|
06cbd602c1 | ||
|
|
055d0920d7 | ||
|
|
f2ee98fe31 | ||
|
|
207cef9602 |
@@ -89,10 +89,6 @@ bool RenderDocEnabled() {
|
||||
return g_config->renderdoc_enabled;
|
||||
}
|
||||
|
||||
bool NggRectlistDrawEnabled() {
|
||||
return g_config->ngg_rectlist_draw_enabled;
|
||||
}
|
||||
|
||||
bool ReadbackLinearImagesEnabled() {
|
||||
return g_config->readback_linear_images;
|
||||
}
|
||||
|
||||
@@ -35,7 +35,6 @@ struct ConfigOptions {
|
||||
ProfilerDirection profiler_direction = ProfilerDirection::None;
|
||||
bool spirv_debug_printf_enabled = false;
|
||||
bool renderdoc_enabled = false;
|
||||
bool ngg_rectlist_draw_enabled = true;
|
||||
bool readback_linear_images = false;
|
||||
};
|
||||
|
||||
@@ -64,7 +63,6 @@ ProfilerDirection GetProfilerDirection();
|
||||
bool SpirvDebugPrintfEnabled();
|
||||
|
||||
bool RenderDocEnabled();
|
||||
bool NggRectlistDrawEnabled();
|
||||
bool ReadbackLinearImagesEnabled();
|
||||
|
||||
} // namespace Config
|
||||
|
||||
@@ -83,8 +83,7 @@ public:
|
||||
uint32_t first_instance = 0);
|
||||
void DrawIndexOffset(uint32_t index_offset, uint32_t index_count, uint32_t flags);
|
||||
void DrawIndexAuto(uint32_t index_count, uint32_t flags,
|
||||
uint32_t render_target_slice_offset = 0, uint32_t instance_count = 1,
|
||||
uint32_t first_vertex = 0, uint32_t first_instance = 0);
|
||||
uint32_t render_target_slice_offset = 0);
|
||||
void DrawIndirect(uint32_t data_offset, uint32_t draw_initiator, bool indexed);
|
||||
void DrawIndirectMulti(uint32_t data_offset, uint32_t max_count_or_count,
|
||||
const volatile uint32_t* count_addr, uint32_t stride_in_bytes,
|
||||
@@ -152,6 +151,9 @@ private:
|
||||
uint32_t interrupt_context_id);
|
||||
void ProcessPm4(Pm4Execution& execution, size_t stop_depth);
|
||||
void SuspendPm4();
|
||||
void SubmitNonIndexedDraw(uint32_t vertex_count, uint32_t flags,
|
||||
uint32_t render_target_slice_offset, uint32_t first_vertex,
|
||||
uint32_t first_instance);
|
||||
|
||||
CommandScheduler& GetScheduler() const { return m_renderer.GetCommandScheduler(); }
|
||||
RenderCommandBuffer& CurrentBuffer() { return GetScheduler().Current(); }
|
||||
@@ -168,7 +170,8 @@ private:
|
||||
uint64_t m_index_base_addr = 0;
|
||||
uint64_t m_draw_indirect_args_base_addr = 0;
|
||||
uint64_t m_dispatch_indirect_args_base_addr = 0;
|
||||
uint32_t m_num_instances = 1;
|
||||
// Persistent draw state: indirect draws update it for subsequent draws.
|
||||
uint32_t m_num_instances = 1;
|
||||
|
||||
uint32_t m_de_count = 0;
|
||||
uint32_t m_ce_count = 0;
|
||||
|
||||
@@ -983,8 +983,9 @@ void CommandProcessor::DrawIndirect(uint32_t data_offset, uint32_t draw_initiato
|
||||
args.start_vertex_location, args.start_instance_location);
|
||||
}
|
||||
}
|
||||
DrawIndexAuto(args.vertex_count_per_instance, 0, 0, args.instance_count,
|
||||
args.start_vertex_location, args.start_instance_location);
|
||||
m_num_instances = args.instance_count;
|
||||
SubmitNonIndexedDraw(args.vertex_count_per_instance, 0, 0, args.start_vertex_location,
|
||||
args.start_instance_location);
|
||||
return;
|
||||
}
|
||||
|
||||
@@ -1024,6 +1025,7 @@ void CommandProcessor::DrawIndirect(uint32_t data_offset, uint32_t draw_initiato
|
||||
}
|
||||
}
|
||||
|
||||
m_num_instances = args.instance_count;
|
||||
DrawIndex(index_count, index_addr, 0, 1, args.instance_count, nullptr, 0,
|
||||
static_cast<int32_t>(args.base_vertex_location), args.start_instance_location);
|
||||
}
|
||||
@@ -1081,8 +1083,9 @@ void CommandProcessor::DrawIndirectMulti(uint32_t data_offset, uint32_t max_coun
|
||||
args->start_vertex_location, args->start_instance_location);
|
||||
}
|
||||
}
|
||||
DrawIndexAuto(args->vertex_count_per_instance, 0, 0, args->instance_count,
|
||||
args->start_vertex_location, args->start_instance_location);
|
||||
m_num_instances = args->instance_count;
|
||||
SubmitNonIndexedDraw(args->vertex_count_per_instance, 0, 0, args->start_vertex_location,
|
||||
args->start_instance_location);
|
||||
continue;
|
||||
}
|
||||
|
||||
@@ -1123,6 +1126,7 @@ void CommandProcessor::DrawIndirectMulti(uint32_t data_offset, uint32_t max_coun
|
||||
}
|
||||
}
|
||||
|
||||
m_num_instances = args->instance_count;
|
||||
DrawIndex(index_count, index_addr, 0, 1, args->instance_count, nullptr, 0,
|
||||
static_cast<int32_t>(args->base_vertex_location), args->start_instance_location);
|
||||
}
|
||||
@@ -1214,12 +1218,17 @@ void CommandProcessor::DispatchIndirect(uint32_t data_offset, uint32_t mode) {
|
||||
}
|
||||
|
||||
void CommandProcessor::DrawIndexAuto(uint32_t index_count, uint32_t flags,
|
||||
uint32_t render_target_slice_offset, uint32_t instance_count,
|
||||
uint32_t first_vertex, uint32_t first_instance) {
|
||||
uint32_t render_target_slice_offset) {
|
||||
SubmitNonIndexedDraw(index_count, flags, render_target_slice_offset, 0, 0);
|
||||
}
|
||||
|
||||
void CommandProcessor::SubmitNonIndexedDraw(uint32_t vertex_count, uint32_t flags,
|
||||
uint32_t render_target_slice_offset,
|
||||
uint32_t first_vertex, uint32_t first_instance) {
|
||||
CheckBuffer();
|
||||
|
||||
m_renderer.GetRenderExecutor().DrawAuto(m_submit_id, CurrentBuffer(), index_count, flags,
|
||||
render_target_slice_offset, instance_count,
|
||||
m_renderer.GetRenderExecutor().DrawAuto(m_submit_id, CurrentBuffer(), vertex_count, flags,
|
||||
render_target_slice_offset, m_num_instances,
|
||||
first_vertex, first_instance);
|
||||
}
|
||||
|
||||
|
||||
+23
-4
@@ -838,11 +838,20 @@ struct TextureCache::ColorTransferPlan {
|
||||
TextureUploadLayout layout;
|
||||
std::vector<vk::BufferImageCopy> regions;
|
||||
std::vector<GpuTileInfo> tiles;
|
||||
uint64_t linear_size = 0;
|
||||
bool tiled = false;
|
||||
bool swap_bgra16 = false;
|
||||
bool valid = false;
|
||||
};
|
||||
|
||||
static uint64_t GetLinearSize(std::span<const GpuTileInfo> tiles) {
|
||||
uint64_t size = 0;
|
||||
for (const auto& tile: tiles) {
|
||||
size = std::max(size, tile.linear_offset + tile.linear_size);
|
||||
}
|
||||
return size;
|
||||
}
|
||||
|
||||
struct TextureCache::DownloadPlan {
|
||||
ColorTransferPlan color;
|
||||
bool depth = false;
|
||||
@@ -919,6 +928,7 @@ TextureCache::BuildColorTransfer(const Image& image, BindingType binding,
|
||||
info.resources.levels, plan.tiles)) {
|
||||
return plan;
|
||||
}
|
||||
plan.linear_size = GetLinearSize(plan.tiles);
|
||||
}
|
||||
plan.valid = true;
|
||||
return plan;
|
||||
@@ -956,11 +966,19 @@ void TextureCache::UploadImage(Image& image, const ImageDesc& desc, Buffer& sour
|
||||
|
||||
if (desc.type != BindingType::DepthTarget) {
|
||||
auto plan = BuildColorTransfer(image, desc.type, TransferDirection::Upload);
|
||||
EXIT_NOT_IMPLEMENTED(!plan.valid);
|
||||
if (!plan.valid) {
|
||||
EXIT("TextureCache: invalid color upload: binding=%u addr=0x%016" PRIx64
|
||||
" size=0x%016" PRIx64 " format=%u tile=%u family=%u extent=%ux%ux%u "
|
||||
"pitch=%u levels=%u layers=%u samples=%u\n",
|
||||
static_cast<uint32_t>(desc.type), info.data.address, info.data.size,
|
||||
info.guest_format, info.tile_mode, static_cast<uint32_t>(plan.layout.tile_family),
|
||||
info.extent.width, info.extent.height, info.extent.depth, info.pitch,
|
||||
info.resources.levels, info.resources.layers, info.samples);
|
||||
}
|
||||
TileManager::Result linear {source.Handle(), source_offset, info.data.size};
|
||||
if (plan.tiled) {
|
||||
linear = m_tiler->Detile(source.Handle(), source_offset, info.data.size, info.data.size,
|
||||
plan.tiles);
|
||||
linear = m_tiler->Detile(source.Handle(), source_offset, info.data.size,
|
||||
plan.linear_size, plan.tiles);
|
||||
}
|
||||
if (plan.swap_bgra16) {
|
||||
linear = m_tiler->SwapBgra16(linear);
|
||||
@@ -1566,7 +1584,7 @@ void TextureCache::DownloadImageData(Image& image, Buffer& destination, uint64_t
|
||||
}
|
||||
|
||||
m_tiler->TileImage(image, color.regions, destination.Handle(), destination_offset,
|
||||
destination_size, destination_size, color.tiles, transform);
|
||||
destination_size, color.linear_size, color.tiles, transform);
|
||||
}
|
||||
|
||||
bool BufferCache::SynchronizeBufferFromImage(Buffer& buffer, uint64_t vaddr, uint64_t size) {
|
||||
@@ -1659,6 +1677,7 @@ bool BufferCache::SynchronizeBufferFromImage(Buffer& buffer, uint64_t vaddr, uin
|
||||
image.info.TransferLayers(), levels, color.tiles)) {
|
||||
return false;
|
||||
}
|
||||
color.linear_size = GetLinearSize(color.tiles);
|
||||
}
|
||||
}
|
||||
m_texture_cache.DownloadImageData(image, buffer, buf_offset, copy_size, std::move(plan));
|
||||
|
||||
@@ -482,10 +482,10 @@ static bool SetGpuTileSize(uint64_t offset, uint64_t length, uint64_t capacity,
|
||||
return true;
|
||||
}
|
||||
|
||||
bool TextureBuildGpuTileInfos(uint64_t size, const std::vector<vk::BufferImageCopy>& regions,
|
||||
bool TextureBuildGpuTileInfos(uint64_t tiled_size, const std::vector<vk::BufferImageCopy>& regions,
|
||||
const TextureUploadLayout& layout, uint32_t fmt, uint32_t depth,
|
||||
uint64_t levels, std::vector<GpuTileInfo>& out_infos) {
|
||||
if (size == 0 || levels == 0 || levels > 16 || depth == 0 ||
|
||||
if (tiled_size == 0 || levels == 0 || levels > 16 || depth == 0 ||
|
||||
regions.size() != GetTextureRegionCount(depth, levels, layout.volume_texture) ||
|
||||
Prospero::IsFmaskTextureFormat(fmt)) {
|
||||
return false;
|
||||
@@ -538,8 +538,9 @@ bool TextureBuildGpuTileInfos(uint64_t size, const std::vector<vk::BufferImageCo
|
||||
const uint64_t linear_span =
|
||||
static_cast<uint64_t>(copy_depth - 1u) * linear_stride +
|
||||
layout.level_sizes[level].size;
|
||||
if (!SetGpuTileSize(info.linear_offset, linear_span, size, info.linear_size) ||
|
||||
!SetGpuTileSize(info.tiled_offset, volume.level_sizes[level], size,
|
||||
if (!SetGpuTileSize(info.linear_offset, linear_span, UINT64_MAX,
|
||||
info.linear_size) ||
|
||||
!SetGpuTileSize(info.tiled_offset, volume.level_sizes[level], tiled_size,
|
||||
info.tiled_size)) {
|
||||
return false;
|
||||
}
|
||||
@@ -588,8 +589,9 @@ bool TextureBuildGpuTileInfos(uint64_t size, const std::vector<vk::BufferImageCo
|
||||
info.bytes_per_element = block.bytes_per_element;
|
||||
info.linear_offset = region.bufferOffset;
|
||||
info.tiled_offset = TextureUploadSliceSourceOffset(layout, level, z);
|
||||
if (!SetGpuTileSize(info.linear_offset, level_size.size, size, info.linear_size) ||
|
||||
!SetGpuTileSize(info.tiled_offset, GetLevelSrcSize(level_size), size,
|
||||
if (!SetGpuTileSize(info.linear_offset, level_size.size, UINT64_MAX,
|
||||
info.linear_size) ||
|
||||
!SetGpuTileSize(info.tiled_offset, GetLevelSrcSize(level_size), tiled_size,
|
||||
info.tiled_size)) {
|
||||
return false;
|
||||
}
|
||||
|
||||
@@ -44,7 +44,7 @@ std::vector<vk::BufferImageCopy> TextureBuildImageCopies(const TextureUploadLayo
|
||||
uint32_t width, uint32_t height,
|
||||
uint32_t depth, uint64_t levels,
|
||||
bool array_texture, bool volume_texture);
|
||||
bool TextureBuildGpuTileInfos(uint64_t size, const std::vector<vk::BufferImageCopy>& regions,
|
||||
bool TextureBuildGpuTileInfos(uint64_t tiled_size, const std::vector<vk::BufferImageCopy>& regions,
|
||||
const TextureUploadLayout& layout, uint32_t fmt, uint32_t depth,
|
||||
uint64_t levels, std::vector<GpuTileInfo>& infos);
|
||||
|
||||
|
||||
@@ -391,9 +391,8 @@ static bool IsSupportedStorageTextureDescriptor(const ShaderRecompiler::IR::Imag
|
||||
const bool supported_swizzle =
|
||||
IsValidImageSwizzle(swizzle) &&
|
||||
(swizzle == DstSel(4, 5, 6, 7) || !resource.read || resource.atomic);
|
||||
const bool supported_mip_view = descriptor.BaseLevel() == 0 || is_1d || is_2d;
|
||||
return (is_1d || is_1d_array || is_2d || is_2d_array || is_3d) && supported_tile &&
|
||||
supported_mip_view && descriptor.BaseLevel() == descriptor.LastLevel() &&
|
||||
descriptor.BaseLevel() == descriptor.LastLevel() &&
|
||||
descriptor.LastLevel() <= descriptor.MaxMip() && descriptor.MinLod() == 0 &&
|
||||
supported_swizzle && descriptor.BCSwizzle() == 0 && !descriptor.MsaaDepth();
|
||||
}
|
||||
@@ -618,14 +617,15 @@ RenderExecutor::ResolveTexture(const ShaderRecompiler::IR::ImageResource& reso
|
||||
const bool multisampled = IsMultisampledTexture(type);
|
||||
const auto levels = multisampled ? 1u : static_cast<uint32_t>(descriptor.MaxMip()) + 1u;
|
||||
const auto tile = descriptor.TileMode();
|
||||
const bool depth_tile = tile == Prospero::GpuEnumValue(Prospero::TileMode::kDepth);
|
||||
const bool depth_tile = tile == Prospero::GpuEnumValue(Prospero::TileMode::kDepth);
|
||||
const bool msaa_tile =
|
||||
depth_tile || tile == Prospero::GpuEnumValue(Prospero::TileMode::kRenderTarget);
|
||||
const bool msaa_array = type == Prospero::ImageType::kColor2DMsaaArray;
|
||||
if ((!multisampled && (base_level > last_level || last_level >= levels)) ||
|
||||
(multisampled &&
|
||||
(base_level != 0 || last_level == 0 || last_level > 3 ||
|
||||
descriptor.MaxMip() != last_level || !msaa_tile || (descriptor.MsaaDepth() && !depth_tile) ||
|
||||
descriptor.MaxMip() != last_level || !msaa_tile ||
|
||||
(descriptor.MsaaDepth() && !depth_tile) ||
|
||||
(!msaa_array && (descriptor.Depth() != 0 || descriptor.BaseArray5() != 0))))) {
|
||||
EXIT("unsupported texture mip view: base=%u last=%u levels=%u max=%u type=%u tile=%u "
|
||||
"kind=%u dimension=%u mip_mode=%u read=%d written=%d "
|
||||
@@ -634,7 +634,8 @@ RenderExecutor::ResolveTexture(const ShaderRecompiler::IR::ImageResource& reso
|
||||
static_cast<uint32_t>(resource.kind), static_cast<uint32_t>(resource.dimension),
|
||||
static_cast<uint32_t>(resource.mip_mode), resource.read, resource.written,
|
||||
descriptor.fields[0], descriptor.fields[1], descriptor.fields[2], descriptor.fields[3],
|
||||
descriptor.fields[4], descriptor.fields[5], descriptor.fields[6], descriptor.fields[7]);
|
||||
descriptor.fields[4], descriptor.fields[5], descriptor.fields[6],
|
||||
descriptor.fields[7]);
|
||||
}
|
||||
const auto samples = multisampled ? 1u << last_level : 1u;
|
||||
const auto view_levels =
|
||||
@@ -661,8 +662,8 @@ RenderExecutor::ResolveTexture(const ShaderRecompiler::IR::ImageResource& reso
|
||||
TileSizeAlign size {};
|
||||
if (multisampled) {
|
||||
const auto bytes = Prospero::NumBytesPerElement(format);
|
||||
pitch = depth_tile ? TileGetDepthPitch(width, bytes, last_level)
|
||||
: TileGetRenderTargetPitch(width, bytes, last_level);
|
||||
pitch = depth_tile ? TileGetDepthPitch(width, bytes, last_level)
|
||||
: TileGetRenderTargetPitch(width, bytes, last_level);
|
||||
if (pitch == 0 || !TileGetRenderTargetSize(width, height, pitch, bytes, size, last_level) ||
|
||||
size.size > UINT32_MAX / image_layers) {
|
||||
EXIT("unsupported multisample texture layout\n");
|
||||
@@ -679,8 +680,8 @@ RenderExecutor::ResolveTexture(const ShaderRecompiler::IR::ImageResource& reso
|
||||
ValidateStorageTexture(resource, descriptor, size.size);
|
||||
}
|
||||
|
||||
const auto pixel_format = TextureGetFormat(format);
|
||||
const auto storage_view_format =
|
||||
const auto pixel_format = TextureGetFormat(format);
|
||||
const auto storage_view_format =
|
||||
storage && format == Prospero::GpuEnumValue(Prospero::BufferFormat::k32SInt)
|
||||
? vk::Format::eR32Uint
|
||||
: SrgbStorageViewFormat(pixel_format);
|
||||
|
||||
@@ -154,8 +154,9 @@ PipelineCache::GraphicsPipeline& PipelineCache::CreateGraphicsPipeline(
|
||||
for (uint32_t i = 0; i < RENDER_COLOR_ATTACHMENTS_MAX; i++) {
|
||||
static_params.color_mask[i] = color_mask[i];
|
||||
}
|
||||
static_params.cull_back = mc.cull_back;
|
||||
static_params.cull_front = mc.cull_front;
|
||||
const bool rect_list = topology == vk::PrimitiveTopology::ePatchList;
|
||||
static_params.cull_back = !rect_list && mc.cull_back;
|
||||
static_params.cull_front = !rect_list && mc.cull_front;
|
||||
static_params.face = mc.face;
|
||||
|
||||
for (uint32_t i = 0; i < color_count; i++) {
|
||||
|
||||
@@ -14,6 +14,7 @@
|
||||
#include "graphics/host_gpu/renderer/renderTarget.h"
|
||||
#include "graphics/host_gpu/vulkanCommon.h"
|
||||
#include "graphics/shader/recompiler/ir/ShaderIR.h"
|
||||
#include "graphics/shader/rectListShader.h"
|
||||
#include "graphics/shader/shader.h"
|
||||
|
||||
#include <algorithm>
|
||||
@@ -463,8 +464,12 @@ void CreatePipelineInternal(
|
||||
uint32_t ps_hash0, uint32_t ps_crc32, bool ps_active) {
|
||||
EXIT_IF(ps_active && ps_input_info == nullptr);
|
||||
|
||||
vk::ShaderModule vert_shader_module = nullptr;
|
||||
vk::ShaderModule frag_shader_module = nullptr;
|
||||
const bool rect_list = static_params.topology == vk::PrimitiveTopology::ePatchList;
|
||||
|
||||
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::ShaderModuleCreateInfo create_info {};
|
||||
|
||||
@@ -491,8 +496,33 @@ void CreatePipelineInternal(
|
||||
}
|
||||
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(
|
||||
rect_list && (tess_control_shader_module == nullptr || tess_eval_shader_module == nullptr));
|
||||
EXIT_NOT_IMPLEMENTED(ps_active && frag_shader_module == nullptr);
|
||||
|
||||
vk::PipelineShaderStageCreateInfo vert_shader_stage_info {};
|
||||
@@ -525,9 +555,28 @@ void CreatePipelineInternal(
|
||||
frag_shader_stage_info);
|
||||
}
|
||||
|
||||
vk::PipelineShaderStageCreateInfo shader_stages[] = {vert_shader_stage_info,
|
||||
frag_shader_stage_info};
|
||||
const uint32_t shader_stage_count = ps_active ? 2u : 1u;
|
||||
vk::PipelineShaderStageCreateInfo tess_control_shader_stage_info {};
|
||||
tess_control_shader_stage_info.sType = vk::StructureType::ePipelineShaderStageCreateInfo;
|
||||
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::VertexInputBindingDescription input_desc[ShaderVertexInputInfo::RES_MAX];
|
||||
@@ -929,10 +978,13 @@ void CreatePipelineInternal(
|
||||
pipeline_info.pStages = shader_stages;
|
||||
pipeline_info.pVertexInputState = &vertex_input_info;
|
||||
pipeline_info.pInputAssemblyState = &input_assembly;
|
||||
pipeline_info.pTessellationState = nullptr;
|
||||
pipeline_info.pViewportState = &viewport_state;
|
||||
pipeline_info.pRasterizationState = &rasterizer;
|
||||
pipeline_info.pMultisampleState = &multisampling;
|
||||
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.pRasterizationState = &rasterizer;
|
||||
pipeline_info.pMultisampleState = &multisampling;
|
||||
pipeline_info.pDepthStencilState = (static_params.with_depth ? &depth_stencil_info : nullptr);
|
||||
pipeline_info.pColorBlendState = &color_blending;
|
||||
pipeline_info.pDynamicState = &dynamic_state;
|
||||
@@ -968,6 +1020,12 @@ void CreatePipelineInternal(
|
||||
if (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);
|
||||
}
|
||||
|
||||
|
||||
@@ -2,7 +2,6 @@
|
||||
|
||||
#include "common/assert.h"
|
||||
#include "common/common.h"
|
||||
#include "common/emulatorConfig.h"
|
||||
#include "common/file.h"
|
||||
#include "common/logging/log.h"
|
||||
#include "common/profiler.h"
|
||||
@@ -650,11 +649,9 @@ static bool ConsumeMetadataColorOperation(const RenderCommandBuffer& buffer) {
|
||||
}
|
||||
|
||||
struct DrawEmitInfo {
|
||||
bool indexed = false;
|
||||
bool draw_prim7_as_ngg = false;
|
||||
uint32_t draw_vertex_count = 0;
|
||||
int32_t vertex_offset = 0;
|
||||
uint32_t first_vertex = 0;
|
||||
bool indexed = false;
|
||||
int32_t vertex_offset = 0;
|
||||
uint32_t first_vertex = 0;
|
||||
};
|
||||
|
||||
struct DrawIndexBufferSource {
|
||||
@@ -767,7 +764,7 @@ static void SetDrawDebugPhase(RenderCommandBuffer& buffer, uint64_t submit_id,
|
||||
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) {
|
||||
|
||||
topology = vk::PrimitiveTopology::ePointList;
|
||||
@@ -791,8 +788,7 @@ static bool GetDrawTopology(const HW::UserConfig& ucfg, bool auto_draw, bool use
|
||||
topology = vk::PrimitiveTopology::eTriangleStrip;
|
||||
break;
|
||||
case Prospero::PrimitiveType::kRectList:
|
||||
topology = (auto_draw && use_ngg_rectlist_draw ? vk::PrimitiveTopology::eTriangleStrip
|
||||
: vk::PrimitiveTopology::eTriangleList);
|
||||
topology = vk::PrimitiveTopology::ePatchList;
|
||||
break;
|
||||
case Prospero::PrimitiveType::kRectListLegacy:
|
||||
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);
|
||||
}
|
||||
|
||||
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,
|
||||
const ShaderVertexInputInfo& vs_input_info, const DrawCallInfo& draw,
|
||||
const DrawEmitInfo& emit) {
|
||||
@@ -1017,22 +999,12 @@ static void EmitDrawPrimitives(const HW::UserConfig& ucfg, vk::CommandBuffer vk_
|
||||
case Prospero::PrimitiveType::kTriList:
|
||||
case Prospero::PrimitiveType::kTriFan:
|
||||
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:
|
||||
if (emit.indexed) {
|
||||
vk_buffer.drawIndexed(draw.index_count, draw.instance_count, 0, emit.vertex_offset,
|
||||
draw.first_instance);
|
||||
} else {
|
||||
EXIT_NOT_IMPLEMENTED(
|
||||
!IsHostExpandedRectListDrawSupported(vs_input_info, draw, emit));
|
||||
vk_buffer.draw(emit.draw_vertex_count, draw.instance_count, emit.first_vertex,
|
||||
vk_buffer.draw(draw.index_count, draw.instance_count, emit.first_vertex,
|
||||
draw.first_instance);
|
||||
}
|
||||
break;
|
||||
@@ -1159,7 +1131,7 @@ void RenderExecutor::DrawIndex(uint64_t submit_id, RenderCommandBuffer& buffer,
|
||||
reinterpret_cast<uint64_t>(index_addr));
|
||||
|
||||
Common::LockGuard lock(m_context.GetMutex());
|
||||
if (index_count == 0) {
|
||||
if (index_count == 0 || instance_count == 0) {
|
||||
return;
|
||||
}
|
||||
|
||||
@@ -1201,7 +1173,7 @@ void RenderExecutor::DrawIndex(uint64_t submit_id, RenderCommandBuffer& buffer,
|
||||
hw_check(buffer);
|
||||
|
||||
vk::PrimitiveTopology topology = vk::PrimitiveTopology::ePointList;
|
||||
if (!GetDrawTopology(ucfg, false, false, topology)) {
|
||||
if (!GetDrawTopology(ucfg, false, topology)) {
|
||||
return;
|
||||
}
|
||||
|
||||
@@ -1229,10 +1201,6 @@ void RenderExecutor::DrawIndex(uint64_t submit_id, RenderCommandBuffer& buffer,
|
||||
|
||||
EXIT_NOT_IMPLEMENTED(flags != 0);
|
||||
EXIT_NOT_IMPLEMENTED(type != 1);
|
||||
if (instance_count == 0) {
|
||||
instance_count = 1;
|
||||
}
|
||||
|
||||
const DrawCallInfo draw {"DrawIndex", CommandBufferDebugOp::DrawIndex,
|
||||
index_count, flags,
|
||||
instance_count, first_instance};
|
||||
@@ -1292,7 +1260,7 @@ void RenderExecutor::DrawAuto(uint64_t submit_id, RenderCommandBuffer& buffer, u
|
||||
index_count, flags, first_vertex, instance_count, first_instance);
|
||||
|
||||
Common::LockGuard lock(m_context.GetMutex());
|
||||
if (index_count == 0) {
|
||||
if (index_count == 0 || instance_count == 0) {
|
||||
return;
|
||||
}
|
||||
|
||||
@@ -1330,10 +1298,6 @@ void RenderExecutor::DrawAuto(uint64_t submit_id, RenderCommandBuffer& buffer, u
|
||||
hw_check(buffer);
|
||||
|
||||
EXIT_NOT_IMPLEMENTED(flags != 0);
|
||||
if (instance_count == 0) {
|
||||
instance_count = 1;
|
||||
}
|
||||
|
||||
const DrawCallInfo draw {"DrawIndexAuto", CommandBufferDebugOp::DrawIndexAuto,
|
||||
index_count, flags,
|
||||
instance_count, first_instance};
|
||||
@@ -1345,20 +1309,15 @@ void RenderExecutor::DrawAuto(uint64_t submit_id, RenderCommandBuffer& buffer, u
|
||||
return;
|
||||
}
|
||||
|
||||
vk::PrimitiveTopology topology = vk::PrimitiveTopology::ePointList;
|
||||
const bool use_ngg_rectlist_draw = Config::NggRectlistDrawEnabled();
|
||||
|
||||
if (!GetDrawTopology(ucfg, true, use_ngg_rectlist_draw, topology)) {
|
||||
vk::PrimitiveTopology topology = vk::PrimitiveTopology::ePointList;
|
||||
if (!GetDrawTopology(ucfg, true, topology)) {
|
||||
ResetBindings();
|
||||
return;
|
||||
}
|
||||
const bool draw_prim7_as_ngg =
|
||||
(use_ngg_rectlist_draw &&
|
||||
ucfg.GetPrimType() == Prospero::GpuEnumValue(Prospero::PrimitiveType::kRectList));
|
||||
|
||||
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) {
|
||||
if (graphics_debug_dump_enabled()) {
|
||||
LOGF("DrawIndexAuto: skipping rect-list draw with no VS param exports and PS inputs: "
|
||||
@@ -1375,13 +1334,10 @@ void RenderExecutor::DrawAuto(uint64_t submit_id, RenderCommandBuffer& buffer, u
|
||||
Prospero::GpuEnumValue(Prospero::PrimitiveType::kRectListLegacy),
|
||||
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) +
|
||||
static_cast<int32_t>(first_vertex);
|
||||
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);
|
||||
const auto vertex_offset = ResolveVertexOffset(ucfg.GetIndexOffset(), state.vs_input_info) +
|
||||
static_cast<int32_t>(first_vertex);
|
||||
DrawEmitInfo emit {};
|
||||
emit.first_vertex = static_cast<uint32_t>(vertex_offset);
|
||||
|
||||
DrawIndexBufferSource index_source {};
|
||||
ExecutePreparedDraw(submit_id, buffer, draw, state, topology, emit, index_source, false, false,
|
||||
|
||||
@@ -19,7 +19,7 @@ static void AppendInstructionWords(std::vector<uint32_t>& section, const uint32_
|
||||
section.insert(section.end(), words + 1, words + words_num);
|
||||
}
|
||||
|
||||
Builder::Builder() {
|
||||
Builder::Builder(uint32_t version): m_version(version) {
|
||||
m_debug.reserve(InitialSpirvSectionReserve);
|
||||
m_annotations.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());
|
||||
|
||||
module.push_back(0x07230203u);
|
||||
module.push_back(0x00010300u);
|
||||
module.push_back(m_version);
|
||||
module.push_back(0u);
|
||||
module.push_back(m_next_id);
|
||||
module.push_back(0u);
|
||||
|
||||
@@ -10,7 +10,7 @@ namespace Libs::Graphics::ShaderRecompiler::Spirv {
|
||||
|
||||
class Builder {
|
||||
public:
|
||||
Builder();
|
||||
explicit Builder(uint32_t version = 0x00010300u);
|
||||
~Builder() = default;
|
||||
KYTY_CLASS_DEFAULT_COPY(Builder);
|
||||
|
||||
@@ -39,6 +39,7 @@ private:
|
||||
static void AppendString(std::vector<uint32_t>& words, const char* text);
|
||||
|
||||
uint32_t m_next_id = 1;
|
||||
uint32_t m_version = 0;
|
||||
std::vector<uint32_t> m_capabilities;
|
||||
std::vector<uint32_t> m_extensions;
|
||||
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) {
|
||||
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;
|
||||
}
|
||||
// VINTRP ATTR selects the PS input slot. SPI_PS_INPUT_CNTL maps that slot to a
|
||||
// VS parameter export, which is the SPIR-V location we must link against.
|
||||
return ps->interpolator_settings[attr] & PsInputOffsetMask;
|
||||
return ShaderPixelParameterMappedLocation(*ps, 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) {
|
||||
if (input.kind != IR::StageInputKind::Parameter) {
|
||||
continue;
|
||||
}
|
||||
|
||||
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;
|
||||
if (input.kind == IR::StageInputKind::Parameter) {
|
||||
active_inputs[active_count++] = input.location;
|
||||
}
|
||||
}
|
||||
|
||||
return PixelParameterMappedLocation(state, attr);
|
||||
return state.stage == ShaderType::Pixel && state.pixel_input_info != nullptr
|
||||
? ShaderPixelParameterLocation(*state.pixel_input_info,
|
||||
{active_inputs.data(), active_count}, attr)
|
||||
: attr;
|
||||
}
|
||||
|
||||
bool PixelParameterIsFlat(const EmitterState& state, uint32_t attr) {
|
||||
const auto* ps = state.pixel_input_info;
|
||||
if (state.stage != ShaderType::Pixel || ps == nullptr || attr >= ps->input_num) {
|
||||
return false;
|
||||
}
|
||||
return (ps->interpolator_settings[attr] & PsInputFlatShade) != 0;
|
||||
return state.stage == ShaderType::Pixel && ps != nullptr &&
|
||||
ShaderPixelParameterIsFlat(*ps, attr);
|
||||
}
|
||||
|
||||
void SetError(std::string* error, const char* message) {
|
||||
|
||||
@@ -425,9 +425,6 @@ struct EmitterState {
|
||||
std::map<uint32_t, uint32_t> float_constants;
|
||||
};
|
||||
|
||||
constexpr uint32_t PsInputOffsetMask = 0x0000001fu;
|
||||
constexpr uint32_t PsInputFlatShade = 0x00000400u;
|
||||
|
||||
enum class VertexInputScalarKind { Float, Sint, Uint };
|
||||
|
||||
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 {
|
||||
|
||||
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 {
|
||||
uint8_t signature[7];
|
||||
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_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_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_sample_mask_export_enable));
|
||||
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; }
|
||||
};
|
||||
|
||||
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 {
|
||||
uint16_t offset_dw : 15;
|
||||
uint16_t size : 1;
|
||||
|
||||
+40
-41
@@ -365,6 +365,7 @@ struct PthreadAttrPrivate {
|
||||
uint64_t stack_map_addr;
|
||||
size_t stack_map_size;
|
||||
int policy;
|
||||
int guest_priority;
|
||||
int inherit_sched;
|
||||
int solosched;
|
||||
bool detached;
|
||||
@@ -2128,13 +2129,8 @@ int KYTY_SYSV_ABI PthreadAttrGetschedparam(const PthreadAttr* attr, KernelSchedP
|
||||
|
||||
int result = pthread_attr_getschedparam(&(*attr)->p, param);
|
||||
|
||||
if (param->sched_priority <= -2) {
|
||||
param->sched_priority = 767;
|
||||
} else if (param->sched_priority >= +2) {
|
||||
param->sched_priority = 256;
|
||||
} else {
|
||||
param->sched_priority = 700;
|
||||
}
|
||||
// Host priority mapping is lossy; return the exact guest value.
|
||||
param->sched_priority = (*attr)->guest_priority;
|
||||
|
||||
if (result == 0) {
|
||||
return OK;
|
||||
@@ -2309,10 +2305,17 @@ int KYTY_SYSV_ABI PthreadAttrSetschedparam(PthreadAttr* attr, const KernelSchedP
|
||||
|
||||
int result = pthread_attr_setschedparam(&attr_value->p, &pparam);
|
||||
|
||||
#if KYTY_PLATFORM == KYTY_PLATFORM_WINDOWS
|
||||
if (result == 0) {
|
||||
attr_value->guest_priority = param->sched_priority;
|
||||
return OK;
|
||||
}
|
||||
return KERNEL_ERROR_EINVAL;
|
||||
#else
|
||||
attr_value->guest_priority = param->sched_priority;
|
||||
(void)result;
|
||||
return OK;
|
||||
#endif
|
||||
}
|
||||
|
||||
int KYTY_SYSV_ABI PthreadAttrSetschedpolicy(PthreadAttr* attr, int policy) {
|
||||
@@ -3639,26 +3642,15 @@ int KYTY_SYSV_ABI PthreadGetprio(Pthread thread, int* prio) {
|
||||
|
||||
EXIT_NOT_IMPLEMENTED(prio == nullptr);
|
||||
|
||||
sched_param param {};
|
||||
int pol = 0;
|
||||
|
||||
int result = pthread_getschedparam(thread->p, &pol, ¶m);
|
||||
|
||||
if (result == 0) {
|
||||
if (param.sched_priority <= -2) {
|
||||
*prio = 767;
|
||||
} else if (param.sched_priority >= +2) {
|
||||
*prio = 256;
|
||||
} else {
|
||||
*prio = 700;
|
||||
}
|
||||
|
||||
LOGF("\t PthreadGetprio: %d, %d\n", thread->unique_id, *prio);
|
||||
|
||||
return OK;
|
||||
sched_param native_param {};
|
||||
int native_policy = 0;
|
||||
if (pthread_getschedparam(thread->p, &native_policy, &native_param) != 0) {
|
||||
return KERNEL_ERROR_EINVAL;
|
||||
}
|
||||
|
||||
return KERNEL_ERROR_EINVAL;
|
||||
*prio = thread->attr->guest_priority;
|
||||
LOGF("\t PthreadGetprio: %d, %d\n", thread->unique_id, *prio);
|
||||
return OK;
|
||||
}
|
||||
|
||||
int KYTY_SYSV_ABI PthreadSetprio(Pthread thread, int prio) {
|
||||
@@ -3673,25 +3665,32 @@ int KYTY_SYSV_ABI PthreadSetprio(Pthread thread, int prio) {
|
||||
|
||||
int result = pthread_getschedparam(thread->p, &pol, ¶m);
|
||||
|
||||
if (result == 0) {
|
||||
if (prio <= 478) {
|
||||
param.sched_priority = +2;
|
||||
} else if (prio >= 733) {
|
||||
param.sched_priority = -2;
|
||||
} else {
|
||||
param.sched_priority = 0;
|
||||
}
|
||||
|
||||
result = pthread_setschedparam(thread->p, pol, ¶m);
|
||||
|
||||
if (result == 0) {
|
||||
LOGF("\t PthreadSetprio: %d, %d\n", thread->unique_id, prio);
|
||||
|
||||
return OK;
|
||||
}
|
||||
if (result != 0) {
|
||||
return KERNEL_ERROR_EINVAL;
|
||||
}
|
||||
|
||||
if (prio <= 478) {
|
||||
param.sched_priority = +2;
|
||||
} else if (prio >= 733) {
|
||||
param.sched_priority = -2;
|
||||
} else {
|
||||
param.sched_priority = 0;
|
||||
}
|
||||
|
||||
result = pthread_setschedparam(thread->p, pol, ¶m);
|
||||
|
||||
if (result == 0) {
|
||||
thread->attr->guest_priority = prio;
|
||||
LOGF("\t PthreadSetprio: %d, %d\n", thread->unique_id, prio);
|
||||
return OK;
|
||||
}
|
||||
|
||||
#if KYTY_PLATFORM == KYTY_PLATFORM_WINDOWS
|
||||
return KERNEL_ERROR_EINVAL;
|
||||
#else
|
||||
thread->attr->guest_priority = prio;
|
||||
return OK;
|
||||
#endif
|
||||
}
|
||||
|
||||
void KYTY_SYSV_ABI PthreadTestcancel() {
|
||||
|
||||
@@ -78,19 +78,6 @@
|
||||
</property>
|
||||
</widget>
|
||||
</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>
|
||||
</widget>
|
||||
</item>
|
||||
|
||||
@@ -94,7 +94,6 @@ public:
|
||||
QString printf_output_file = "_kyty.txt";
|
||||
ProfilerDirection profiler_direction = ProfilerDirection::None;
|
||||
bool renderdoc_enabled = false;
|
||||
bool ngg_rectlist_draw_enabled = true;
|
||||
|
||||
QString elf = QStringLiteral("eboot.bin");
|
||||
|
||||
@@ -112,7 +111,6 @@ public:
|
||||
printf_output_file = other.printf_output_file;
|
||||
profiler_direction = other.profiler_direction;
|
||||
renderdoc_enabled = other.renderdoc_enabled;
|
||||
ngg_rectlist_draw_enabled = other.ngg_rectlist_draw_enabled;
|
||||
}
|
||||
|
||||
void CopyFrom(const Configuration& other) {
|
||||
@@ -147,7 +145,6 @@ public:
|
||||
KYTY_CFG_SET(printf_output_file);
|
||||
KYTY_CFG_SET(profiler_direction);
|
||||
KYTY_CFG_SET(renderdoc_enabled);
|
||||
KYTY_CFG_SET(ngg_rectlist_draw_enabled);
|
||||
KYTY_CFG_SET(elf);
|
||||
}
|
||||
|
||||
@@ -169,8 +166,6 @@ public:
|
||||
KYTY_CFG_GET(printf_output_file);
|
||||
KYTY_CFG_GET(profiler_direction);
|
||||
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();
|
||||
}
|
||||
};
|
||||
|
||||
@@ -124,7 +124,6 @@ void ConfigurationEditDialog::Init(const Configuration& info) {
|
||||
m_ui->checkBox_shader_validation->setChecked(info.shader_validation_enabled);
|
||||
m_ui->checkBox_vulkan_validation->setChecked(info.vulkan_validation_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_log_direction, info.shader_log_direction);
|
||||
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.shader_validation_enabled = ui.checkBox_shader_validation->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>(
|
||||
ui.comboBox_shader_optimization_type->currentText());
|
||||
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 << "--profiler-direction" << EnumToText(info.profiler_direction);
|
||||
args << "--spirv-debug-printf" << "false";
|
||||
args << "--ngg-rectlist-draw" << BoolArg(info.ngg_rectlist_draw_enabled);
|
||||
if (info.renderdoc_enabled) {
|
||||
args << "--rd";
|
||||
}
|
||||
|
||||
@@ -59,8 +59,6 @@ static void PrintUsage() {
|
||||
::printf(" --printf-output-file <path> Guest printf output file.\n");
|
||||
::printf(" --profiler-direction <value> None or Network.\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(
|
||||
" --readback-linear-images <true|false> Read back writable linear images on submit.\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());
|
||||
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") {
|
||||
if (!ParseBool(value, options.config.readback_linear_images)) {
|
||||
::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/SpirvEmitter.h"
|
||||
#include "graphics/shader/recompiler/ir/BindingLayout.h"
|
||||
#include "graphics/shader/rectListShader.h"
|
||||
#include "graphics/shader/shader.h"
|
||||
#include "kernel/memory.h"
|
||||
#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) {
|
||||
if (test.required_spirv.empty() && test.forbidden_spirv.empty()) {
|
||||
return;
|
||||
@@ -7279,26 +7365,30 @@ public:
|
||||
}
|
||||
|
||||
u32 case_index = 0;
|
||||
auto check_round_trip = [&](const char* stage, uint64_t size,
|
||||
auto check_round_trip = [&](const char* stage, uint64_t tiled_size,
|
||||
std::span<const GpuTileInfo> infos) {
|
||||
std::vector<uint8_t> tiled(size);
|
||||
std::vector<uint8_t> cpu(size, 0);
|
||||
std::vector<uint8_t> gpu(size, 0xab);
|
||||
uint64_t linear_size = 0;
|
||||
for (const auto& info: infos) {
|
||||
linear_size = std::max(linear_size, info.linear_offset + info.linear_size);
|
||||
}
|
||||
std::vector<uint8_t> tiled(tiled_size);
|
||||
std::vector<uint8_t> cpu(linear_size, 0);
|
||||
std::vector<uint8_t> gpu(linear_size, 0xab);
|
||||
fill(&tiled, ++case_index);
|
||||
for (const auto& info: infos) {
|
||||
convert_reference(false, &cpu, tiled, info);
|
||||
}
|
||||
gpu_detile(tiled, &gpu, size, size, infos);
|
||||
gpu_detile(tiled, &gpu, tiled_size, linear_size, infos);
|
||||
compare((std::string(stage) + " detile bytes").c_str(), cpu, gpu);
|
||||
|
||||
std::vector<uint8_t> linear(size);
|
||||
std::vector<uint8_t> cpu_tiled(size, 0xab);
|
||||
std::vector<uint8_t> gpu_tiled(size, 0xab);
|
||||
std::vector<uint8_t> linear(linear_size);
|
||||
std::vector<uint8_t> cpu_tiled(tiled_size, 0xab);
|
||||
std::vector<uint8_t> gpu_tiled(tiled_size, 0xab);
|
||||
fill(&linear, 0x280u + case_index);
|
||||
for (const auto& info: infos) {
|
||||
convert_reference(true, &cpu_tiled, linear, info);
|
||||
}
|
||||
gpu_tile(linear, &gpu_tiled, size, size, infos);
|
||||
gpu_tile(linear, &gpu_tiled, tiled_size, linear_size, infos);
|
||||
compare((std::string(stage) + " tile bytes").c_str(), cpu_tiled, gpu_tiled);
|
||||
};
|
||||
for (const auto family: families) {
|
||||
@@ -7436,6 +7526,32 @@ public:
|
||||
Require(name, "format coverage", format_cases != 0,
|
||||
"no CPU-supported standard formats were tested");
|
||||
|
||||
{
|
||||
constexpr u32 format = Prospero::GpuEnumValue(Prospero::BufferFormat::kBc1UNorm);
|
||||
constexpr u32 tile = Prospero::GpuEnumValue(Prospero::TileMode::kStandard64KB);
|
||||
constexpr u32 width = 256, height = 256, levels = 9;
|
||||
const u32 pitch = TileGetTexturePitch(format, width, levels, tile);
|
||||
TileSizeAlign total {};
|
||||
TileGetTextureSize(format, width, height, pitch, levels, tile, &total, nullptr,
|
||||
nullptr);
|
||||
const auto layout = TextureCalcUploadLayout(format, width, height, levels, 1, pitch,
|
||||
tile, total.size, false, false, name);
|
||||
const auto regions =
|
||||
TextureBuildImageCopies(layout, width, height, 1, levels, false, false);
|
||||
std::vector<GpuTileInfo> infos;
|
||||
const bool built =
|
||||
TextureBuildGpuTileInfos(total.size, regions, layout, format, 1, levels, infos);
|
||||
uint64_t linear_size = 0;
|
||||
for (const auto& info: infos) {
|
||||
linear_size = std::max(linear_size, info.linear_offset + info.linear_size);
|
||||
}
|
||||
Require(name, "BC1 mip-tail capacities",
|
||||
built && total.size == 0x10000 && layout.first_tail_level == 0 &&
|
||||
linear_size == 0x15560 && linear_size > total.size,
|
||||
"BC1 mip tail conflated tiled and linear capacities");
|
||||
check_round_trip("BC1 mip tail", total.size, infos);
|
||||
}
|
||||
|
||||
{
|
||||
constexpr u32 format = Prospero::GpuEnumValue(Prospero::BufferFormat::k32Float);
|
||||
constexpr u32 tile = Prospero::GpuEnumValue(Prospero::TileMode::kRenderTarget);
|
||||
@@ -15959,11 +16075,6 @@ ShaderTextureResource AtomicStorageTextureDescriptor() {
|
||||
resource = BasicArrayStorageTextureResource();
|
||||
descriptor = BasicArrayStorageTextureDescriptor();
|
||||
descriptor.fields[4] |= 1u << 16u;
|
||||
} else if (std::strcmp(kind, "array-mip-view") == 0) {
|
||||
resource = BasicArrayStorageTextureResource();
|
||||
descriptor = BasicArrayStorageTextureDescriptor();
|
||||
descriptor.fields[3] |= (1u << 12u) | (1u << 16u);
|
||||
descriptor.fields[5] |= 1u << 4u;
|
||||
} else if (std::strcmp(kind, "reserved") == 0) {
|
||||
descriptor.fields[1] |= 1u << 29u;
|
||||
} else if (std::strcmp(kind, "uint-format") == 0) {
|
||||
@@ -16139,6 +16250,16 @@ void CheckBasicStorageTextureDescriptor() {
|
||||
array.DstSelXYZW() == DstSel(6, 5, 4, 7),
|
||||
"PPSA21268 2D-array storage descriptor fixture is malformed");
|
||||
ValidateStorageTexture(BasicArrayStorageTextureResource(), array, 0x10000);
|
||||
const ShaderTextureResource mip_array {{0x20268d00u, 0xc4700000u, 0x001fc01fu,
|
||||
0xd1b11facu, 0x00000000u, 0x00700070u,
|
||||
0x00000000u, 0x00000000u}};
|
||||
Require("BasicStorageTexture", "PPSA14457 mip-one 2D-array descriptor",
|
||||
mip_array.BaseLevel() == 1 && mip_array.LastLevel() == 1 &&
|
||||
mip_array.MaxMip() == 7 &&
|
||||
mip_array.Type() == Prospero::GpuEnumValue(Prospero::ImageType::kColor2DArray) &&
|
||||
mip_array.Depth() == 0 && mip_array.BaseArray5() == 0,
|
||||
"PPSA14457 mip-one 2D-array storage descriptor fixture is malformed");
|
||||
ValidateStorageTexture(BasicArrayStorageTextureResource(), mip_array, 0x30000);
|
||||
|
||||
const auto uint_array = BasicUintArrayStorageTextureDescriptor();
|
||||
Require("BasicStorageTexture", "uint 2D-array descriptor",
|
||||
@@ -16299,7 +16420,6 @@ void CheckBasicStorageTextureDescriptor() {
|
||||
"yzwx-read",
|
||||
"reserved-swizzle",
|
||||
"array-base-out-of-range",
|
||||
"array-mip-view",
|
||||
"reserved",
|
||||
"uint-format",
|
||||
"uint-resource-float-format",
|
||||
@@ -17468,6 +17588,7 @@ int main(int argc, char** argv) {
|
||||
CheckPm4CeCompletion(vulkan.RuntimeRenderer());
|
||||
CheckEmbeddedFetchVertexOffset();
|
||||
CheckEmbeddedFetchLaneSpill();
|
||||
CheckRectListShaders();
|
||||
CheckPs5GameExampleImageClearRuntimeShape();
|
||||
vulkan.CheckSchedulerTimeline();
|
||||
vulkan.CheckGpuMappedRangeLifecycle();
|
||||
|
||||
Reference in New Issue
Block a user