Compare commits

...
Author SHA1 Message Date
nmzik 05d14f5421 system: add console language conf 2026-08-03 01:37:42 +02:00
Stefanos Costaandnmzik 1a164c3628 kernel: preserve guest pthread priorities
Extracted from Stefanos Costa's pthread fix in #147.
2026-08-03 01:10:38 +02:00
nmzik 86a586025f renderer: expand rectangle lists with tessellation
Remove the legacy NGG rectangle workaround and its configuration toggle.
2026-08-03 00:35:02 +02:00
nmzik 06cbd602c1 graphics: preserve indirect instance state 2026-08-03 00:35:02 +02:00
nmzik 055d0920d7 renderer: allow array storage views at nonzero mips 2026-08-03 00:35:02 +02:00
nmzik f2ee98fe31 renderer: separate tiled and linear texture capacities 2026-08-03 00:35:02 +02:00
nmzik 207cef9602 renderer: report invalid texture upload layout 2026-08-03 00:35:02 +02:00
27 changed files with 909 additions and 284 deletions
+4 -4
View File
@@ -37,6 +37,10 @@ uint32_t GetVblankFrequency() {
return std::clamp(g_config->vblank_frequency, 30u, 360u); return std::clamp(g_config->vblank_frequency, 30u, 360u);
} }
uint32_t GetConsoleLanguage() {
return g_config->console_language;
}
bool VulkanValidationEnabled() { bool VulkanValidationEnabled() {
return g_config->vulkan_validation_enabled; return g_config->vulkan_validation_enabled;
} }
@@ -89,10 +93,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;
} }
+5 -2
View File
@@ -18,10 +18,14 @@ enum class ProfilerDirection { None, Network };
enum class OutputDirection { Silent, Console, File }; enum class OutputDirection { Silent, Console, File };
constexpr uint32_t DEFAULT_CONSOLE_LANGUAGE = 1;
constexpr uint32_t MAX_CONSOLE_LANGUAGE = 29;
struct ConfigOptions { struct ConfigOptions {
uint32_t screen_width = 1280; uint32_t screen_width = 1280;
uint32_t screen_height = 720; uint32_t screen_height = 720;
uint32_t vblank_frequency = 60; uint32_t vblank_frequency = 60;
uint32_t console_language = DEFAULT_CONSOLE_LANGUAGE;
bool vulkan_validation_enabled = false; bool vulkan_validation_enabled = false;
bool shader_validation_enabled = false; bool shader_validation_enabled = false;
ShaderOptimizationType shader_optimization_type = ShaderOptimizationType::None; ShaderOptimizationType shader_optimization_type = ShaderOptimizationType::None;
@@ -35,7 +39,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;
}; };
@@ -44,6 +47,7 @@ void Load(const ConfigOptions& cfg);
uint32_t GetScreenWidth(); uint32_t GetScreenWidth();
uint32_t GetScreenHeight(); uint32_t GetScreenHeight();
uint32_t GetVblankFrequency(); uint32_t GetVblankFrequency();
uint32_t GetConsoleLanguage();
bool VulkanValidationEnabled(); bool VulkanValidationEnabled();
bool ShaderValidationEnabled(); bool ShaderValidationEnabled();
@@ -64,7 +68,6 @@ ProfilerDirection GetProfilerDirection();
bool SpirvDebugPrintfEnabled(); bool SpirvDebugPrintfEnabled();
bool RenderDocEnabled(); bool RenderDocEnabled();
bool NggRectlistDrawEnabled();
bool ReadbackLinearImagesEnabled(); bool ReadbackLinearImagesEnabled();
} // namespace Config } // namespace Config
@@ -83,8 +83,7 @@ public:
uint32_t first_instance = 0); uint32_t first_instance = 0);
void DrawIndexOffset(uint32_t index_offset, uint32_t index_count, uint32_t flags); void DrawIndexOffset(uint32_t index_offset, uint32_t index_count, uint32_t flags);
void DrawIndexAuto(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 render_target_slice_offset = 0);
uint32_t first_vertex = 0, uint32_t first_instance = 0);
void DrawIndirect(uint32_t data_offset, uint32_t draw_initiator, bool indexed); void DrawIndirect(uint32_t data_offset, uint32_t draw_initiator, bool indexed);
void DrawIndirectMulti(uint32_t data_offset, uint32_t max_count_or_count, void DrawIndirectMulti(uint32_t data_offset, uint32_t max_count_or_count,
const volatile uint32_t* count_addr, uint32_t stride_in_bytes, const volatile uint32_t* count_addr, uint32_t stride_in_bytes,
@@ -152,6 +151,9 @@ private:
uint32_t interrupt_context_id); uint32_t interrupt_context_id);
void ProcessPm4(Pm4Execution& execution, size_t stop_depth); void ProcessPm4(Pm4Execution& execution, size_t stop_depth);
void SuspendPm4(); 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(); } CommandScheduler& GetScheduler() const { return m_renderer.GetCommandScheduler(); }
RenderCommandBuffer& CurrentBuffer() { return GetScheduler().Current(); } RenderCommandBuffer& CurrentBuffer() { return GetScheduler().Current(); }
@@ -168,7 +170,8 @@ private:
uint64_t m_index_base_addr = 0; uint64_t m_index_base_addr = 0;
uint64_t m_draw_indirect_args_base_addr = 0; uint64_t m_draw_indirect_args_base_addr = 0;
uint64_t m_dispatch_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_de_count = 0;
uint32_t m_ce_count = 0; uint32_t m_ce_count = 0;
+17 -8
View File
@@ -983,8 +983,9 @@ void CommandProcessor::DrawIndirect(uint32_t data_offset, uint32_t draw_initiato
args.start_vertex_location, args.start_instance_location); args.start_vertex_location, args.start_instance_location);
} }
} }
DrawIndexAuto(args.vertex_count_per_instance, 0, 0, args.instance_count, m_num_instances = args.instance_count;
args.start_vertex_location, args.start_instance_location); SubmitNonIndexedDraw(args.vertex_count_per_instance, 0, 0, args.start_vertex_location,
args.start_instance_location);
return; 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, DrawIndex(index_count, index_addr, 0, 1, args.instance_count, nullptr, 0,
static_cast<int32_t>(args.base_vertex_location), args.start_instance_location); 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); args->start_vertex_location, args->start_instance_location);
} }
} }
DrawIndexAuto(args->vertex_count_per_instance, 0, 0, args->instance_count, m_num_instances = args->instance_count;
args->start_vertex_location, args->start_instance_location); SubmitNonIndexedDraw(args->vertex_count_per_instance, 0, 0, args->start_vertex_location,
args->start_instance_location);
continue; 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, DrawIndex(index_count, index_addr, 0, 1, args->instance_count, nullptr, 0,
static_cast<int32_t>(args->base_vertex_location), args->start_instance_location); 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, void CommandProcessor::DrawIndexAuto(uint32_t index_count, uint32_t flags,
uint32_t render_target_slice_offset, uint32_t instance_count, uint32_t render_target_slice_offset) {
uint32_t first_vertex, uint32_t first_instance) { 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(); CheckBuffer();
m_renderer.GetRenderExecutor().DrawAuto(m_submit_id, CurrentBuffer(), index_count, flags, m_renderer.GetRenderExecutor().DrawAuto(m_submit_id, CurrentBuffer(), vertex_count, flags,
render_target_slice_offset, instance_count, render_target_slice_offset, m_num_instances,
first_vertex, first_instance); first_vertex, first_instance);
} }
+23 -4
View File
@@ -838,11 +838,20 @@ struct TextureCache::ColorTransferPlan {
TextureUploadLayout layout; TextureUploadLayout layout;
std::vector<vk::BufferImageCopy> regions; std::vector<vk::BufferImageCopy> regions;
std::vector<GpuTileInfo> tiles; std::vector<GpuTileInfo> tiles;
uint64_t linear_size = 0;
bool tiled = false; bool tiled = false;
bool swap_bgra16 = false; bool swap_bgra16 = false;
bool valid = 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 { struct TextureCache::DownloadPlan {
ColorTransferPlan color; ColorTransferPlan color;
bool depth = false; bool depth = false;
@@ -919,6 +928,7 @@ TextureCache::BuildColorTransfer(const Image& image, BindingType binding,
info.resources.levels, plan.tiles)) { info.resources.levels, plan.tiles)) {
return plan; return plan;
} }
plan.linear_size = GetLinearSize(plan.tiles);
} }
plan.valid = true; plan.valid = true;
return plan; return plan;
@@ -956,11 +966,19 @@ void TextureCache::UploadImage(Image& image, const ImageDesc& desc, Buffer& sour
if (desc.type != BindingType::DepthTarget) { if (desc.type != BindingType::DepthTarget) {
auto plan = BuildColorTransfer(image, desc.type, TransferDirection::Upload); 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}; TileManager::Result linear {source.Handle(), source_offset, info.data.size};
if (plan.tiled) { if (plan.tiled) {
linear = m_tiler->Detile(source.Handle(), source_offset, info.data.size, info.data.size, linear = m_tiler->Detile(source.Handle(), source_offset, info.data.size,
plan.tiles); plan.linear_size, plan.tiles);
} }
if (plan.swap_bgra16) { if (plan.swap_bgra16) {
linear = m_tiler->SwapBgra16(linear); 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, 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) { 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)) { image.info.TransferLayers(), levels, color.tiles)) {
return false; return false;
} }
color.linear_size = GetLinearSize(color.tiles);
} }
} }
m_texture_cache.DownloadImageData(image, buffer, buf_offset, copy_size, std::move(plan)); 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; 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, const TextureUploadLayout& layout, uint32_t fmt, uint32_t depth,
uint64_t levels, std::vector<GpuTileInfo>& out_infos) { 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) || regions.size() != GetTextureRegionCount(depth, levels, layout.volume_texture) ||
Prospero::IsFmaskTextureFormat(fmt)) { Prospero::IsFmaskTextureFormat(fmt)) {
return false; return false;
@@ -538,8 +538,9 @@ bool TextureBuildGpuTileInfos(uint64_t size, const std::vector<vk::BufferImageCo
const uint64_t linear_span = const uint64_t linear_span =
static_cast<uint64_t>(copy_depth - 1u) * linear_stride + static_cast<uint64_t>(copy_depth - 1u) * linear_stride +
layout.level_sizes[level].size; layout.level_sizes[level].size;
if (!SetGpuTileSize(info.linear_offset, linear_span, size, info.linear_size) || if (!SetGpuTileSize(info.linear_offset, linear_span, UINT64_MAX,
!SetGpuTileSize(info.tiled_offset, volume.level_sizes[level], size, info.linear_size) ||
!SetGpuTileSize(info.tiled_offset, volume.level_sizes[level], tiled_size,
info.tiled_size)) { info.tiled_size)) {
return false; 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.bytes_per_element = block.bytes_per_element;
info.linear_offset = region.bufferOffset; info.linear_offset = region.bufferOffset;
info.tiled_offset = TextureUploadSliceSourceOffset(layout, level, z); info.tiled_offset = TextureUploadSliceSourceOffset(layout, level, z);
if (!SetGpuTileSize(info.linear_offset, level_size.size, size, info.linear_size) || if (!SetGpuTileSize(info.linear_offset, level_size.size, UINT64_MAX,
!SetGpuTileSize(info.tiled_offset, GetLevelSrcSize(level_size), size, info.linear_size) ||
!SetGpuTileSize(info.tiled_offset, GetLevelSrcSize(level_size), tiled_size,
info.tiled_size)) { info.tiled_size)) {
return false; return false;
} }
@@ -44,7 +44,7 @@ std::vector<vk::BufferImageCopy> TextureBuildImageCopies(const TextureUploadLayo
uint32_t width, uint32_t height, uint32_t width, uint32_t height,
uint32_t depth, uint64_t levels, uint32_t depth, uint64_t levels,
bool array_texture, bool volume_texture); 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, const TextureUploadLayout& layout, uint32_t fmt, uint32_t depth,
uint64_t levels, std::vector<GpuTileInfo>& infos); uint64_t levels, std::vector<GpuTileInfo>& infos);
@@ -391,9 +391,8 @@ static bool IsSupportedStorageTextureDescriptor(const ShaderRecompiler::IR::Imag
const bool supported_swizzle = const bool supported_swizzle =
IsValidImageSwizzle(swizzle) && IsValidImageSwizzle(swizzle) &&
(swizzle == DstSel(4, 5, 6, 7) || !resource.read || resource.atomic); (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 && 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 && descriptor.LastLevel() <= descriptor.MaxMip() && descriptor.MinLod() == 0 &&
supported_swizzle && descriptor.BCSwizzle() == 0 && !descriptor.MsaaDepth(); supported_swizzle && descriptor.BCSwizzle() == 0 && !descriptor.MsaaDepth();
} }
@@ -618,14 +617,15 @@ RenderExecutor::ResolveTexture(const ShaderRecompiler::IR::ImageResource& reso
const bool multisampled = IsMultisampledTexture(type); const bool multisampled = IsMultisampledTexture(type);
const auto levels = multisampled ? 1u : static_cast<uint32_t>(descriptor.MaxMip()) + 1u; const auto levels = multisampled ? 1u : static_cast<uint32_t>(descriptor.MaxMip()) + 1u;
const auto tile = descriptor.TileMode(); 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 = const bool msaa_tile =
depth_tile || tile == Prospero::GpuEnumValue(Prospero::TileMode::kRenderTarget); depth_tile || tile == Prospero::GpuEnumValue(Prospero::TileMode::kRenderTarget);
const bool msaa_array = type == Prospero::ImageType::kColor2DMsaaArray; const bool msaa_array = type == Prospero::ImageType::kColor2DMsaaArray;
if ((!multisampled && (base_level > last_level || last_level >= levels)) || if ((!multisampled && (base_level > last_level || last_level >= levels)) ||
(multisampled && (multisampled &&
(base_level != 0 || last_level == 0 || last_level > 3 || (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))))) { (!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 " 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 " "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.kind), static_cast<uint32_t>(resource.dimension),
static_cast<uint32_t>(resource.mip_mode), resource.read, resource.written, 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[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 samples = multisampled ? 1u << last_level : 1u;
const auto view_levels = const auto view_levels =
@@ -661,8 +662,8 @@ RenderExecutor::ResolveTexture(const ShaderRecompiler::IR::ImageResource& reso
TileSizeAlign size {}; TileSizeAlign size {};
if (multisampled) { if (multisampled) {
const auto bytes = Prospero::NumBytesPerElement(format); const auto bytes = Prospero::NumBytesPerElement(format);
pitch = depth_tile ? TileGetDepthPitch(width, bytes, last_level) pitch = depth_tile ? TileGetDepthPitch(width, bytes, last_level)
: TileGetRenderTargetPitch(width, bytes, last_level); : TileGetRenderTargetPitch(width, bytes, last_level);
if (pitch == 0 || !TileGetRenderTargetSize(width, height, pitch, bytes, size, last_level) || if (pitch == 0 || !TileGetRenderTargetSize(width, height, pitch, bytes, size, last_level) ||
size.size > UINT32_MAX / image_layers) { size.size > UINT32_MAX / image_layers) {
EXIT("unsupported multisample texture layout\n"); EXIT("unsupported multisample texture layout\n");
@@ -679,8 +680,8 @@ RenderExecutor::ResolveTexture(const ShaderRecompiler::IR::ImageResource& reso
ValidateStorageTexture(resource, descriptor, size.size); ValidateStorageTexture(resource, descriptor, size.size);
} }
const auto pixel_format = TextureGetFormat(format); const auto pixel_format = TextureGetFormat(format);
const auto storage_view_format = const auto storage_view_format =
storage && format == Prospero::GpuEnumValue(Prospero::BufferFormat::k32SInt) storage && format == Prospero::GpuEnumValue(Prospero::BufferFormat::k32SInt)
? vk::Format::eR32Uint ? vk::Format::eR32Uint
: SrgbStorageViewFormat(pixel_format); : SrgbStorageViewFormat(pixel_format);
@@ -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,8 +464,12 @@ 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);
vk::ShaderModule vert_shader_module = nullptr; const bool rect_list = static_params.topology == vk::PrimitiveTopology::ePatchList;
vk::ShaderModule frag_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::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,10 +978,13 @@ 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 {};
pipeline_info.pViewportState = &viewport_state; tessellation_state.sType = vk::StructureType::ePipelineTessellationStateCreateInfo;
pipeline_info.pRasterizationState = &rasterizer; tessellation_state.patchControlPoints = 3;
pipeline_info.pMultisampleState = &multisampling; 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.pDepthStencilState = (static_params.with_depth ? &depth_stencil_info : nullptr);
pipeline_info.pColorBlendState = &color_blending; pipeline_info.pColorBlendState = &color_blending;
pipeline_info.pDynamicState = &dynamic_state; pipeline_info.pDynamicState = &dynamic_state;
@@ -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);
} }
+17 -61
View File
@@ -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"
@@ -650,11 +649,9 @@ static bool ConsumeMetadataColorOperation(const RenderCommandBuffer& buffer) {
} }
struct DrawEmitInfo { struct DrawEmitInfo {
bool indexed = false; bool indexed = false;
bool draw_prim7_as_ngg = false; int32_t vertex_offset = 0;
uint32_t draw_vertex_count = 0; uint32_t first_vertex = 0;
int32_t vertex_offset = 0;
uint32_t first_vertex = 0;
}; };
struct DrawIndexBufferSource { struct DrawIndexBufferSource {
@@ -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;
@@ -1159,7 +1131,7 @@ void RenderExecutor::DrawIndex(uint64_t submit_id, RenderCommandBuffer& buffer,
reinterpret_cast<uint64_t>(index_addr)); reinterpret_cast<uint64_t>(index_addr));
Common::LockGuard lock(m_context.GetMutex()); Common::LockGuard lock(m_context.GetMutex());
if (index_count == 0) { if (index_count == 0 || instance_count == 0) {
return; return;
} }
@@ -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;
} }
@@ -1229,10 +1201,6 @@ void RenderExecutor::DrawIndex(uint64_t submit_id, RenderCommandBuffer& buffer,
EXIT_NOT_IMPLEMENTED(flags != 0); EXIT_NOT_IMPLEMENTED(flags != 0);
EXIT_NOT_IMPLEMENTED(type != 1); EXIT_NOT_IMPLEMENTED(type != 1);
if (instance_count == 0) {
instance_count = 1;
}
const DrawCallInfo draw {"DrawIndex", CommandBufferDebugOp::DrawIndex, const DrawCallInfo draw {"DrawIndex", CommandBufferDebugOp::DrawIndex,
index_count, flags, index_count, flags,
instance_count, first_instance}; 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); index_count, flags, first_vertex, instance_count, first_instance);
Common::LockGuard lock(m_context.GetMutex()); Common::LockGuard lock(m_context.GetMutex());
if (index_count == 0) { if (index_count == 0 || instance_count == 0) {
return; return;
} }
@@ -1330,10 +1298,6 @@ void RenderExecutor::DrawAuto(uint64_t submit_id, RenderCommandBuffer& buffer, u
hw_check(buffer); hw_check(buffer);
EXIT_NOT_IMPLEMENTED(flags != 0); EXIT_NOT_IMPLEMENTED(flags != 0);
if (instance_count == 0) {
instance_count = 1;
}
const DrawCallInfo draw {"DrawIndexAuto", CommandBufferDebugOp::DrawIndexAuto, const DrawCallInfo draw {"DrawIndexAuto", CommandBufferDebugOp::DrawIndexAuto,
index_count, flags, index_count, flags,
instance_count, first_instance}; instance_count, first_instance};
@@ -1345,20 +1309,15 @@ void RenderExecutor::DrawAuto(uint64_t submit_id, RenderCommandBuffer& buffer, u
return; return;
} }
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: "
@@ -1375,13 +1334,10 @@ 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.first_vertex = static_cast<uint32_t>(vertex_offset);
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);
DrawIndexBufferSource index_source {}; DrawIndexBufferSource index_source {};
ExecutePreparedDraw(submit_id, buffer, draw, state, topology, emit, index_source, false, false, 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); 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;
+397
View File
@@ -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
+22
View File
@@ -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_
+37
View File
@@ -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));
+5
View File
@@ -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;
+36 -47
View File
@@ -365,6 +365,7 @@ struct PthreadAttrPrivate {
uint64_t stack_map_addr; uint64_t stack_map_addr;
size_t stack_map_size; size_t stack_map_size;
int policy; int policy;
int guest_priority;
int inherit_sched; int inherit_sched;
int solosched; int solosched;
bool detached; bool detached;
@@ -2128,13 +2129,8 @@ int KYTY_SYSV_ABI PthreadAttrGetschedparam(const PthreadAttr* attr, KernelSchedP
int result = pthread_attr_getschedparam(&(*attr)->p, param); int result = pthread_attr_getschedparam(&(*attr)->p, param);
if (param->sched_priority <= -2) { // Host priority mapping is lossy; return the exact guest value.
param->sched_priority = 767; param->sched_priority = (*attr)->guest_priority;
} else if (param->sched_priority >= +2) {
param->sched_priority = 256;
} else {
param->sched_priority = 700;
}
if (result == 0) { if (result == 0) {
return OK; return OK;
@@ -2298,6 +2294,7 @@ int KYTY_SYSV_ABI PthreadAttrSetschedparam(PthreadAttr* attr, const KernelSchedP
return KERNEL_ERROR_EINVAL; return KERNEL_ERROR_EINVAL;
} }
#if KYTY_PLATFORM == KYTY_PLATFORM_WINDOWS
KernelSchedParam pparam {}; KernelSchedParam pparam {};
if (param->sched_priority <= 478) { if (param->sched_priority <= 478) {
pparam.sched_priority = +2; pparam.sched_priority = +2;
@@ -2307,12 +2304,13 @@ int KYTY_SYSV_ABI PthreadAttrSetschedparam(PthreadAttr* attr, const KernelSchedP
pparam.sched_priority = 0; pparam.sched_priority = 0;
} }
int result = pthread_attr_setschedparam(&attr_value->p, &pparam); if (pthread_attr_setschedparam(&attr_value->p, &pparam) != 0) {
return KERNEL_ERROR_EINVAL;
if (result == 0) {
return OK;
} }
return KERNEL_ERROR_EINVAL; #endif
attr_value->guest_priority = param->sched_priority;
return OK;
} }
int KYTY_SYSV_ABI PthreadAttrSetschedpolicy(PthreadAttr* attr, int policy) { int KYTY_SYSV_ABI PthreadAttrSetschedpolicy(PthreadAttr* attr, int policy) {
@@ -3639,26 +3637,15 @@ int KYTY_SYSV_ABI PthreadGetprio(Pthread thread, int* prio) {
EXIT_NOT_IMPLEMENTED(prio == nullptr); EXIT_NOT_IMPLEMENTED(prio == nullptr);
sched_param param {}; sched_param native_param {};
int pol = 0; int native_policy = 0;
if (pthread_getschedparam(thread->p, &native_policy, &native_param) != 0) {
int result = pthread_getschedparam(thread->p, &pol, &param); return KERNEL_ERROR_EINVAL;
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;
} }
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) { int KYTY_SYSV_ABI PthreadSetprio(Pthread thread, int prio) {
@@ -3673,25 +3660,27 @@ int KYTY_SYSV_ABI PthreadSetprio(Pthread thread, int prio) {
int result = pthread_getschedparam(thread->p, &pol, &param); int result = pthread_getschedparam(thread->p, &pol, &param);
if (result == 0) { if (result != 0) {
if (prio <= 478) { return KERNEL_ERROR_EINVAL;
param.sched_priority = +2;
} else if (prio >= 733) {
param.sched_priority = -2;
} else {
param.sched_priority = 0;
}
result = pthread_setschedparam(thread->p, pol, &param);
if (result == 0) {
LOGF("\t PthreadSetprio: %d, %d\n", thread->unique_id, prio);
return OK;
}
} }
return KERNEL_ERROR_EINVAL; #if KYTY_PLATFORM == KYTY_PLATFORM_WINDOWS
if (prio <= 478) {
param.sched_priority = +2;
} else if (prio >= 733) {
param.sched_priority = -2;
} else {
param.sched_priority = 0;
}
if (pthread_setschedparam(thread->p, pol, &param) != 0) {
return KERNEL_ERROR_EINVAL;
}
#endif
thread->attr->guest_priority = prio;
LOGF("\t PthreadSetprio: %d, %d\n", thread->unique_id, prio);
return OK;
} }
void KYTY_SYSV_ABI PthreadTestcancel() { void KYTY_SYSV_ABI PthreadTestcancel() {
+28 -27
View File
@@ -7,7 +7,7 @@
<x>0</x> <x>0</x>
<y>0</y> <y>0</y>
<width>560</width> <width>560</width>
<height>420</height> <height>450</height>
</rect> </rect>
</property> </property>
<property name="windowTitle"> <property name="windowTitle">
@@ -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>
@@ -132,41 +119,55 @@
</widget> </widget>
</item> </item>
<item row="3" column="0"> <item row="3" column="0">
<widget class="QLabel" name="label_console_language">
<property name="text">
<string>Console language:</string>
</property>
</widget>
</item>
<item row="3" column="1">
<widget class="QComboBox" name="comboBox_console_language">
<property name="toolTip">
<string>Language</string>
</property>
</widget>
</item>
<item row="4" column="0">
<widget class="QLabel" name="label_20"> <widget class="QLabel" name="label_20">
<property name="text"> <property name="text">
<string>Shader optimization type:</string> <string>Shader optimization type:</string>
</property> </property>
</widget> </widget>
</item> </item>
<item row="3" column="1"> <item row="4" column="1">
<widget class="QComboBox" name="comboBox_shader_optimization_type"> <widget class="QComboBox" name="comboBox_shader_optimization_type">
<property name="toolTip"> <property name="toolTip">
<string>Optimize shaders for code size or performance</string> <string>Optimize shaders for code size or performance</string>
</property> </property>
</widget> </widget>
</item> </item>
<item row="4" column="0"> <item row="5" column="0">
<widget class="QLabel" name="label_21"> <widget class="QLabel" name="label_21">
<property name="text"> <property name="text">
<string>Shader log direction:</string> <string>Shader log direction:</string>
</property> </property>
</widget> </widget>
</item> </item>
<item row="4" column="1"> <item row="5" column="1">
<widget class="QComboBox" name="comboBox_shader_log_direction"> <widget class="QComboBox" name="comboBox_shader_log_direction">
<property name="toolTip"> <property name="toolTip">
<string>Dump shaders to file or console window. If enabled may decrease emulator performance</string> <string>Dump shaders to file or console window. If enabled may decrease emulator performance</string>
</property> </property>
</widget> </widget>
</item> </item>
<item row="5" column="0"> <item row="6" column="0">
<widget class="QLabel" name="label_22"> <widget class="QLabel" name="label_22">
<property name="text"> <property name="text">
<string>Shader log folder:</string> <string>Shader log folder:</string>
</property> </property>
</widget> </widget>
</item> </item>
<item row="5" column="1"> <item row="6" column="1">
<widget class="MandatoryLineEdit" name="lineEdit_shader_log_folder"> <widget class="MandatoryLineEdit" name="lineEdit_shader_log_folder">
<property name="toolTip"> <property name="toolTip">
<string>Specify directory to dump shaders</string> <string>Specify directory to dump shaders</string>
@@ -176,14 +177,14 @@
</property> </property>
</widget> </widget>
</item> </item>
<item row="6" column="0"> <item row="7" column="0">
<widget class="QLabel" name="label_24"> <widget class="QLabel" name="label_24">
<property name="text"> <property name="text">
<string>Command buffer dump folder:</string> <string>Command buffer dump folder:</string>
</property> </property>
</widget> </widget>
</item> </item>
<item row="6" column="1"> <item row="7" column="1">
<widget class="MandatoryLineEdit" name="lineEdit_cmd_dump_folder"> <widget class="MandatoryLineEdit" name="lineEdit_cmd_dump_folder">
<property name="toolTip"> <property name="toolTip">
<string>Specify directory to dump command buffers</string> <string>Specify directory to dump command buffers</string>
@@ -193,28 +194,28 @@
</property> </property>
</widget> </widget>
</item> </item>
<item row="7" column="0"> <item row="8" column="0">
<widget class="QLabel" name="label_25"> <widget class="QLabel" name="label_25">
<property name="text"> <property name="text">
<string>Printf direction:</string> <string>Printf direction:</string>
</property> </property>
</widget> </widget>
</item> </item>
<item row="7" column="1"> <item row="8" column="1">
<widget class="QComboBox" name="comboBox_printf_direction"> <widget class="QComboBox" name="comboBox_printf_direction">
<property name="toolTip"> <property name="toolTip">
<string>Print logs to file or console window. If enabled may decrease emulator performance</string> <string>Print logs to file or console window. If enabled may decrease emulator performance</string>
</property> </property>
</widget> </widget>
</item> </item>
<item row="8" column="0"> <item row="9" column="0">
<widget class="QLabel" name="label_26"> <widget class="QLabel" name="label_26">
<property name="text"> <property name="text">
<string>Printf output file:</string> <string>Printf output file:</string>
</property> </property>
</widget> </widget>
</item> </item>
<item row="8" column="1"> <item row="9" column="1">
<widget class="MandatoryLineEdit" name="lineEdit_printf_file"> <widget class="MandatoryLineEdit" name="lineEdit_printf_file">
<property name="toolTip"> <property name="toolTip">
<string>Specify file to dump logs</string> <string>Specify file to dump logs</string>
@@ -224,14 +225,14 @@
</property> </property>
</widget> </widget>
</item> </item>
<item row="9" column="0"> <item row="10" column="0">
<widget class="QLabel" name="label_27"> <widget class="QLabel" name="label_27">
<property name="text"> <property name="text">
<string>Profiler direction:</string> <string>Profiler direction:</string>
</property> </property>
</widget> </widget>
</item> </item>
<item row="9" column="1"> <item row="10" column="1">
<widget class="QComboBox" name="comboBox_profiler_direction"> <widget class="QComboBox" name="comboBox_profiler_direction">
<property name="toolTip"> <property name="toolTip">
<string>Enable or disable profiler. If enabled may decrease emulator performance</string> <string>Enable or disable profiler. If enabled may decrease emulator performance</string>
+10 -5
View File
@@ -48,6 +48,9 @@ class Configuration: public QObject {
Q_OBJECT Q_OBJECT
public: public:
static constexpr int DEFAULT_CONSOLE_LANGUAGE = 1;
static constexpr int MAX_CONSOLE_LANGUAGE = 29;
enum class Resolution { enum class Resolution {
R1280X720, R1280X720,
R1920X1080, R1920X1080,
@@ -83,6 +86,7 @@ public:
Resolution screen_resolution = Resolution::R1280X720; Resolution screen_resolution = Resolution::R1280X720;
int vblank_frequency = 60; int vblank_frequency = 60;
int console_language = DEFAULT_CONSOLE_LANGUAGE;
bool vulkan_validation_enabled = true; bool vulkan_validation_enabled = true;
bool shader_validation_enabled = true; bool shader_validation_enabled = true;
ShaderOptimizationType shader_optimization_type = ShaderOptimizationType::Performance; ShaderOptimizationType shader_optimization_type = ShaderOptimizationType::Performance;
@@ -94,13 +98,13 @@ 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");
void CopyEmulatorSettingsFrom(const Configuration& other) { void CopyEmulatorSettingsFrom(const Configuration& other) {
screen_resolution = other.screen_resolution; screen_resolution = other.screen_resolution;
vblank_frequency = other.vblank_frequency; vblank_frequency = other.vblank_frequency;
console_language = other.console_language;
vulkan_validation_enabled = other.vulkan_validation_enabled; vulkan_validation_enabled = other.vulkan_validation_enabled;
shader_validation_enabled = other.shader_validation_enabled; shader_validation_enabled = other.shader_validation_enabled;
shader_optimization_type = other.shader_optimization_type; shader_optimization_type = other.shader_optimization_type;
@@ -112,7 +116,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) {
@@ -136,6 +139,7 @@ public:
KYTY_CFG_SET(custom_settings); KYTY_CFG_SET(custom_settings);
KYTY_CFG_SET(screen_resolution); KYTY_CFG_SET(screen_resolution);
KYTY_CFG_SET(vblank_frequency); KYTY_CFG_SET(vblank_frequency);
KYTY_CFG_SET(console_language);
KYTY_CFG_SET(vulkan_validation_enabled); KYTY_CFG_SET(vulkan_validation_enabled);
KYTY_CFG_SET(shader_validation_enabled); KYTY_CFG_SET(shader_validation_enabled);
KYTY_CFG_SET(shader_optimization_type); KYTY_CFG_SET(shader_optimization_type);
@@ -147,7 +151,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);
} }
@@ -158,6 +161,10 @@ public:
KYTY_CFG_GET(custom_settings); KYTY_CFG_GET(custom_settings);
KYTY_CFG_GET(screen_resolution); KYTY_CFG_GET(screen_resolution);
vblank_frequency = s->value("vblank_frequency", vblank_frequency).toInt(); vblank_frequency = s->value("vblank_frequency", vblank_frequency).toInt();
console_language = s->value("console_language", console_language).toInt();
if (console_language < 0 || console_language > MAX_CONSOLE_LANGUAGE) {
console_language = DEFAULT_CONSOLE_LANGUAGE;
}
KYTY_CFG_GET(vulkan_validation_enabled); KYTY_CFG_GET(vulkan_validation_enabled);
KYTY_CFG_GET(shader_validation_enabled); KYTY_CFG_GET(shader_validation_enabled);
KYTY_CFG_GET(shader_optimization_type); KYTY_CFG_GET(shader_optimization_type);
@@ -169,8 +176,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();
} }
}; };
+40 -2
View File
@@ -30,6 +30,39 @@ constexpr char SETTINGS_CFG_DIALOG[] = "ConfigurationEditDialog";
constexpr char SETTINGS_CFG_LAST_GEOMETRY[] = "geometry"; constexpr char SETTINGS_CFG_LAST_GEOMETRY[] = "geometry";
constexpr int GLOBAL_SETTINGS_GAME_DIRS_MIN_WIDTH = 560; constexpr int GLOBAL_SETTINGS_GAME_DIRS_MIN_WIDTH = 560;
const QStringList CONSOLE_LANGUAGE_NAMES = {
"Japanese",
"English (United States)",
"French (France)",
"Spanish (Spain)",
"German",
"Italian",
"Dutch",
"Portuguese (Portugal)",
"Russian",
"Korean",
"Chinese (Traditional)",
"Chinese (Simplified)",
"Finnish",
"Swedish",
"Danish",
"Norwegian",
"Polish",
"Portuguese (Brazil)",
"English (United Kingdom)",
"Turkish",
"Spanish (Latin America)",
"Arabic",
"French (Canada)",
"Czech",
"Hungarian",
"Greek",
"Romanian",
"Thai",
"Vietnamese",
"Indonesian",
};
static QString NormalizeGameDirectory(const QString& dir) { static QString NormalizeGameDirectory(const QString& dir) {
const auto trimmed = dir.trimmed(); const auto trimmed = dir.trimmed();
if (trimmed.isEmpty()) { if (trimmed.isEmpty()) {
@@ -121,10 +154,15 @@ static void ListInit(QComboBox* combo, T value) {
void ConfigurationEditDialog::Init(const Configuration& info) { void ConfigurationEditDialog::Init(const Configuration& info) {
ListInit(m_ui->comboBox_screen_resolution, info.screen_resolution); ListInit(m_ui->comboBox_screen_resolution, info.screen_resolution);
m_ui->spinBox_vblank_frequency->setValue(info.vblank_frequency); m_ui->spinBox_vblank_frequency->setValue(info.vblank_frequency);
m_ui->comboBox_console_language->clear();
m_ui->comboBox_console_language->addItems(CONSOLE_LANGUAGE_NAMES);
m_ui->comboBox_console_language->setCurrentIndex(
info.console_language >= 0 && info.console_language < CONSOLE_LANGUAGE_NAMES.size()
? info.console_language
: Configuration::DEFAULT_CONSOLE_LANGUAGE);
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);
@@ -242,10 +280,10 @@ static void UpdateInfo(Configuration& info, Ui::ConfigurationEditDialog& ui) {
info.screen_resolution = info.screen_resolution =
TextToEnum<Configuration::Resolution>(ui.comboBox_screen_resolution->currentText()); TextToEnum<Configuration::Resolution>(ui.comboBox_screen_resolution->currentText());
info.vblank_frequency = ui.spinBox_vblank_frequency->value(); info.vblank_frequency = ui.spinBox_vblank_frequency->value();
info.console_language = ui.comboBox_console_language->currentIndex();
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>(
+1 -1
View File
@@ -205,6 +205,7 @@ static QStringList CreateEmulatorArgs(const Configuration& info) {
args << "--screen-width" << r.at(0); args << "--screen-width" << r.at(0);
args << "--screen-height" << r.at(1); args << "--screen-height" << r.at(1);
args << "--vblank-frequency" << QString::number(info.vblank_frequency); args << "--vblank-frequency" << QString::number(info.vblank_frequency);
args << "--console-language" << QString::number(info.console_language);
args << "--vulkan-validation" << BoolArg(info.vulkan_validation_enabled); args << "--vulkan-validation" << BoolArg(info.vulkan_validation_enabled);
args << "--shader-validation" << BoolArg(info.shader_validation_enabled); args << "--shader-validation" << BoolArg(info.shader_validation_enabled);
args << "--shader-optimization-type" << EnumToText(info.shader_optimization_type); args << "--shader-optimization-type" << EnumToText(info.shader_optimization_type);
@@ -216,7 +217,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";
} }
+2 -32
View File
@@ -1,6 +1,7 @@
#include "common/abi.h" #include "common/abi.h"
#include "common/assert.h" #include "common/assert.h"
#include "common/common.h" #include "common/common.h"
#include "common/emulatorConfig.h"
#include "common/logging/log.h" #include "common/logging/log.h"
#include "common/stringUtils.h" #include "common/stringUtils.h"
#include "libs/errno.h" #include "libs/errno.h"
@@ -25,37 +26,6 @@ namespace SystemService {
[[maybe_unused]] constexpr int PARAM_ID_SCREEN_READER = 208; [[maybe_unused]] constexpr int PARAM_ID_SCREEN_READER = 208;
[[maybe_unused]] constexpr int PARAM_ID_ENTER_BUTTON_ASSIGN = 1000; [[maybe_unused]] constexpr int PARAM_ID_ENTER_BUTTON_ASSIGN = 1000;
[[maybe_unused]] constexpr int PARAM_LANG_JAPANESE = 0;
[[maybe_unused]] constexpr int PARAM_LANG_ENGLISH_US = 1;
[[maybe_unused]] constexpr int PARAM_LANG_FRENCH = 2;
[[maybe_unused]] constexpr int PARAM_LANG_SPANISH = 3;
[[maybe_unused]] constexpr int PARAM_LANG_GERMAN = 4;
[[maybe_unused]] constexpr int PARAM_LANG_ITALIAN = 5;
[[maybe_unused]] constexpr int PARAM_LANG_DUTCH = 6;
[[maybe_unused]] constexpr int PARAM_LANG_PORTUGUESE_PT = 7;
[[maybe_unused]] constexpr int PARAM_LANG_RUSSIAN = 8;
[[maybe_unused]] constexpr int PARAM_LANG_KOREAN = 9;
[[maybe_unused]] constexpr int PARAM_LANG_CHINESE_T = 10;
[[maybe_unused]] constexpr int PARAM_LANG_CHINESE_S = 11;
[[maybe_unused]] constexpr int PARAM_LANG_FINNISH = 12;
[[maybe_unused]] constexpr int PARAM_LANG_SWEDISH = 13;
[[maybe_unused]] constexpr int PARAM_LANG_DANISH = 14;
[[maybe_unused]] constexpr int PARAM_LANG_NORWEGIAN = 15;
[[maybe_unused]] constexpr int PARAM_LANG_POLISH = 16;
[[maybe_unused]] constexpr int PARAM_LANG_PORTUGUESE_BR = 17;
[[maybe_unused]] constexpr int PARAM_LANG_ENGLISH_GB = 18;
[[maybe_unused]] constexpr int PARAM_LANG_TURKISH = 19;
[[maybe_unused]] constexpr int PARAM_LANG_SPANISH_LA = 20;
[[maybe_unused]] constexpr int PARAM_LANG_ARABIC = 21;
[[maybe_unused]] constexpr int PARAM_LANG_FRENCH_CA = 22;
[[maybe_unused]] constexpr int PARAM_LANG_CZECH = 23;
[[maybe_unused]] constexpr int PARAM_LANG_HUNGARIAN = 24;
[[maybe_unused]] constexpr int PARAM_LANG_GREEK = 25;
[[maybe_unused]] constexpr int PARAM_LANG_ROMANIAN = 26;
[[maybe_unused]] constexpr int PARAM_LANG_THAI = 27;
[[maybe_unused]] constexpr int PARAM_LANG_VIETNAMESE = 28;
[[maybe_unused]] constexpr int PARAM_LANG_INDONESIAN = 29;
[[maybe_unused]] constexpr int PARAM_DATE_FORMAT_YYYYMMDD = 0; [[maybe_unused]] constexpr int PARAM_DATE_FORMAT_YYYYMMDD = 0;
[[maybe_unused]] constexpr int PARAM_DATE_FORMAT_DDMMYYYY = 1; [[maybe_unused]] constexpr int PARAM_DATE_FORMAT_DDMMYYYY = 1;
[[maybe_unused]] constexpr int PARAM_DATE_FORMAT_MMDDYYYY = 2; [[maybe_unused]] constexpr int PARAM_DATE_FORMAT_MMDDYYYY = 2;
@@ -121,7 +91,7 @@ static int KYTY_SYSV_ABI SystemServiceParamGetInt(int param_id, int* value) {
int v = 0; int v = 0;
switch (param_id) { switch (param_id) {
case PARAM_ID_LANG: v = PARAM_LANG_ENGLISH_US; break; case PARAM_ID_LANG: v = static_cast<int>(Config::GetConsoleLanguage()); break;
case PARAM_ID_DATE_FORMAT: v = PARAM_DATE_FORMAT_DDMMYYYY; break; case PARAM_ID_DATE_FORMAT: v = PARAM_DATE_FORMAT_DDMMYYYY; break;
case PARAM_ID_TIME_FORMAT: v = PARAM_TIME_FORMAT_24HOUR; break; case PARAM_ID_TIME_FORMAT: v = PARAM_TIME_FORMAT_24HOUR; break;
case PARAM_ID_TIME_ZONE: v = +180; break; case PARAM_ID_TIME_ZONE: v = +180; break;
+18 -7
View File
@@ -10,6 +10,7 @@
#include "emulator.h" #include "emulator.h"
#include "kytyGitVersion.h" #include "kytyGitVersion.h"
#include <charconv>
#include <cstdio> #include <cstdio>
#include <fmt/format.h> #include <fmt/format.h>
@@ -47,6 +48,7 @@ static void PrintUsage() {
::printf(" --screen-width <num> Window width. Default: 1280.\n"); ::printf(" --screen-width <num> Window width. Default: 1280.\n");
::printf(" --screen-height <num> Window height. Default: 720.\n"); ::printf(" --screen-height <num> Window height. Default: 720.\n");
::printf(" --vblank-frequency <num> Virtual vblank frequency. Default: 60.\n"); ::printf(" --vblank-frequency <num> Virtual vblank frequency. Default: 60.\n");
::printf(" --console-language <0-29> Console language. Default: 1 (English US).\n");
::printf(" --vulkan-validation <true|false> Enable Vulkan validation.\n"); ::printf(" --vulkan-validation <true|false> Enable Vulkan validation.\n");
::printf(" --shader-validation <true|false> Enable shader validation.\n"); ::printf(" --shader-validation <true|false> Enable shader validation.\n");
::printf(" --shader-optimization-type <value> None, Size, or Performance.\n"); ::printf(" --shader-optimization-type <value> None, Size, or Performance.\n");
@@ -59,8 +61,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");
@@ -103,6 +103,17 @@ static bool ParseEnum(const std::string& value, E& out) {
return true; return true;
} }
static bool ParseConsoleLanguage(const std::string& value, uint32_t& out) {
uint32_t language = 0;
auto [end, error] = std::from_chars(value.data(), value.data() + value.size(), language);
if (error != std::errc {} || end != value.data() + value.size() ||
language > Config::MAX_CONSOLE_LANGUAGE) {
return false;
}
out = language;
return true;
}
static bool ParseArgs(int argc, char* argv[], RunOptions& options, bool& show_help) { static bool ParseArgs(int argc, char* argv[], RunOptions& options, bool& show_help) {
show_help = false; show_help = false;
@@ -169,6 +180,11 @@ static bool ParseArgs(int argc, char* argv[], RunOptions& options, bool& show_he
const int32_t vblank_frequency = Common::ToInt32(value); const int32_t vblank_frequency = Common::ToInt32(value);
options.config.vblank_frequency = options.config.vblank_frequency =
static_cast<uint32_t>(vblank_frequency < 0 ? 0 : vblank_frequency); static_cast<uint32_t>(vblank_frequency < 0 ? 0 : vblank_frequency);
} else if (arg == "--console-language") {
if (!ParseConsoleLanguage(value, options.config.console_language)) {
::printf("invalid console language: %s\n", value.c_str());
return false;
}
} else if (arg == "--vulkan-validation") { } else if (arg == "--vulkan-validation") {
if (!ParseBool(value, options.config.vulkan_validation_enabled)) { if (!ParseBool(value, options.config.vulkan_validation_enabled)) {
::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());
@@ -220,11 +236,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());
+136 -15
View File
@@ -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;
@@ -7279,26 +7365,30 @@ public:
} }
u32 case_index = 0; 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::span<const GpuTileInfo> infos) {
std::vector<uint8_t> tiled(size); uint64_t linear_size = 0;
std::vector<uint8_t> cpu(size, 0); for (const auto& info: infos) {
std::vector<uint8_t> gpu(size, 0xab); 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); fill(&tiled, ++case_index);
for (const auto& info: infos) { for (const auto& info: infos) {
convert_reference(false, &cpu, tiled, info); 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); compare((std::string(stage) + " detile bytes").c_str(), cpu, gpu);
std::vector<uint8_t> linear(size); std::vector<uint8_t> linear(linear_size);
std::vector<uint8_t> cpu_tiled(size, 0xab); std::vector<uint8_t> cpu_tiled(tiled_size, 0xab);
std::vector<uint8_t> gpu_tiled(size, 0xab); std::vector<uint8_t> gpu_tiled(tiled_size, 0xab);
fill(&linear, 0x280u + case_index); fill(&linear, 0x280u + case_index);
for (const auto& info: infos) { for (const auto& info: infos) {
convert_reference(true, &cpu_tiled, linear, info); 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); compare((std::string(stage) + " tile bytes").c_str(), cpu_tiled, gpu_tiled);
}; };
for (const auto family: families) { for (const auto family: families) {
@@ -7436,6 +7526,32 @@ public:
Require(name, "format coverage", format_cases != 0, Require(name, "format coverage", format_cases != 0,
"no CPU-supported standard formats were tested"); "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 format = Prospero::GpuEnumValue(Prospero::BufferFormat::k32Float);
constexpr u32 tile = Prospero::GpuEnumValue(Prospero::TileMode::kRenderTarget); constexpr u32 tile = Prospero::GpuEnumValue(Prospero::TileMode::kRenderTarget);
@@ -15959,11 +16075,6 @@ ShaderTextureResource AtomicStorageTextureDescriptor() {
resource = BasicArrayStorageTextureResource(); resource = BasicArrayStorageTextureResource();
descriptor = BasicArrayStorageTextureDescriptor(); descriptor = BasicArrayStorageTextureDescriptor();
descriptor.fields[4] |= 1u << 16u; 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) { } else if (std::strcmp(kind, "reserved") == 0) {
descriptor.fields[1] |= 1u << 29u; descriptor.fields[1] |= 1u << 29u;
} else if (std::strcmp(kind, "uint-format") == 0) { } else if (std::strcmp(kind, "uint-format") == 0) {
@@ -16139,6 +16250,16 @@ void CheckBasicStorageTextureDescriptor() {
array.DstSelXYZW() == DstSel(6, 5, 4, 7), array.DstSelXYZW() == DstSel(6, 5, 4, 7),
"PPSA21268 2D-array storage descriptor fixture is malformed"); "PPSA21268 2D-array storage descriptor fixture is malformed");
ValidateStorageTexture(BasicArrayStorageTextureResource(), array, 0x10000); 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(); const auto uint_array = BasicUintArrayStorageTextureDescriptor();
Require("BasicStorageTexture", "uint 2D-array descriptor", Require("BasicStorageTexture", "uint 2D-array descriptor",
@@ -16299,7 +16420,6 @@ void CheckBasicStorageTextureDescriptor() {
"yzwx-read", "yzwx-read",
"reserved-swizzle", "reserved-swizzle",
"array-base-out-of-range", "array-base-out-of-range",
"array-mip-view",
"reserved", "reserved",
"uint-format", "uint-format",
"uint-resource-float-format", "uint-resource-float-format",
@@ -17468,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();