mirror of
https://github.com/KytyPS5/KytyPS5.git
synced 2026-08-03 11:23:49 +00:00
Compare commits
12
Commits
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
77aa28b27c | ||
|
|
d04938c88c | ||
|
|
85622befb8 | ||
|
|
3965d41d36 | ||
|
|
c508c4a9c0 | ||
|
|
e91dd39cb0 | ||
|
|
cc76827e63 | ||
|
|
832bc84100 | ||
|
|
65a0f0baa7 | ||
|
|
b9ae2537ef | ||
|
|
0b9edaa721 | ||
|
|
8a244677d7 |
@@ -50,7 +50,7 @@ graphical glitches, low compatibility, and poor performance.
|
||||
</tr>
|
||||
<tr>
|
||||
<td align="center">
|
||||
<strong>Minecraft Legends</strong><br>
|
||||
<strong>Neptunia ReVerse</strong><br>
|
||||
<img src="docs/screenshots/ps5-04.png" width="300" alt="Minecraft Legends running in KytyPS5">
|
||||
</td>
|
||||
<td align="center">
|
||||
@@ -58,8 +58,20 @@ graphical glitches, low compatibility, and poor performance.
|
||||
<img src="docs/screenshots/ps5-05.png" width="300" alt="SILENT HILL: The Short Message running in KytyPS5">
|
||||
</td>
|
||||
</tr>
|
||||
<tr>
|
||||
<td align="center">
|
||||
<strong>Hellboy</strong><br>
|
||||
<img src="docs/screenshots/ps5-02.png" width="300" alt="Disgaea 6 running in KytyPS5">
|
||||
</td>
|
||||
<td align="center">
|
||||
<strong>Paleo Pines</strong><br>
|
||||
<img src="docs/screenshots/ps5-06.png" width="300" alt="Dreaming Sarah running in KytyPS5">
|
||||
</td>
|
||||
</tr>
|
||||
</table>
|
||||
|
||||
<p align="center"><em>And many more...</em></p>
|
||||
|
||||
## Contributing
|
||||
|
||||
Testing games and submitting detailed bug reports are useful ways to contribute. Search existing
|
||||
|
||||
Binary file not shown.
|
After Width: | Height: | Size: 1.6 MiB |
Binary file not shown.
|
Before Width: | Height: | Size: 2.7 MiB After Width: | Height: | Size: 2.6 MiB |
Binary file not shown.
|
After Width: | Height: | Size: 2.4 MiB |
@@ -432,11 +432,6 @@ uint64_t SysVirtualReserveAligned(uint64_t address, uint64_t size, uint64_t alig
|
||||
|
||||
pthread_mutex_lock(&g_virtual_mutex);
|
||||
record_alloc(ret_addr, size);
|
||||
uintptr_t page_start = ret_addr >> 12u;
|
||||
uintptr_t page_end = (ret_addr + size - 1) >> 12u;
|
||||
for (uintptr_t page = page_start; page <= page_end; page++) {
|
||||
(*g_protects)[page] = PROT_NONE;
|
||||
}
|
||||
pthread_mutex_unlock(&g_virtual_mutex);
|
||||
|
||||
return ret_addr;
|
||||
@@ -470,11 +465,6 @@ bool SysVirtualReserveFixed(uint64_t address, uint64_t size) {
|
||||
if (ptr != MAP_FAILED) {
|
||||
pthread_mutex_lock(&g_virtual_mutex);
|
||||
record_alloc(ret_addr, size);
|
||||
uintptr_t page_start = ret_addr >> 12u;
|
||||
uintptr_t page_end = (ret_addr + size - 1) >> 12u;
|
||||
for (uintptr_t page = page_start; page <= page_end; page++) {
|
||||
(*g_protects)[page] = PROT_NONE;
|
||||
}
|
||||
pthread_mutex_unlock(&g_virtual_mutex);
|
||||
|
||||
return true;
|
||||
|
||||
+2
-1
@@ -394,6 +394,7 @@ void BufferCache::InvalidateMemory(uint64_t vaddr, uint64_t size) {
|
||||
}
|
||||
|
||||
void BufferCache::ReadMemory(uint64_t vaddr, uint64_t size) {
|
||||
(void)SynchronizeBacking(vaddr, size);
|
||||
std::vector<DownloadCopy> copies;
|
||||
{
|
||||
FaultSafeCacheLock lock(this, m_mutex);
|
||||
@@ -426,7 +427,7 @@ void BufferCache::ReadMemory(uint64_t vaddr, uint64_t size) {
|
||||
});
|
||||
}
|
||||
if (copies.empty()) {
|
||||
EXIT("BufferCache: GPU-owned invalidation has no dirty byte ranges\n");
|
||||
return;
|
||||
}
|
||||
auto downloads = RecordDownloads(copies);
|
||||
m_scheduler.FinishCurrent();
|
||||
|
||||
+1
-1
@@ -50,6 +50,7 @@ public:
|
||||
[[nodiscard]] bool InvalidateMemory(PageFaultAccess access, uint64_t vaddr, uint64_t size,
|
||||
PageFaultPhase phase) noexcept;
|
||||
void InvalidateMemory(uint64_t vaddr, uint64_t size);
|
||||
void ReadMemory(uint64_t vaddr, uint64_t size);
|
||||
void UnmapMemory(uint64_t vaddr, uint64_t size);
|
||||
[[nodiscard]] BufferBinding ObtainBuffer(CommandBuffer& command, uint64_t vaddr, uint64_t size,
|
||||
bool is_written = false, bool is_read = true,
|
||||
@@ -111,7 +112,6 @@ private:
|
||||
void QueueGarbageDownload(std::span<const DownloadCopy> copies, RetiredBuffer retire);
|
||||
void RefreshInvalidatedRanges(CommandBuffer& command, CachedBuffer& cached, uint64_t vaddr,
|
||||
uint64_t size, bool upload);
|
||||
void ReadMemory(uint64_t vaddr, uint64_t size);
|
||||
void DiscardGpuDirtyBytesLocked(uint64_t vaddr, uint64_t size, const char* operation);
|
||||
void WriteHostMemory(uint64_t vaddr, std::span<const uint8_t> data);
|
||||
|
||||
|
||||
@@ -36,7 +36,8 @@ bool GpuResourceManager::InvalidateMemory(PageFaultAccess access, uint64_t vaddr
|
||||
}
|
||||
|
||||
bool GpuResourceManager::HandleFault(PageFaultAccess access, uint64_t fault_vaddr) noexcept {
|
||||
if (!m_page_manager.IsMapped(fault_vaddr, 1)) {
|
||||
constexpr uint64_t fault_size = 8;
|
||||
if (!IsMapped(fault_vaddr, fault_size)) {
|
||||
return false;
|
||||
}
|
||||
if (CommandScheduler::InDeferredOperation()) {
|
||||
@@ -47,10 +48,15 @@ bool GpuResourceManager::HandleFault(PageFaultAccess access, uint64_t fault_vadd
|
||||
bool handled = false;
|
||||
const auto resolve = [this, access, fault_vaddr, &handled](CommandProcessor& cp) {
|
||||
cp.BeginReadbackTransaction();
|
||||
(void)m_buffer_cache.SynchronizeBacking(fault_vaddr, 1);
|
||||
{
|
||||
ResourceMutex::FaultScope fault(m_resource_mutex);
|
||||
handled = m_page_manager.HandleFault(access, fault_vaddr);
|
||||
if (access == PageFaultAccess::Write) {
|
||||
m_buffer_cache.InvalidateMemory(fault_vaddr, fault_size);
|
||||
m_texture_cache.InvalidateMemory(fault_vaddr, fault_size);
|
||||
} else {
|
||||
m_buffer_cache.ReadMemory(fault_vaddr, fault_size);
|
||||
}
|
||||
handled = true;
|
||||
}
|
||||
cp.EndReadbackTransaction();
|
||||
};
|
||||
|
||||
+35
-16
@@ -88,15 +88,34 @@ TextureCache::~TextureCache() {
|
||||
|
||||
bool TextureCache::SameBacking(const ImageInfo& cached, const ImageInfo& requested,
|
||||
bool exact_format) {
|
||||
const bool unit_extent =
|
||||
requested.extent.width == 1 && requested.extent.height == 1 && requested.extent.depth == 1;
|
||||
return cached.data == requested.data && cached.extent == requested.extent &&
|
||||
cached.samples == requested.samples &&
|
||||
cached.bytes_per_block == requested.bytes_per_block &&
|
||||
(cached.type == requested.type || unit_extent) &&
|
||||
(exact_format
|
||||
? cached.pixel_format == requested.pixel_format
|
||||
: ImageViewOps::FormatsCompatible(cached.pixel_format, requested.pixel_format));
|
||||
if (cached.data.address != requested.data.address) {
|
||||
return false;
|
||||
}
|
||||
if (cached.data.size != requested.data.size) {
|
||||
return false;
|
||||
}
|
||||
if (cached.extent != requested.extent) {
|
||||
return false;
|
||||
}
|
||||
if (cached.samples != requested.samples) {
|
||||
return false;
|
||||
}
|
||||
if (cached.bytes_per_block != requested.bytes_per_block) {
|
||||
return false;
|
||||
}
|
||||
if (cached.tile_mode != requested.tile_mode) {
|
||||
return false;
|
||||
}
|
||||
if (!ImageViewOps::FormatsCompatible(cached.pixel_format, requested.pixel_format)) {
|
||||
return false;
|
||||
}
|
||||
if (cached.type != requested.type && requested.extent != vk::Extent3D {1, 1, 1}) {
|
||||
return false;
|
||||
}
|
||||
if (exact_format && cached.pixel_format != requested.pixel_format) {
|
||||
return false;
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
TextureCache::BindingType TextureCache::UploadBinding(const Image& image) {
|
||||
@@ -735,6 +754,12 @@ TextureCache::OverlapResult TextureCache::ResolveOverlap(const ImageInfo& reques
|
||||
(requested.IsVolume() || cached.info.IsVolume())) {
|
||||
return {ExpandImage(requested, cached_id)};
|
||||
}
|
||||
if (requested.tile_mode != cached.info.tile_mode) {
|
||||
if (safe_to_delete) {
|
||||
DeleteImages(std::array {cached_id}, cached_id);
|
||||
}
|
||||
return {merged_id};
|
||||
}
|
||||
if (requested.pixel_format != cached.info.pixel_format ||
|
||||
requested.data.size <= cached.info.data.size) {
|
||||
const auto result_id = merged_id ? merged_id : cached_id;
|
||||
@@ -747,12 +772,6 @@ TextureCache::OverlapResult TextureCache::ResolveOverlap(const ImageInfo& reques
|
||||
if (requested.type == cached.info.type && requested.resources > cached.info.resources) {
|
||||
return {ExpandImage(requested, cached_id)};
|
||||
}
|
||||
if (requested.tile_mode != cached.info.tile_mode) {
|
||||
if (safe_to_delete) {
|
||||
DeleteImages(std::array {cached_id}, cached_id);
|
||||
}
|
||||
return {merged_id};
|
||||
}
|
||||
EXIT("TextureCache: unresolvable equal-address image overlap, address=0x%016" PRIx64
|
||||
" requested=%ux%u "
|
||||
"cached=%ux%u requested_size=0x%016" PRIx64 " cached_size=0x%016" PRIx64
|
||||
@@ -1122,7 +1141,7 @@ ImageId TextureCache::FindImage(ImageDesc& desc, bool exact_format) {
|
||||
|
||||
for (const auto id: candidates) {
|
||||
const auto owner = ResolveOwner(id);
|
||||
if (owner == nullptr || owner->info.data != desc.info.data) {
|
||||
if (owner == nullptr) {
|
||||
continue;
|
||||
}
|
||||
if (SameBacking(owner->info, desc.info, exact_format)) {
|
||||
|
||||
@@ -103,10 +103,7 @@ IsSupportedSampledDepthUintResource(const ShaderRecompiler::IR::ImageResource& r
|
||||
|
||||
inline void ValidateStorageColorView(vk::Format image_format, vk::Format view_format,
|
||||
uint32_t swizzle) noexcept {
|
||||
const auto srgb_view = SrgbStorageViewFormat(image_format);
|
||||
const bool srgb_storage_view =
|
||||
srgb_view != vk::Format::eUndefined && view_format == srgb_view;
|
||||
if ((image_format != view_format && !srgb_storage_view) ||
|
||||
if (!ImageViewOps::FormatsCompatible(image_format, view_format) ||
|
||||
!IsValidImageSwizzle(swizzle)) {
|
||||
UnsupportedColorView("storage", image_format, view_format, swizzle);
|
||||
}
|
||||
@@ -122,7 +119,10 @@ IsSupportedStorageImageResource(const ShaderRecompiler::IR::ImageResource& resou
|
||||
resource.dimension == ShaderRecompiler::Decoder::ImageDimension::Dim3D ||
|
||||
resource.dimension == ShaderRecompiler::Decoder::ImageDimension::Dim2DArray) &&
|
||||
resource.mip_mode == ShaderRecompiler::IR::ImageMipMode::None && resource.written &&
|
||||
!resource.atomic && !resource.depth_compare;
|
||||
(!resource.atomic ||
|
||||
(resource.kind == ShaderRecompiler::IR::ResourceKind::StorageImageUint &&
|
||||
resource.read)) &&
|
||||
!resource.depth_compare;
|
||||
}
|
||||
|
||||
inline void
|
||||
|
||||
@@ -14,13 +14,13 @@
|
||||
#include "graphics/guest_gpu/tile.h"
|
||||
#include "graphics/host_gpu/graphicContext.h"
|
||||
#include "graphics/host_gpu/hostMemory.h"
|
||||
#include "graphics/host_gpu/renderer/image/textureCommon.h"
|
||||
#include "graphics/host_gpu/renderer/debug.h"
|
||||
#include "graphics/host_gpu/renderer/pipeline/descriptorCache.h"
|
||||
#include "graphics/host_gpu/renderer/image/imageView.h"
|
||||
#include "graphics/host_gpu/renderer/image/textureCommon.h"
|
||||
#include "graphics/host_gpu/renderer/pipeline/descriptorCache.h"
|
||||
#include "graphics/host_gpu/renderer/pipeline/shaderResourceBarrier.h"
|
||||
#include "graphics/host_gpu/renderer/render.h"
|
||||
#include "graphics/host_gpu/renderer/renderContext.h"
|
||||
#include "graphics/host_gpu/renderer/pipeline/shaderResourceBarrier.h"
|
||||
#include "graphics/host_gpu/vma.h"
|
||||
#include "graphics/host_gpu/vulkanCommon.h"
|
||||
#include "graphics/shader/recompiler/ir/BindingLayout.h"
|
||||
@@ -239,11 +239,11 @@ bool IsSupportedDepthTextureEncoding(const ShaderTextureResource& descriptor, co
|
||||
const uint32_t field3_expected =
|
||||
(descriptor.Type() << 28u) | field3_common | descriptor.DstSelXYZW();
|
||||
const uint32_t field4_expected = descriptor.Depth() | (descriptor.BaseArray5() << 16u);
|
||||
const bool common = (descriptor.fields[1] & field1_reserved_mask) == 0 &&
|
||||
(descriptor.fields[2] & field2_reserved_mask) == 0 &&
|
||||
descriptor.fields[3] == field3_expected &&
|
||||
descriptor.fields[4] == field4_expected &&
|
||||
descriptor.fields[5] == field5_expected;
|
||||
const bool common = (descriptor.fields[1] & field1_reserved_mask) == 0 &&
|
||||
(descriptor.fields[2] & field2_reserved_mask) == 0 &&
|
||||
descriptor.fields[3] == field3_expected &&
|
||||
descriptor.fields[4] == field4_expected &&
|
||||
descriptor.fields[5] == field5_expected;
|
||||
if (!common || (descriptor.fields[6] == 0 && descriptor.fields[7] != 0)) {
|
||||
return false;
|
||||
}
|
||||
@@ -318,8 +318,8 @@ static bool IsSupportedStorageTextureDescriptor(const ShaderRecompiler::IR::Imag
|
||||
const bool valid_2d_slice =
|
||||
(is_color_2d && descriptor.Depth() == 0 && descriptor.BaseArray5() == 0) ||
|
||||
(is_color_2d_array && descriptor.BaseArray5() <= descriptor.Depth());
|
||||
const bool is_2d = resource.dimension == ShaderRecompiler::Decoder::ImageDimension::Dim2D &&
|
||||
valid_2d_slice;
|
||||
const bool is_2d =
|
||||
resource.dimension == ShaderRecompiler::Decoder::ImageDimension::Dim2D && valid_2d_slice;
|
||||
const bool is_2d_array =
|
||||
resource.dimension == ShaderRecompiler::Decoder::ImageDimension::Dim2DArray &&
|
||||
is_color_2d_array && descriptor.BaseArray5() <= descriptor.Depth();
|
||||
@@ -340,13 +340,13 @@ static bool IsSupportedStorageTextureDescriptor(const ShaderRecompiler::IR::Imag
|
||||
const bool supported_tile = tile == Prospero::GpuEnumValue(Prospero::TileMode::kLinear) ||
|
||||
tile == Prospero::GpuEnumValue(Prospero::TileMode::kRenderTarget) ||
|
||||
supported_depth_tile || supported_standard_tile;
|
||||
const auto swizzle = descriptor.DstSelXYZW();
|
||||
const bool supported_swizzle =
|
||||
IsValidImageSwizzle(descriptor.DstSelXYZW()) &&
|
||||
(descriptor.DstSelXYZW() == DstSel(4, 5, 6, 7) || !resource.read);
|
||||
IsValidImageSwizzle(swizzle) &&
|
||||
(swizzle == DstSel(4, 5, 6, 7) || !resource.read || resource.atomic);
|
||||
const bool supported_mip_view = descriptor.BaseLevel() == 0 || is_1d || is_2d;
|
||||
return (is_1d || is_1d_array || is_2d || is_2d_array || is_3d) && supported_tile &&
|
||||
supported_mip_view &&
|
||||
descriptor.BaseLevel() == descriptor.LastLevel() &&
|
||||
supported_mip_view && descriptor.BaseLevel() == descriptor.LastLevel() &&
|
||||
descriptor.LastLevel() <= descriptor.MaxMip() && descriptor.MinLod() == 0 &&
|
||||
supported_swizzle && descriptor.BCSwizzle() == 0 && !descriptor.MsaaDepth();
|
||||
}
|
||||
@@ -377,8 +377,10 @@ void ValidateStorageTexture(const ShaderRecompiler::IR::ImageResource& resource,
|
||||
const bool encoding_ok = IsSupportedStorageTextureEncoding(descriptor);
|
||||
const bool uint_resource =
|
||||
resource.kind == ShaderRecompiler::IR::ResourceKind::StorageImageUint;
|
||||
const bool format_ok = Prospero::IsSupportedTextureFormat(format) &&
|
||||
uint_resource == Prospero::IsUintTextureFormat(format);
|
||||
const bool format_ok =
|
||||
Prospero::IsSupportedTextureFormat(format) &&
|
||||
uint_resource == Prospero::IsUintTextureFormat(format) &&
|
||||
(!resource.atomic || format == Prospero::GpuEnumValue(Prospero::BufferFormat::k32UInt));
|
||||
if (resource_ok && descriptor_ok && encoding_ok && format_ok && size != 0) {
|
||||
return;
|
||||
}
|
||||
@@ -618,12 +620,12 @@ RenderExecutor::ResolveTexture(const ShaderRecompiler::IR::ImageResource& reso
|
||||
resource.written);
|
||||
}
|
||||
|
||||
const auto pixel_format = TextureGetFormat(format);
|
||||
const auto pixel_format = TextureGetFormat(format);
|
||||
const auto storage_view_format = SrgbStorageViewFormat(pixel_format);
|
||||
const auto view_format =
|
||||
storage && storage_view_format != vk::Format::eUndefined ? storage_view_format
|
||||
: pixel_format;
|
||||
const auto block_bytes = Prospero::BlockCompressedBytesPerBlock(format);
|
||||
const auto view_format = storage && storage_view_format != vk::Format::eUndefined
|
||||
? storage_view_format
|
||||
: pixel_format;
|
||||
const auto block_bytes = Prospero::BlockCompressedBytesPerBlock(format);
|
||||
TextureCache::ImageDesc desc {};
|
||||
desc.info.data = {address, size.size};
|
||||
desc.info.pixel_format = pixel_format;
|
||||
|
||||
@@ -18,15 +18,16 @@
|
||||
#include "graphics/host_gpu/renderer/depthRenderTarget.h"
|
||||
#include "graphics/host_gpu/renderer/pipeline/descriptorCache.h"
|
||||
#include "graphics/host_gpu/renderer/pipeline/pipelineCache.h"
|
||||
#include "graphics/host_gpu/renderer/render.h"
|
||||
#include "graphics/host_gpu/renderer/renderContext.h"
|
||||
#include "graphics/host_gpu/renderer/pipeline/shaderResourceBarrier.h"
|
||||
#include "graphics/host_gpu/renderer/pipeline/shaderSubgroup.h"
|
||||
#include "graphics/host_gpu/renderer/render.h"
|
||||
#include "graphics/host_gpu/renderer/renderContext.h"
|
||||
#include "graphics/host_gpu/vulkanCommon.h"
|
||||
#include "graphics/shader/recompiler/ir/ResourceMaterialization.h"
|
||||
#include "graphics/shader/recompiler/ir/ShaderIR.h"
|
||||
#include "graphics/shader/shader.h"
|
||||
#include "kernel/eventQueue.h"
|
||||
#include "kernel/memory.h"
|
||||
#include "kernel/pthread.h"
|
||||
#include "libs/errno.h"
|
||||
|
||||
@@ -221,8 +222,7 @@ static void LogDrawTargetState(const char* draw_name, const RenderColorInfo& col
|
||||
LogMrtState(draw_name, buffer, ps_input_info);
|
||||
}
|
||||
|
||||
static void LogDrawInputState(const RenderCommandBuffer& buffer,
|
||||
const RenderColorInfo& color,
|
||||
static void LogDrawInputState(const RenderCommandBuffer& buffer, const RenderColorInfo& color,
|
||||
const ShaderVertexInputInfo& vs_input_info,
|
||||
uint32_t index_type_and_size, uint32_t index_count,
|
||||
const void* index_addr) {
|
||||
@@ -499,9 +499,9 @@ struct DrawCallInfo {
|
||||
};
|
||||
|
||||
RenderState RenderExecutor::AcquireRenderTargets(CommandBuffer& buffer, RenderColorInfo* colors,
|
||||
uint32_t color_count, RenderDepthInfo& depth) {
|
||||
uint32_t color_count, RenderDepthInfo& depth) {
|
||||
EXIT_IF(colors == nullptr || color_count > RENDER_COLOR_ATTACHMENTS_MAX);
|
||||
auto& cache = m_context.GetTextureCache();
|
||||
auto& cache = m_context.GetTextureCache();
|
||||
RenderState state {};
|
||||
state.width = std::numeric_limits<uint32_t>::max();
|
||||
state.height = std::numeric_limits<uint32_t>::max();
|
||||
@@ -512,8 +512,7 @@ RenderState RenderExecutor::AcquireRenderTargets(CommandBuffer& buffer, RenderCo
|
||||
auto& target = colors[i];
|
||||
EXIT_IF(!target.image_id);
|
||||
const auto old_image = cache.ResolveOwner(target.image_id);
|
||||
if (old_image == nullptr ||
|
||||
(!old_image->registered && !old_image->info.data.Empty()) ||
|
||||
if (old_image == nullptr || (!old_image->registered && !old_image->info.data.Empty()) ||
|
||||
old_image->binding.needs_rebind) {
|
||||
if (old_image != nullptr) {
|
||||
old_image->binding = {};
|
||||
@@ -522,7 +521,7 @@ RenderState RenderExecutor::AcquireRenderTargets(CommandBuffer& buffer, RenderCo
|
||||
BindRenderTarget(target.image_id);
|
||||
}
|
||||
target.image_view = cache.FindRenderTarget(target.image_id, target.desc);
|
||||
auto& image = cache.GetImage(target.image_id);
|
||||
auto& image = cache.GetImage(target.image_id);
|
||||
EXIT_IF(image.backing.samples != target.samples || target.image_view == nullptr);
|
||||
if (attachment_samples == 0) {
|
||||
attachment_samples = target.samples;
|
||||
@@ -530,20 +529,19 @@ RenderState RenderExecutor::AcquireRenderTargets(CommandBuffer& buffer, RenderCo
|
||||
EXIT("mixed color attachment sample counts are unsupported: %u and %u\n",
|
||||
attachment_samples, target.samples);
|
||||
}
|
||||
const auto& view = target.desc.view_info;
|
||||
const auto layout =
|
||||
image.binding.is_bound ? vk::ImageLayout::eGeneral
|
||||
: vk::ImageLayout::eColorAttachmentOptimal;
|
||||
const auto& view = target.desc.view_info;
|
||||
const auto layout = image.binding.is_bound ? vk::ImageLayout::eGeneral
|
||||
: vk::ImageLayout::eColorAttachmentOptimal;
|
||||
image.Transit(layout,
|
||||
vk::AccessFlagBits2::eColorAttachmentRead |
|
||||
vk::AccessFlagBits2::eColorAttachmentWrite,
|
||||
ImageSubresourceRange {view.base_level, view.level_count, view.base_layer,
|
||||
view.layer_count},
|
||||
buffer.Handle());
|
||||
state.width = std::min(state.width, target.extent.width);
|
||||
state.height = std::min(state.height, target.extent.height);
|
||||
state.num_layers = std::min(state.num_layers, view.layer_count);
|
||||
auto& attachment = state.color_attachments[i];
|
||||
state.width = std::min(state.width, target.extent.width);
|
||||
state.height = std::min(state.height, target.extent.height);
|
||||
state.num_layers = std::min(state.num_layers, view.layer_count);
|
||||
auto& attachment = state.color_attachments[i];
|
||||
attachment.image_view = target.image_view;
|
||||
attachment.image_layout = layout;
|
||||
attachment.clear_value = target.color_clear_value.uint32;
|
||||
@@ -561,8 +559,7 @@ RenderState RenderExecutor::AcquireRenderTargets(CommandBuffer& buffer, RenderCo
|
||||
depth.depth_meta_clear_enable =
|
||||
depth.htile &&
|
||||
cache.IsMetaCleared(depth.htile_buffer_vaddr, depth.desc.view_info.base_layer);
|
||||
depth.depth_load_clear_enable =
|
||||
depth.depth_clear_enable || depth.depth_meta_clear_enable;
|
||||
depth.depth_load_clear_enable = depth.depth_clear_enable || depth.depth_meta_clear_enable;
|
||||
if (depth.depth_meta_clear_enable &&
|
||||
!cache.TouchMeta(depth.htile_buffer_vaddr, depth.desc.view_info.base_layer, false)) {
|
||||
EXIT("failed to consume HTile clear state\n");
|
||||
@@ -572,12 +569,12 @@ RenderState RenderExecutor::AcquireRenderTargets(CommandBuffer& buffer, RenderCo
|
||||
if (attachment_samples == 0) {
|
||||
attachment_samples = depth.samples;
|
||||
} else if (attachment_samples != depth.samples) {
|
||||
EXIT("mixed color/depth sample counts are unsupported: %u and %u\n",
|
||||
attachment_samples, depth.samples);
|
||||
EXIT("mixed color/depth sample counts are unsupported: %u and %u\n", attachment_samples,
|
||||
depth.samples);
|
||||
}
|
||||
const auto layout = depth_attachment_layout(depth);
|
||||
const auto writes = depth.AttachmentWriteAspects();
|
||||
auto access = vk::AccessFlags2 {vk::AccessFlagBits2::eDepthStencilAttachmentRead};
|
||||
auto access = vk::AccessFlags2 {vk::AccessFlagBits2::eDepthStencilAttachmentRead};
|
||||
if (writes) {
|
||||
access |= vk::AccessFlagBits2::eDepthStencilAttachmentWrite;
|
||||
}
|
||||
@@ -586,21 +583,19 @@ RenderState RenderExecutor::AcquireRenderTargets(CommandBuffer& buffer, RenderCo
|
||||
ImageSubresourceRange {view.base_level, view.level_count, view.base_layer,
|
||||
view.layer_count},
|
||||
buffer.Handle());
|
||||
state.width = std::min(state.width, depth.width);
|
||||
state.height = std::min(state.height, depth.height);
|
||||
state.num_layers = std::min(state.num_layers, view.layer_count);
|
||||
const auto aspects = ImageViewOps::DepthAspectMask(depth.format);
|
||||
auto& attachment = state.depth_stencil_attachment;
|
||||
state.width = std::min(state.width, depth.width);
|
||||
state.height = std::min(state.height, depth.height);
|
||||
state.num_layers = std::min(state.num_layers, view.layer_count);
|
||||
const auto aspects = ImageViewOps::DepthAspectMask(depth.format);
|
||||
auto& attachment = state.depth_stencil_attachment;
|
||||
attachment.image_view = depth.image_view;
|
||||
attachment.image_layout = layout;
|
||||
attachment.clear_value[0] = std::bit_cast<uint32_t>(depth.depth_clear_value);
|
||||
attachment.clear_value[1] = depth.stencil_clear_value;
|
||||
attachment.has_depth =
|
||||
static_cast<bool>(aspects & vk::ImageAspectFlagBits::eDepth);
|
||||
attachment.depth_clear = depth.depth_load_clear_enable;
|
||||
attachment.has_stencil =
|
||||
static_cast<bool>(aspects & vk::ImageAspectFlagBits::eStencil);
|
||||
attachment.stencil_clear = depth.stencil_clear_enable;
|
||||
attachment.has_depth = static_cast<bool>(aspects & vk::ImageAspectFlagBits::eDepth);
|
||||
attachment.depth_clear = depth.depth_load_clear_enable;
|
||||
attachment.has_stencil = static_cast<bool>(aspects & vk::ImageAspectFlagBits::eStencil);
|
||||
attachment.stencil_clear = depth.stencil_clear_enable;
|
||||
}
|
||||
if (attachment_samples == 0 ||
|
||||
vulkan_sample_count(attachment_samples) == vk::SampleCountFlagBits {}) {
|
||||
@@ -685,6 +680,85 @@ static uint64_t VertexBufferDescriptorSize(const ShaderVertexInputBuffer& buffer
|
||||
: buffer.num_records);
|
||||
}
|
||||
|
||||
struct VertexBufferRange {
|
||||
uint64_t base_address = 0;
|
||||
uint64_t requested_end = 0;
|
||||
uint64_t acquired_end = 0;
|
||||
BufferBinding binding;
|
||||
|
||||
[[nodiscard]] uint64_t RequestedSize() const { return requested_end - base_address; }
|
||||
};
|
||||
|
||||
static std::vector<BufferBinding> AcquireVertexBuffers(RenderCommandBuffer& buffer,
|
||||
const ShaderVertexInputInfo& vs_input_info) {
|
||||
// Collect the non-empty guest vertex ranges.
|
||||
std::vector<VertexBufferRange> ranges;
|
||||
ranges.reserve(vs_input_info.buffers_num);
|
||||
for (int i = 0; i < vs_input_info.buffers_num; i++) {
|
||||
const auto& vertex = vs_input_info.buffers[i];
|
||||
const auto size = VertexBufferDescriptorSize(vertex);
|
||||
if (size == 0) {
|
||||
continue;
|
||||
}
|
||||
if (vertex.addr == 0 || size > UINT64_MAX - vertex.addr) {
|
||||
EXIT("invalid vertex buffer range: addr=0x%016" PRIx64 " size=0x%016" PRIx64 "\n",
|
||||
vertex.addr, size);
|
||||
}
|
||||
ranges.push_back({vertex.addr, vertex.addr + size});
|
||||
}
|
||||
|
||||
std::ranges::sort(ranges, [](const VertexBufferRange& left, const VertexBufferRange& right) {
|
||||
return left.base_address < right.base_address;
|
||||
});
|
||||
|
||||
// Merge overlapping or touching ranges before acquiring host buffers.
|
||||
std::vector<VertexBufferRange> merged_ranges;
|
||||
merged_ranges.reserve(ranges.size());
|
||||
for (const auto& range: ranges) {
|
||||
if (!merged_ranges.empty() && merged_ranges.back().requested_end >= range.base_address) {
|
||||
merged_ranges.back().requested_end =
|
||||
std::max(merged_ranges.back().requested_end, range.requested_end);
|
||||
continue;
|
||||
}
|
||||
merged_ranges.push_back(range);
|
||||
}
|
||||
|
||||
auto& cache = buffer.GetContext().GetBufferCache();
|
||||
for (auto& range: merged_ranges) {
|
||||
// PPSA20298
|
||||
const auto size =
|
||||
Libs::LibKernel::Memory::ClampRangeSize(range.base_address, range.RequestedSize());
|
||||
range.acquired_end = range.base_address + size;
|
||||
range.binding = cache.ObtainBuffer(buffer, range.base_address, size);
|
||||
}
|
||||
|
||||
// Rebuild slot bindings, offsetting non-empty slots into their acquired merged range.
|
||||
std::vector<BufferBinding> bindings;
|
||||
bindings.reserve(vs_input_info.buffers_num);
|
||||
for (int i = 0; i < vs_input_info.buffers_num; i++) {
|
||||
const auto& vertex = vs_input_info.buffers[i];
|
||||
const auto size = VertexBufferDescriptorSize(vertex);
|
||||
if (size == 0) {
|
||||
auto owner = cache.ObtainNullBuffer();
|
||||
bindings.push_back({owner, owner->Handle(), 0});
|
||||
continue;
|
||||
}
|
||||
|
||||
const auto range = std::ranges::find_if(merged_ranges, [&](const VertexBufferRange& value) {
|
||||
return vertex.addr >= value.base_address && vertex.addr < value.acquired_end;
|
||||
});
|
||||
if (range == merged_ranges.end()) {
|
||||
EXIT("vertex buffer address is outside the acquired range: addr=0x%016" PRIx64 "\n",
|
||||
vertex.addr);
|
||||
}
|
||||
|
||||
auto binding = range->binding;
|
||||
binding.offset += vertex.addr - range->base_address;
|
||||
bindings.push_back(std::move(binding));
|
||||
}
|
||||
return bindings;
|
||||
}
|
||||
|
||||
static void SetDrawDebugPhase(RenderCommandBuffer& buffer, uint64_t submit_id,
|
||||
const DrawCallInfo& draw, uint32_t phase) {
|
||||
EXIT_IF(draw.name == nullptr);
|
||||
@@ -736,9 +810,9 @@ static bool GetDrawTopology(const HW::UserConfig& ucfg, bool auto_draw, bool use
|
||||
}
|
||||
|
||||
bool RenderExecutor::PrepareDrawRenderState(uint64_t submit_id, RenderCommandBuffer& buffer,
|
||||
const DrawCallInfo& draw,
|
||||
uint32_t render_target_slice_offset,
|
||||
bool log_setup_phases, DrawRenderState& state) {
|
||||
const DrawCallInfo& draw,
|
||||
uint32_t render_target_slice_offset,
|
||||
bool log_setup_phases, DrawRenderState& state) {
|
||||
EXIT_IF(draw.name == nullptr);
|
||||
auto& ctx = buffer.GetRegisters();
|
||||
|
||||
@@ -823,37 +897,13 @@ static std::vector<BufferBinding> PrepareVertexBuffers(uint64_t
|
||||
(void)submit_id;
|
||||
|
||||
LogDrawPhase(draw.name, "PrepareVertexBuffers");
|
||||
std::vector<BufferBinding> bindings;
|
||||
bindings.reserve(vs_input_info.buffers_num);
|
||||
for (int i = 0; i < vs_input_info.buffers_num; i++) {
|
||||
const auto& b = vs_input_info.buffers[i];
|
||||
const auto size = VertexBufferDescriptorSize(b);
|
||||
if (size == 0) {
|
||||
auto owner = buffer.GetContext().GetBufferCache().ObtainNullBuffer();
|
||||
bindings.push_back({owner, owner->Handle(), 0});
|
||||
} else {
|
||||
bindings.push_back(
|
||||
buffer.GetContext().GetBufferCache().ObtainBuffer(buffer, b.addr, size));
|
||||
}
|
||||
}
|
||||
return bindings;
|
||||
return AcquireVertexBuffers(buffer, vs_input_info);
|
||||
}
|
||||
|
||||
static void RebindVertexBuffers(RenderCommandBuffer& buffer,
|
||||
const ShaderVertexInputInfo& vs_input_info,
|
||||
std::vector<BufferBinding>& bindings) {
|
||||
EXIT_IF(bindings.size() != static_cast<size_t>(vs_input_info.buffers_num));
|
||||
for (int i = 0; i < vs_input_info.buffers_num; i++) {
|
||||
const auto& vertex = vs_input_info.buffers[i];
|
||||
const auto size = VertexBufferDescriptorSize(vertex);
|
||||
if (size == 0) {
|
||||
auto owner = buffer.GetContext().GetBufferCache().ObtainNullBuffer();
|
||||
bindings[i] = {owner, owner->Handle(), 0};
|
||||
} else {
|
||||
bindings[i] =
|
||||
buffer.GetContext().GetBufferCache().ObtainBuffer(buffer, vertex.addr, size);
|
||||
}
|
||||
}
|
||||
bindings = AcquireVertexBuffers(buffer, vs_input_info);
|
||||
}
|
||||
|
||||
static PreparedIndexBuffer PrepareIndexBuffer(RenderCommandBuffer& buffer,
|
||||
@@ -1011,17 +1061,17 @@ static void EmitDrawPrimitives(const HW::UserConfig& ucfg, vk::CommandBuffer vk_
|
||||
}
|
||||
|
||||
void RenderExecutor::ExecutePreparedDraw(uint64_t submit_id, RenderCommandBuffer& buffer,
|
||||
const DrawCallInfo& draw, DrawRenderState& state,
|
||||
vk::PrimitiveTopology topology, const DrawEmitInfo& emit,
|
||||
const DrawIndexBufferSource& index_source,
|
||||
bool log_pipeline_phase, bool set_bind_debug,
|
||||
bool set_auto_debug) {
|
||||
const DrawCallInfo& draw, DrawRenderState& state,
|
||||
vk::PrimitiveTopology topology, const DrawEmitInfo& emit,
|
||||
const DrawIndexBufferSource& index_source,
|
||||
bool log_pipeline_phase, bool set_bind_debug,
|
||||
bool set_auto_debug) {
|
||||
EXIT_IF(draw.name == nullptr);
|
||||
auto& ucfg = buffer.GetUserConfig();
|
||||
|
||||
LogDrawPhase(draw.name, "PrepareBindings");
|
||||
auto bindings = PrepareGraphicsBindings(buffer, state.vs_input_info.stage,
|
||||
state.ps_input_info.stage, state.ps_active);
|
||||
auto bindings = PrepareGraphicsBindings(buffer, state.vs_input_info.stage,
|
||||
state.ps_input_info.stage, state.ps_active);
|
||||
auto vertex_bindings = PrepareVertexBuffers(submit_id, buffer, draw, state.vs_input_info);
|
||||
auto index_binding = PrepareIndexBuffer(buffer, index_source);
|
||||
RebindVertexBuffers(buffer, state.vs_input_info, vertex_bindings);
|
||||
@@ -1094,10 +1144,10 @@ void RenderExecutor::ExecutePreparedDraw(uint64_t submit_id, RenderCommandBuffer
|
||||
}
|
||||
|
||||
void RenderExecutor::DrawIndex(uint64_t submit_id, RenderCommandBuffer& buffer,
|
||||
uint32_t index_type_and_size, uint32_t index_count,
|
||||
const void* index_addr, uint32_t flags, uint32_t type,
|
||||
uint32_t instance_count, uint32_t render_target_slice_offset,
|
||||
int32_t vertex_offset_add, uint32_t first_instance) {
|
||||
uint32_t index_type_and_size, uint32_t index_count,
|
||||
const void* index_addr, uint32_t flags, uint32_t type,
|
||||
uint32_t instance_count, uint32_t render_target_slice_offset,
|
||||
int32_t vertex_offset_add, uint32_t first_instance) {
|
||||
KYTY_PROFILER_FUNCTION();
|
||||
|
||||
EXIT_IF(buffer.IsInvalid());
|
||||
@@ -1228,11 +1278,10 @@ void RenderExecutor::DrawIndex(uint64_t submit_id, RenderCommandBuffer& buffer,
|
||||
}
|
||||
|
||||
// NOLINTNEXTLINE(readability-function-cognitive-complexity)
|
||||
void RenderExecutor::DrawAuto(uint64_t submit_id, RenderCommandBuffer& buffer,
|
||||
uint32_t index_count,
|
||||
uint32_t flags, uint32_t render_target_slice_offset,
|
||||
uint32_t instance_count, uint32_t first_vertex,
|
||||
uint32_t first_instance) {
|
||||
void RenderExecutor::DrawAuto(uint64_t submit_id, RenderCommandBuffer& buffer, uint32_t index_count,
|
||||
uint32_t flags, uint32_t render_target_slice_offset,
|
||||
uint32_t instance_count, uint32_t first_vertex,
|
||||
uint32_t first_instance) {
|
||||
KYTY_PROFILER_FUNCTION();
|
||||
|
||||
EXIT_IF(buffer.IsInvalid());
|
||||
@@ -1290,7 +1339,8 @@ void RenderExecutor::DrawAuto(uint64_t submit_id, RenderCommandBuffer& buffer,
|
||||
instance_count, first_instance};
|
||||
|
||||
DrawRenderState state {};
|
||||
if (!PrepareDrawRenderState(submit_id, buffer, draw, render_target_slice_offset, false, state)) {
|
||||
if (!PrepareDrawRenderState(submit_id, buffer, draw, render_target_slice_offset, false,
|
||||
state)) {
|
||||
ResetBindings();
|
||||
return;
|
||||
}
|
||||
@@ -1340,7 +1390,7 @@ void RenderExecutor::DrawAuto(uint64_t submit_id, RenderCommandBuffer& buffer,
|
||||
}
|
||||
|
||||
bool RenderExecutor::ResolveColorTargets(uint64_t submit_id, RenderCommandBuffer& buffer,
|
||||
uint32_t render_target_slice_offset) {
|
||||
uint32_t render_target_slice_offset) {
|
||||
const auto& hw = buffer.GetRegisters();
|
||||
if (hw.GetColorControl().mode != 3) {
|
||||
return false;
|
||||
@@ -1369,8 +1419,7 @@ bool RenderExecutor::ResolveColorTargets(uint64_t submit_id, RenderCommandBuffer
|
||||
cache.MarkGpuWritten(dst.image_id);
|
||||
auto& source = cache.GetImage(src.image_id);
|
||||
auto& destination = cache.GetImage(dst.image_id);
|
||||
destination.Resolve(source,
|
||||
{src.base_mip_level, 1, src.base_array_layer, 1},
|
||||
destination.Resolve(source, {src.base_mip_level, 1, src.base_array_layer, 1},
|
||||
{dst.base_mip_level, 1, dst.base_array_layer, 1});
|
||||
return true;
|
||||
}
|
||||
|
||||
@@ -421,13 +421,13 @@ bool DecodeDs(uint32_t pc, std::span<const uint32_t> code, uint32_t word_index,
|
||||
const uint32_t data0 = (word1 >> 8u) & 0xffu;
|
||||
const uint32_t addr = word1 & 0xffu;
|
||||
|
||||
inst.pc = pc;
|
||||
inst.word = word0;
|
||||
inst.word_count = 2;
|
||||
inst.offset = offset0 | (offset1 << 8u);
|
||||
inst.gds = ((word0 >> 17u) & 1u) != 0u;
|
||||
inst.family = Family::DS;
|
||||
inst.opcode_id = opcode;
|
||||
inst.pc = pc;
|
||||
inst.word = word0;
|
||||
inst.word_count = 2;
|
||||
inst.offset = offset0 | (offset1 << 8u);
|
||||
inst.gds = ((word0 >> 17u) & 1u) != 0u;
|
||||
inst.family = Family::DS;
|
||||
inst.opcode_id = opcode;
|
||||
const auto* info = LookupMemoryOpcode(DS_OPS, static_cast<uint32_t>(std::size(DS_OPS)), opcode);
|
||||
ApplyMemoryInfo(inst, info);
|
||||
SetRawWords(inst, code, word_index, 2);
|
||||
@@ -442,11 +442,6 @@ bool DecodeDs(uint32_t pc, std::span<const uint32_t> code, uint32_t word_index,
|
||||
inst.opcode == Opcode::DsReadAddtidB32)) {
|
||||
SetUnsupported(inst, Family::DS, opcode, "DS swizzle/addtid is available only for LDS");
|
||||
}
|
||||
if (inst.gds && (inst.opcode == Opcode::DsAppend || inst.opcode == Opcode::DsConsume) &&
|
||||
inst.offset != 0u) {
|
||||
SetUnsupported(inst, Family::DS, opcode,
|
||||
"GDS append/consume requires a zero instruction offset");
|
||||
}
|
||||
if (inst.opcode == Opcode::DsWriteAddtidB32 && data1 != 0u) {
|
||||
SetUnsupported(inst, Family::DS, opcode,
|
||||
"DS write addtid data1 operand is not implemented");
|
||||
|
||||
@@ -1,7 +1,7 @@
|
||||
#include "graphics/shader/recompiler/emitter/SpirvEmitter.h"
|
||||
|
||||
#include "graphics/shader/recompiler/ir/SrtWalker.h"
|
||||
#include "graphics/shader/recompiler/emitter/spirvEmitterInternal.h"
|
||||
#include "graphics/shader/recompiler/ir/SrtWalker.h"
|
||||
|
||||
#include <algorithm>
|
||||
#include <array>
|
||||
@@ -160,8 +160,7 @@ bool ValidateInstructionContract(const IR::Instruction& inst, std::string* error
|
||||
inst.dst.kind != IR::OperandKind::Null)) ||
|
||||
((inst.op == IR::Opcode::DsAppend || inst.op == IR::Opcode::DsConsume) &&
|
||||
(!ds_kind || !ds_resource || inst.src_count != 1 ||
|
||||
inst.dst.kind != IR::OperandKind::Register ||
|
||||
(kind == IR::ResourceKind::Gds && inst.memory.offset != 0))) ||
|
||||
inst.dst.kind != IR::OperandKind::Register)) ||
|
||||
((inst.op == IR::Opcode::DsMinF32 || inst.op == IR::Opcode::DsMaxF32) &&
|
||||
(!ds_kind || !ds_resource || inst.src_count != 3 ||
|
||||
inst.dst.kind != IR::OperandKind::Null)) ||
|
||||
|
||||
@@ -1025,15 +1025,47 @@ void EmitDispatcherSwitch(EmitterState& state, const IR::Program& program) {
|
||||
EmitDispatcherExit(state);
|
||||
}
|
||||
|
||||
size_t BufferLoadGroupSize(const IR::BasicBlock& block, size_t first_index) {
|
||||
const auto& first = block.instructions[first_index];
|
||||
if (first.op != IR::Opcode::BufferLoadDword || first.memory.component_index != 0u ||
|
||||
first.memory.component_count <= 1u) {
|
||||
return 1u;
|
||||
}
|
||||
|
||||
size_t count = 1u;
|
||||
while (first_index + count < block.instructions.size() &&
|
||||
count < first.memory.component_count) {
|
||||
const auto& next = block.instructions[first_index + count];
|
||||
if (next.op != IR::Opcode::BufferLoadDword || next.pc != first.pc ||
|
||||
next.memory.component_index != count ||
|
||||
next.memory.component_count != first.memory.component_count) {
|
||||
break;
|
||||
}
|
||||
count++;
|
||||
}
|
||||
return count;
|
||||
}
|
||||
|
||||
void EmitBlockInstructions(EmitterState& state, const IR::BasicBlock& block) {
|
||||
for (size_t i = 0; i < block.instructions.size();) {
|
||||
const auto count = BufferLoadGroupSize(block, i);
|
||||
if (count > 1u) {
|
||||
EmitBufferLoadDwordGroup(state, block.instructions.data() + i,
|
||||
static_cast<uint32_t>(count));
|
||||
} else {
|
||||
EmitInstruction(state, block.instructions[i]);
|
||||
}
|
||||
i += count;
|
||||
}
|
||||
}
|
||||
|
||||
void EmitDispatcherBlocks(EmitterState& state, const IR::Program& program) {
|
||||
for (const auto& block: program.blocks) {
|
||||
if (block.id >= state.reachable_blocks.size() || !state.reachable_blocks[block.id]) {
|
||||
continue;
|
||||
}
|
||||
state.builder.AddFunction({OpLabel, BlockLabel(state, block.id)});
|
||||
for (const auto& inst: block.instructions) {
|
||||
EmitInstruction(state, inst);
|
||||
}
|
||||
EmitBlockInstructions(state, block);
|
||||
EmitDispatcherTerminator(state, block.terminator);
|
||||
}
|
||||
}
|
||||
@@ -1083,9 +1115,7 @@ void EmitFunction(EmitterState& state, const IR::Program& program) {
|
||||
continue;
|
||||
}
|
||||
state.builder.AddFunction({OpLabel, BlockLabel(state, block.id)});
|
||||
for (const auto& inst: block.instructions) {
|
||||
EmitInstruction(state, inst);
|
||||
}
|
||||
EmitBlockInstructions(state, block);
|
||||
EmitTerminator(state, block.terminator);
|
||||
}
|
||||
|
||||
|
||||
@@ -3,11 +3,11 @@
|
||||
|
||||
#include "common/common.h"
|
||||
#include "common/stringUtils.h"
|
||||
#include "graphics/shader/recompiler/ir/BindingLayout.h"
|
||||
#include "graphics/shader/recompiler/BufferFormat.h"
|
||||
#include "graphics/shader/recompiler/emitter/SpirvBuilder.h"
|
||||
#include "graphics/shader/recompiler/ir/BindingLayout.h"
|
||||
#include "graphics/shader/recompiler/ir/ResourceMaterialization.h"
|
||||
#include "graphics/shader/recompiler/ir/ShaderIR.h"
|
||||
#include "graphics/shader/recompiler/emitter/SpirvBuilder.h"
|
||||
|
||||
#include <algorithm>
|
||||
#include <array>
|
||||
@@ -312,117 +312,117 @@ struct EmitterState {
|
||||
EmitterState(const IR::Program& program_, const IR::ResourceSnapshot& resources_)
|
||||
: program(program_), resources(resources_) {}
|
||||
|
||||
Builder builder;
|
||||
const IR::Program& program;
|
||||
const IR::ResourceSnapshot& resources;
|
||||
const ShaderVertexInputInfo* vertex_input_info = nullptr;
|
||||
const ShaderPixelInputInfo* pixel_input_info = nullptr;
|
||||
const ShaderComputeInputInfo* compute_input_info = nullptr;
|
||||
ShaderType stage = ShaderType::Unknown;
|
||||
uint32_t wave_size = 64;
|
||||
bool exact_subgroup_operations = false;
|
||||
bool per_invocation_masks = false;
|
||||
uint32_t void_type = 0;
|
||||
uint32_t bool_type = 0;
|
||||
uint32_t uint_type = 0;
|
||||
uint32_t uint_pair_type = 0;
|
||||
uint32_t int_pair_type = 0;
|
||||
uint32_t int_type = 0;
|
||||
uint32_t float_type = 0;
|
||||
uint32_t vec2_uint_type = 0;
|
||||
uint32_t vec3_uint_type = 0;
|
||||
uint32_t vec4_uint_type = 0;
|
||||
uint32_t vec2_int_type = 0;
|
||||
uint32_t vec3_int_type = 0;
|
||||
uint32_t vec4_int_type = 0;
|
||||
uint32_t vec2_float_type = 0;
|
||||
uint32_t vec3_float_type = 0;
|
||||
uint32_t vec4_float_type = 0;
|
||||
uint32_t ptr_func_uint = 0;
|
||||
uint32_t ptr_input_float = 0;
|
||||
uint32_t ptr_input_bool = 0;
|
||||
uint32_t ptr_input_int = 0;
|
||||
uint32_t ptr_input_uint = 0;
|
||||
uint32_t ptr_input_vec2_float = 0;
|
||||
uint32_t ptr_input_vec3_float = 0;
|
||||
uint32_t ptr_input_vec2_int = 0;
|
||||
uint32_t ptr_input_vec3_int = 0;
|
||||
uint32_t ptr_input_vec4_int = 0;
|
||||
uint32_t ptr_input_vec2_uint = 0;
|
||||
uint32_t ptr_input_vec3_uint = 0;
|
||||
uint32_t ptr_input_vec4_uint = 0;
|
||||
uint32_t ptr_input_vec4_float = 0;
|
||||
uint32_t sample_mask_array_type = 0;
|
||||
uint32_t ptr_output_int = 0;
|
||||
uint32_t ptr_output_sample_mask_array = 0;
|
||||
uint32_t ptr_output_float = 0;
|
||||
uint32_t ptr_output_vec4_float = 0;
|
||||
uint32_t per_vertex_type = 0;
|
||||
uint32_t ptr_output_per_vertex = 0;
|
||||
uint32_t storage_runtime_array_type = 0;
|
||||
uint32_t storage_buffer_type = 0;
|
||||
uint32_t ptr_storage_buffer = 0;
|
||||
uint32_t ptr_storage_buffer_uint = 0;
|
||||
uint32_t storage_buffer_array_type = 0;
|
||||
uint32_t ptr_storage_buffer_array = 0;
|
||||
uint32_t storage_buffer_variable = 0;
|
||||
Builder builder;
|
||||
const IR::Program& program;
|
||||
const IR::ResourceSnapshot& resources;
|
||||
const ShaderVertexInputInfo* vertex_input_info = nullptr;
|
||||
const ShaderPixelInputInfo* pixel_input_info = nullptr;
|
||||
const ShaderComputeInputInfo* compute_input_info = nullptr;
|
||||
ShaderType stage = ShaderType::Unknown;
|
||||
uint32_t wave_size = 64;
|
||||
bool exact_subgroup_operations = false;
|
||||
bool per_invocation_masks = false;
|
||||
uint32_t void_type = 0;
|
||||
uint32_t bool_type = 0;
|
||||
uint32_t uint_type = 0;
|
||||
uint32_t uint_pair_type = 0;
|
||||
uint32_t int_pair_type = 0;
|
||||
uint32_t int_type = 0;
|
||||
uint32_t float_type = 0;
|
||||
uint32_t vec2_uint_type = 0;
|
||||
uint32_t vec3_uint_type = 0;
|
||||
uint32_t vec4_uint_type = 0;
|
||||
uint32_t vec2_int_type = 0;
|
||||
uint32_t vec3_int_type = 0;
|
||||
uint32_t vec4_int_type = 0;
|
||||
uint32_t vec2_float_type = 0;
|
||||
uint32_t vec3_float_type = 0;
|
||||
uint32_t vec4_float_type = 0;
|
||||
uint32_t ptr_func_uint = 0;
|
||||
uint32_t ptr_input_float = 0;
|
||||
uint32_t ptr_input_bool = 0;
|
||||
uint32_t ptr_input_int = 0;
|
||||
uint32_t ptr_input_uint = 0;
|
||||
uint32_t ptr_input_vec2_float = 0;
|
||||
uint32_t ptr_input_vec3_float = 0;
|
||||
uint32_t ptr_input_vec2_int = 0;
|
||||
uint32_t ptr_input_vec3_int = 0;
|
||||
uint32_t ptr_input_vec4_int = 0;
|
||||
uint32_t ptr_input_vec2_uint = 0;
|
||||
uint32_t ptr_input_vec3_uint = 0;
|
||||
uint32_t ptr_input_vec4_uint = 0;
|
||||
uint32_t ptr_input_vec4_float = 0;
|
||||
uint32_t sample_mask_array_type = 0;
|
||||
uint32_t ptr_output_int = 0;
|
||||
uint32_t ptr_output_sample_mask_array = 0;
|
||||
uint32_t ptr_output_float = 0;
|
||||
uint32_t ptr_output_vec4_float = 0;
|
||||
uint32_t per_vertex_type = 0;
|
||||
uint32_t ptr_output_per_vertex = 0;
|
||||
uint32_t storage_runtime_array_type = 0;
|
||||
uint32_t storage_buffer_type = 0;
|
||||
uint32_t ptr_storage_buffer = 0;
|
||||
uint32_t ptr_storage_buffer_uint = 0;
|
||||
uint32_t storage_buffer_array_type = 0;
|
||||
uint32_t ptr_storage_buffer_array = 0;
|
||||
uint32_t storage_buffer_variable = 0;
|
||||
std::array<uint32_t, IR::ShaderInfo::MaxBuffers> storage_buffer_offsets {};
|
||||
uint32_t address_memory_array_type = 0;
|
||||
uint32_t ptr_address_memory_array = 0;
|
||||
uint32_t address_memory_variable = 0;
|
||||
uint32_t gds_variable = 0;
|
||||
uint32_t push_constant_array_type = 0;
|
||||
uint32_t push_constant_block_type = 0;
|
||||
uint32_t ptr_push_constant_block = 0;
|
||||
uint32_t ptr_push_constant_uint = 0;
|
||||
uint32_t push_constant_variable = 0;
|
||||
uint32_t vsharp_storage_variable = 0;
|
||||
uint32_t flattened_srt_variable = 0;
|
||||
uint32_t lds_array_type = 0;
|
||||
uint32_t ptr_workgroup_array = 0;
|
||||
uint32_t ptr_workgroup_uint = 0;
|
||||
uint32_t lds_variable = 0;
|
||||
std::array<SampledImageDescriptors, 10> sampled_images;
|
||||
std::array<StorageImageDescriptors, 10> storage_images;
|
||||
uint32_t sampler_type = 0;
|
||||
uint32_t sampler_array_type = 0;
|
||||
uint32_t ptr_uniform_sampler = 0;
|
||||
uint32_t ptr_uniform_sampler_array = 0;
|
||||
uint32_t sampler_variable = 0;
|
||||
uint32_t ptr_image_uint = 0;
|
||||
uint32_t func_type = 0;
|
||||
uint32_t main_func = 0;
|
||||
uint32_t entry_label = 0;
|
||||
uint32_t pixel_valid_mask_variable = 0;
|
||||
bool dispatcher_fallback = false;
|
||||
uint32_t dispatch_pc_variable = 0;
|
||||
uint32_t dispatch_header_label = 0;
|
||||
uint32_t dispatch_select_label = 0;
|
||||
uint32_t dispatch_default_label = 0;
|
||||
uint32_t dispatch_after_switch_label = 0;
|
||||
uint32_t dispatch_continue_label = 0;
|
||||
uint32_t dispatch_merge_label = 0;
|
||||
uint32_t glsl_std450 = 0;
|
||||
uint32_t subgroup_local_invocation_id_variable = 0;
|
||||
uint32_t per_vertex_variable = 0;
|
||||
uint32_t depth_variable = 0;
|
||||
uint32_t sample_mask_variable = 0;
|
||||
bool needs_subgroup_ballot = false;
|
||||
bool needs_subgroup_shuffle = false;
|
||||
bool needs_subgroup_local_invocation_id = false;
|
||||
bool needs_compute_derivatives = false;
|
||||
bool needs_image_gather_extended = false;
|
||||
bool needs_function_lds = false;
|
||||
bool needs_pixel_valid_mask = false;
|
||||
std::vector<RegisterBinding> registers;
|
||||
std::vector<InputBinding> inputs;
|
||||
std::vector<OutputBinding> outputs;
|
||||
std::vector<uint32_t> interface_variables;
|
||||
std::vector<bool> reachable_blocks;
|
||||
std::map<uint32_t, uint32_t> block_labels;
|
||||
std::map<uint32_t, uint32_t> constants;
|
||||
std::map<uint32_t, uint32_t> signed_constants;
|
||||
std::map<uint32_t, uint32_t> float_constants;
|
||||
uint32_t address_memory_array_type = 0;
|
||||
uint32_t ptr_address_memory_array = 0;
|
||||
uint32_t address_memory_variable = 0;
|
||||
uint32_t gds_variable = 0;
|
||||
uint32_t push_constant_array_type = 0;
|
||||
uint32_t push_constant_block_type = 0;
|
||||
uint32_t ptr_push_constant_block = 0;
|
||||
uint32_t ptr_push_constant_uint = 0;
|
||||
uint32_t push_constant_variable = 0;
|
||||
uint32_t vsharp_storage_variable = 0;
|
||||
uint32_t flattened_srt_variable = 0;
|
||||
uint32_t lds_array_type = 0;
|
||||
uint32_t ptr_workgroup_array = 0;
|
||||
uint32_t ptr_workgroup_uint = 0;
|
||||
uint32_t lds_variable = 0;
|
||||
std::array<SampledImageDescriptors, 10> sampled_images;
|
||||
std::array<StorageImageDescriptors, 10> storage_images;
|
||||
uint32_t sampler_type = 0;
|
||||
uint32_t sampler_array_type = 0;
|
||||
uint32_t ptr_uniform_sampler = 0;
|
||||
uint32_t ptr_uniform_sampler_array = 0;
|
||||
uint32_t sampler_variable = 0;
|
||||
uint32_t ptr_image_uint = 0;
|
||||
uint32_t func_type = 0;
|
||||
uint32_t main_func = 0;
|
||||
uint32_t entry_label = 0;
|
||||
uint32_t pixel_valid_mask_variable = 0;
|
||||
bool dispatcher_fallback = false;
|
||||
uint32_t dispatch_pc_variable = 0;
|
||||
uint32_t dispatch_header_label = 0;
|
||||
uint32_t dispatch_select_label = 0;
|
||||
uint32_t dispatch_default_label = 0;
|
||||
uint32_t dispatch_after_switch_label = 0;
|
||||
uint32_t dispatch_continue_label = 0;
|
||||
uint32_t dispatch_merge_label = 0;
|
||||
uint32_t glsl_std450 = 0;
|
||||
uint32_t subgroup_local_invocation_id_variable = 0;
|
||||
uint32_t per_vertex_variable = 0;
|
||||
uint32_t depth_variable = 0;
|
||||
uint32_t sample_mask_variable = 0;
|
||||
bool needs_subgroup_ballot = false;
|
||||
bool needs_subgroup_shuffle = false;
|
||||
bool needs_subgroup_local_invocation_id = false;
|
||||
bool needs_compute_derivatives = false;
|
||||
bool needs_image_gather_extended = false;
|
||||
bool needs_function_lds = false;
|
||||
bool needs_pixel_valid_mask = false;
|
||||
std::vector<RegisterBinding> registers;
|
||||
std::vector<InputBinding> inputs;
|
||||
std::vector<OutputBinding> outputs;
|
||||
std::vector<uint32_t> interface_variables;
|
||||
std::vector<bool> reachable_blocks;
|
||||
std::map<uint32_t, uint32_t> block_labels;
|
||||
std::map<uint32_t, uint32_t> constants;
|
||||
std::map<uint32_t, uint32_t> signed_constants;
|
||||
std::map<uint32_t, uint32_t> float_constants;
|
||||
};
|
||||
|
||||
constexpr uint32_t PsInputOffsetMask = 0x0000001fu;
|
||||
@@ -990,11 +990,6 @@ uint32_t NormalizeFormatComponent(EmitterState& state, const Format::BufferForma
|
||||
uint32_t UnpackTBufferFormat(EmitterState& state, const IR::Instruction& inst,
|
||||
const Format::BufferFormatInfo& info);
|
||||
|
||||
bool EmitTypedTBufferLoad(EmitterState& state, const IR::Instruction& inst,
|
||||
const Format::BufferFormatInfo& info);
|
||||
|
||||
bool EmitFormattedBufferLoad(EmitterState& state, const IR::Instruction& inst);
|
||||
|
||||
uint32_t FormattedBufferDwordStoreComponentCount(Prospero::BufferFormat format,
|
||||
uint32_t opcode_components);
|
||||
|
||||
@@ -1020,6 +1015,9 @@ void EmitBufferLoadSshort(EmitterState& state, const IR::Instruction& inst);
|
||||
|
||||
void EmitBufferLoadDword(EmitterState& state, const IR::Instruction& inst);
|
||||
|
||||
void EmitBufferLoadDwordGroup(EmitterState& state, const IR::Instruction* instructions,
|
||||
uint32_t count);
|
||||
|
||||
void EmitBufferStoreDword(EmitterState& state, const IR::Instruction& inst);
|
||||
|
||||
void EmitFlatLoadUbyte(EmitterState& state, const IR::Instruction& inst);
|
||||
|
||||
@@ -34,16 +34,15 @@ uint32_t EmitDppWriteActiveBool(EmitterState& state, const IR::Operand& dst) {
|
||||
{OpShiftLeftLogical, state.uint_type, bank_bit, ConstantU32(state, 1), bank});
|
||||
state.builder.AddFunction(
|
||||
{OpShiftLeftLogical, state.uint_type, row_bit, ConstantU32(state, 1), row});
|
||||
state.builder.AddFunction({OpBitwiseAnd, state.uint_type, bank_hit,
|
||||
ConstantU32(state, dst.dpp_bank_mask), bank_bit});
|
||||
state.builder.AddFunction(
|
||||
{OpBitwiseAnd, state.uint_type, bank_hit, ConstantU32(state, dst.dpp_bank_mask), bank_bit});
|
||||
state.builder.AddFunction(
|
||||
{OpBitwiseAnd, state.uint_type, row_hit, ConstantU32(state, dst.dpp_row_mask), row_bit});
|
||||
state.builder.AddFunction(
|
||||
{OpINotEqual, state.bool_type, bank_active, bank_hit, ConstantU32(state, 0)});
|
||||
state.builder.AddFunction(
|
||||
{OpINotEqual, state.bool_type, row_active, row_hit, ConstantU32(state, 0)});
|
||||
state.builder.AddFunction(
|
||||
{OpLogicalAnd, state.bool_type, dpp_active, bank_active, row_active});
|
||||
state.builder.AddFunction({OpLogicalAnd, state.bool_type, dpp_active, bank_active, row_active});
|
||||
uint32_t write_active = dpp_active;
|
||||
if (!dst.dpp_bound_ctrl) {
|
||||
const auto target = EmitDppTargetLane(state, dst.dpp_ctrl);
|
||||
@@ -102,7 +101,7 @@ void EmitStoreU32(EmitterState& state, const IR::Operand& dst, uint32_t value) {
|
||||
const auto selected = state.builder.AllocateId();
|
||||
state.builder.AddFunction({OpLoad, state.uint_type, old_value, pointer});
|
||||
state.builder.AddFunction({OpSelect, state.uint_type, selected,
|
||||
EmitDppWriteActiveBool(state, dst), wave_value, old_value});
|
||||
EmitDppWriteActiveBool(state, dst), wave_value, old_value});
|
||||
state.builder.AddFunction({OpStore, pointer, selected});
|
||||
return;
|
||||
}
|
||||
@@ -131,8 +130,7 @@ uint32_t EmitNotEqualZeroBool(EmitterState& state, uint32_t value) {
|
||||
uint32_t EmitSelectU32Value(EmitterState& state, uint32_t condition, uint32_t true_value,
|
||||
uint32_t false_value) {
|
||||
const auto ret = state.builder.AllocateId();
|
||||
state.builder.AddFunction(
|
||||
{OpSelect, state.uint_type, ret, condition, true_value, false_value});
|
||||
state.builder.AddFunction({OpSelect, state.uint_type, ret, condition, true_value, false_value});
|
||||
return ret;
|
||||
}
|
||||
|
||||
@@ -212,12 +210,12 @@ bool IsStorageBufferMemoryKind(IR::ResourceKind kind) {
|
||||
|
||||
void EmitStorageBufferOffsets(EmitterState& state) {
|
||||
for (uint32_t i = 0; i < state.program.bindings.buffer_offset_count; i++) {
|
||||
const auto word = EmitShaderDataDwordLoad(
|
||||
state, state.program.bindings.buffer_offset_dword + i / 4u);
|
||||
const auto shift = ConstantU32(state, (i % 4u) * 8u + 2u);
|
||||
const auto word =
|
||||
EmitShaderDataDwordLoad(state, state.program.bindings.buffer_offset_dword + i / 4u);
|
||||
const auto shift = ConstantU32(state, (i % 4u) * 8u + 2u);
|
||||
state.storage_buffer_offsets[i] = EmitBinaryU32(
|
||||
state, OpBitwiseAnd,
|
||||
EmitBinaryU32(state, OpShiftRightLogical, word, shift), ConstantU32(state, 0x3fu));
|
||||
state, OpBitwiseAnd, EmitBinaryU32(state, OpShiftRightLogical, word, shift),
|
||||
ConstantU32(state, 0x3fu));
|
||||
}
|
||||
}
|
||||
|
||||
@@ -329,8 +327,7 @@ uint32_t EmitRelativeAddress(EmitterState& state, const IR::Instruction& inst, u
|
||||
|
||||
uint32_t EmitFlatVirtualAddress(EmitterState& state, const IR::Instruction& inst,
|
||||
uint32_t first_src, uint32_t src_count) {
|
||||
if (inst.memory.resource >= state.resources.addresses.size() ||
|
||||
src_count < 2) {
|
||||
if (inst.memory.resource >= state.resources.addresses.size() || src_count < 2) {
|
||||
ExitDescriptorBindingFailure(state, IR::DescriptorBindingKind::AddressMemory,
|
||||
inst.memory.resource, "flat address snapshot is missing");
|
||||
}
|
||||
@@ -429,20 +426,20 @@ uint32_t EmitStorageBufferObjectPointer(EmitterState& state, const IR::MemoryInf
|
||||
ResourceForDescriptor(state, IR::DescriptorBindingKind::AddressMemory, mem.resource);
|
||||
const auto pointer = state.builder.AllocateId();
|
||||
state.builder.AddFunction({OpAccessChain, state.ptr_storage_buffer, pointer,
|
||||
state.address_memory_variable,
|
||||
ConstantU32(state, binding.array_index)});
|
||||
state.address_memory_variable,
|
||||
ConstantU32(state, binding.array_index)});
|
||||
return pointer;
|
||||
}
|
||||
const auto binding = StorageBufferBindingForMemory(state, mem, use_pc);
|
||||
const auto pointer = state.builder.AllocateId();
|
||||
state.builder.AddFunction({OpAccessChain, state.ptr_storage_buffer, pointer,
|
||||
state.storage_buffer_variable,
|
||||
ConstantU32(state, binding.array_index)});
|
||||
state.storage_buffer_variable,
|
||||
ConstantU32(state, binding.array_index)});
|
||||
return pointer;
|
||||
}
|
||||
|
||||
uint32_t EmitStorageBufferElementInBounds(EmitterState& state, const IR::MemoryInfo& mem,
|
||||
uint32_t index, uint32_t use_pc) {
|
||||
uint32_t index, uint32_t use_pc) {
|
||||
index = EmitStorageBufferIndex(state, mem, index, use_pc);
|
||||
const auto object = EmitStorageBufferObjectPointer(state, mem, use_pc);
|
||||
const auto length = state.builder.AllocateId();
|
||||
@@ -453,7 +450,7 @@ uint32_t EmitStorageBufferElementInBounds(EmitterState& state, const IR::MemoryI
|
||||
}
|
||||
|
||||
uint32_t EmitStorageBufferElementPointer(EmitterState& state, const IR::MemoryInfo& mem,
|
||||
uint32_t index, uint32_t use_pc) {
|
||||
uint32_t index, uint32_t use_pc) {
|
||||
index = EmitStorageBufferIndex(state, mem, index, use_pc);
|
||||
if (IsFlatMemoryKind(mem.kind)) {
|
||||
if (state.address_memory_variable == 0) {
|
||||
@@ -589,9 +586,9 @@ uint32_t EmitMemoryLoadSubDwordValueU32(EmitterState& state, const IR::Instructi
|
||||
const auto left = state.builder.AllocateId();
|
||||
const auto sign_shift = 32u - data_bits;
|
||||
state.builder.AddFunction({OpShiftLeftLogical, state.uint_type, left, masked,
|
||||
ConstantU32(state, sign_shift)});
|
||||
ConstantU32(state, sign_shift)});
|
||||
state.builder.AddFunction({OpShiftRightArithmetic, state.uint_type, value, left,
|
||||
ConstantU32(state, sign_shift)});
|
||||
ConstantU32(state, sign_shift)});
|
||||
}
|
||||
return value;
|
||||
};
|
||||
@@ -659,16 +656,15 @@ void EmitAtomicUpdateU32(EmitterState& state, uint32_t pointer, IR::ResourceKind
|
||||
state.builder.AddFunction({OpBranch, preheader});
|
||||
state.builder.AddFunction({OpLabel, preheader});
|
||||
state.builder.AddFunction({OpAtomicLoad, state.uint_type, initial, pointer,
|
||||
ConstantU32(state, scope),
|
||||
ConstantU32(state, MemorySemanticsNone)});
|
||||
ConstantU32(state, scope), ConstantU32(state, MemorySemanticsNone)});
|
||||
state.builder.AddFunction({OpBranch, header});
|
||||
state.builder.AddFunction({OpLabel, header});
|
||||
state.builder.AddFunction(
|
||||
{OpPhi, state.uint_type, observed, initial, preheader, exchanged, continue_label});
|
||||
const auto desired = desired_value(observed);
|
||||
state.builder.AddFunction({OpAtomicCompareExchange, state.uint_type, exchanged, pointer,
|
||||
ConstantU32(state, scope), ConstantU32(state, MemorySemanticsNone),
|
||||
ConstantU32(state, MemorySemanticsNone), desired, observed});
|
||||
ConstantU32(state, scope), ConstantU32(state, MemorySemanticsNone),
|
||||
ConstantU32(state, MemorySemanticsNone), desired, observed});
|
||||
const auto success = state.builder.AllocateId();
|
||||
state.builder.AddFunction({OpIEqual, state.bool_type, success, exchanged, observed});
|
||||
state.builder.AddFunction({OpLoopMerge, merge, continue_label, LoopControlNone});
|
||||
@@ -793,8 +789,7 @@ uint32_t EmitTBufferBitcastU32ToI32(EmitterState& state, uint32_t value) {
|
||||
uint32_t EmitTBufferCompareU32Constant(EmitterState& state, uint32_t opcode, uint32_t value,
|
||||
uint32_t constant) {
|
||||
const auto ret = state.builder.AllocateId();
|
||||
state.builder.AddFunction(
|
||||
{opcode, state.bool_type, ret, value, ConstantU32(state, constant)});
|
||||
state.builder.AddFunction({opcode, state.bool_type, ret, value, ConstantU32(state, constant)});
|
||||
return ret;
|
||||
}
|
||||
|
||||
@@ -821,7 +816,7 @@ uint32_t EmitExtractFormatFieldU32(EmitterState& state, uint32_t raw_word, uint3
|
||||
const auto signed_word = EmitTBufferBitcastU32ToI32(state, raw_word);
|
||||
const auto extracted = state.builder.AllocateId();
|
||||
state.builder.AddFunction({OpBitFieldSExtract, state.int_type, extracted, signed_word,
|
||||
ConstantU32(state, offset), ConstantU32(state, bits)});
|
||||
ConstantU32(state, offset), ConstantU32(state, bits)});
|
||||
const auto ret = state.builder.AllocateId();
|
||||
state.builder.AddFunction({OpBitcast, state.uint_type, ret, extracted});
|
||||
return ret;
|
||||
@@ -829,7 +824,7 @@ uint32_t EmitExtractFormatFieldU32(EmitterState& state, uint32_t raw_word, uint3
|
||||
|
||||
const auto extracted = state.builder.AllocateId();
|
||||
state.builder.AddFunction({OpBitFieldUExtract, state.uint_type, extracted, raw_word,
|
||||
ConstantU32(state, offset), ConstantU32(state, bits)});
|
||||
ConstantU32(state, offset), ConstantU32(state, bits)});
|
||||
return extracted;
|
||||
}
|
||||
|
||||
@@ -940,7 +935,7 @@ uint32_t NormalizeFormatComponent(EmitterState& state, const Format::BufferForma
|
||||
state.builder.AddFunction(
|
||||
{OpFDiv, state.float_type, normalized, value, ConstantF32Value(state, max_value)});
|
||||
state.builder.AddFunction({OpExtInst, state.float_type, clamped, state.glsl_std450,
|
||||
GlslFMax, normalized, ConstantF32Value(state, -1.0f)});
|
||||
GlslFMax, normalized, ConstantF32Value(state, -1.0f)});
|
||||
return EmitTBufferBitcastF32ToU32(state, clamped);
|
||||
}
|
||||
case Format::ComponentType::Float:
|
||||
@@ -961,18 +956,8 @@ uint32_t UnpackTBufferFormat(EmitterState& state, const IR::Instruction& inst,
|
||||
return NormalizeFormatComponent(state, info, inst.memory.component_index, raw);
|
||||
}
|
||||
|
||||
bool EmitTypedTBufferLoad(EmitterState& state, const IR::Instruction& inst,
|
||||
const Format::BufferFormatInfo& info) {
|
||||
if (!Format::CanUseTypedBufferLoad(info.format)) {
|
||||
return false;
|
||||
}
|
||||
const auto value = EmitMemoryLoadDwordValueU32(state, inst, IR::ResourceKind::Buffer, 0,
|
||||
AddressSourceCount(inst, 0));
|
||||
EmitStoreU32(state, inst.dst, value);
|
||||
return true;
|
||||
}
|
||||
|
||||
bool EmitFormattedBufferLoad(EmitterState& state, const IR::Instruction& inst) {
|
||||
bool EmitFormattedBufferLoadValueU32(EmitterState& state, const IR::Instruction& inst,
|
||||
uint32_t& value) {
|
||||
if (!IsFormattedBufferComponent(inst)) {
|
||||
return false;
|
||||
}
|
||||
@@ -984,18 +969,29 @@ bool EmitFormattedBufferLoad(EmitterState& state, const IR::Instruction& inst) {
|
||||
|
||||
const auto info = Format::GetFormatInfo(format);
|
||||
if (inst.memory.component_index >= info.component_count) {
|
||||
EmitStoreU32(state, inst.dst, ConstantU32(state, 0));
|
||||
value = ConstantU32(state, 0);
|
||||
return true;
|
||||
}
|
||||
|
||||
if (EmitTypedTBufferLoad(state, inst, info)) {
|
||||
if (Format::CanUseTypedBufferLoad(info.format)) {
|
||||
value = EmitMemoryLoadDwordValueU32(state, inst, IR::ResourceKind::Buffer, 0,
|
||||
AddressSourceCount(inst, 0));
|
||||
return true;
|
||||
}
|
||||
|
||||
EmitStoreU32(state, inst.dst, UnpackTBufferFormat(state, inst, info));
|
||||
value = UnpackTBufferFormat(state, inst, info);
|
||||
return true;
|
||||
}
|
||||
|
||||
uint32_t EmitBufferLoadDwordValueU32(EmitterState& state, const IR::Instruction& inst) {
|
||||
uint32_t value = 0;
|
||||
if (EmitFormattedBufferLoadValueU32(state, inst, value)) {
|
||||
return value;
|
||||
}
|
||||
return EmitMemoryLoadDwordValueU32(state, inst, IR::ResourceKind::Buffer, 0,
|
||||
AddressSourceCount(inst, 0));
|
||||
}
|
||||
|
||||
uint32_t FormattedBufferDwordStoreComponentCount(Prospero::BufferFormat format,
|
||||
uint32_t opcode_components) {
|
||||
switch (format) {
|
||||
@@ -1117,8 +1113,8 @@ uint32_t EmitAtomicPointer(EmitterState& state, const IR::Instruction& inst) {
|
||||
StorageImageDescriptorPointer(state, inst.memory.resource, true, inst.pc, view);
|
||||
const auto pointer = state.builder.AllocateId();
|
||||
state.builder.AddFunction({OpImageTexelPointer, state.ptr_image_uint, pointer,
|
||||
image_pointer, EmitImageCoordU32(state, inst, view),
|
||||
ConstantU32(state, 0)});
|
||||
image_pointer, EmitImageCoordU32(state, inst, view),
|
||||
ConstantU32(state, 0)});
|
||||
return pointer;
|
||||
}
|
||||
default: return 0;
|
||||
@@ -1142,8 +1138,8 @@ void EmitAtomicU32(EmitterState& state, const IR::Instruction& inst, uint32_t op
|
||||
EmitStorageBufferElementPointer(state, inst.memory, index, inst.pc);
|
||||
const auto result = state.builder.AllocateId();
|
||||
state.builder.AddFunction({opcode, state.uint_type, result, pointer,
|
||||
ConstantU32(state, ScopeDevice),
|
||||
ConstantU32(state, MemorySemanticsNone), value});
|
||||
ConstantU32(state, ScopeDevice),
|
||||
ConstantU32(state, MemorySemanticsNone), value});
|
||||
EmitDeviceAtomicMemoryBarrier(state);
|
||||
return result;
|
||||
});
|
||||
@@ -1158,8 +1154,8 @@ void EmitAtomicU32(EmitterState& state, const IR::Instruction& inst, uint32_t op
|
||||
const auto pointer = EmitGdsElementPointer(state, index);
|
||||
const auto result = state.builder.AllocateId();
|
||||
state.builder.AddFunction({opcode, state.uint_type, result, pointer,
|
||||
ConstantU32(state, ScopeDevice),
|
||||
ConstantU32(state, MemorySemanticsNone), value});
|
||||
ConstantU32(state, ScopeDevice),
|
||||
ConstantU32(state, MemorySemanticsNone), value});
|
||||
EmitDeviceAtomicMemoryBarrier(state);
|
||||
return result;
|
||||
});
|
||||
@@ -1177,7 +1173,7 @@ void EmitAtomicU32(EmitterState& state, const IR::Instruction& inst, uint32_t op
|
||||
const auto old = state.builder.AllocateId();
|
||||
const auto scope = inst.memory.kind == IR::ResourceKind::Lds ? ScopeWorkgroup : ScopeDevice;
|
||||
state.builder.AddFunction({opcode, state.uint_type, old, pointer, ConstantU32(state, scope),
|
||||
ConstantU32(state, MemorySemanticsNone), value});
|
||||
ConstantU32(state, MemorySemanticsNone), value});
|
||||
if (inst.memory.kind == IR::ResourceKind::StorageImageUint ||
|
||||
inst.memory.kind == IR::ResourceKind::Gds) {
|
||||
EmitDeviceAtomicMemoryBarrier(state);
|
||||
@@ -1199,8 +1195,8 @@ void EmitSLoadDword(EmitterState& state, const IR::Instruction& inst) {
|
||||
{OpShiftRightLogical, state.uint_type, index, address, ConstantU32(state, 2)});
|
||||
const auto object = state.builder.AllocateId();
|
||||
state.builder.AddFunction({OpAccessChain, state.ptr_storage_buffer, object,
|
||||
state.address_memory_variable,
|
||||
ConstantU32(state, binding.array_index)});
|
||||
state.address_memory_variable,
|
||||
ConstantU32(state, binding.array_index)});
|
||||
const auto length = state.builder.AllocateId();
|
||||
const auto in_bounds = state.builder.AllocateId();
|
||||
state.builder.AddFunction({OpArrayLength, state.uint_type, length, object, 0});
|
||||
@@ -1227,8 +1223,8 @@ void EmitLoadSrtDword(EmitterState& state, const IR::Instruction& inst) {
|
||||
const auto pointer = state.builder.AllocateId();
|
||||
const auto value = state.builder.AllocateId();
|
||||
state.builder.AddFunction({OpAccessChain, state.ptr_storage_buffer_uint, pointer,
|
||||
state.flattened_srt_variable, ConstantU32(state, 0),
|
||||
ConstantU32(state, inst.src[0].imm)});
|
||||
state.flattened_srt_variable, ConstantU32(state, 0),
|
||||
ConstantU32(state, inst.src[0].imm)});
|
||||
state.builder.AddFunction({OpLoad, state.uint_type, value, pointer});
|
||||
EmitStoreU32(state, inst.dst, value);
|
||||
}
|
||||
@@ -1262,11 +1258,27 @@ void EmitBufferLoadSshort(EmitterState& state, const IR::Instruction& inst) {
|
||||
}
|
||||
|
||||
void EmitBufferLoadDword(EmitterState& state, const IR::Instruction& inst) {
|
||||
EmitGuardedByExec(
|
||||
state, [&]() { EmitStoreU32(state, inst.dst, EmitBufferLoadDwordValueU32(state, inst)); });
|
||||
}
|
||||
|
||||
void EmitBufferLoadDwordGroup(EmitterState& state, const IR::Instruction* instructions,
|
||||
uint32_t count) {
|
||||
if (instructions == nullptr || count == 0u) {
|
||||
return;
|
||||
}
|
||||
|
||||
EmitGuardedByExec(state, [&]() {
|
||||
if (EmitFormattedBufferLoad(state, inst)) {
|
||||
return;
|
||||
// RDNA VMEM captures every VADDR component before making overlapping VDATA writes
|
||||
// visible. Keep the split IR components instruction-atomic by deferring all stores.
|
||||
std::vector<uint32_t> values;
|
||||
values.reserve(count);
|
||||
for (uint32_t i = 0; i < count; i++) {
|
||||
values.push_back(EmitBufferLoadDwordValueU32(state, instructions[i]));
|
||||
}
|
||||
for (uint32_t i = 0; i < count; i++) {
|
||||
EmitStoreU32(state, instructions[i].dst, values[i]);
|
||||
}
|
||||
EmitMemoryLoadU32(state, inst, IR::ResourceKind::Buffer, 0, AddressSourceCount(inst, 0));
|
||||
});
|
||||
}
|
||||
|
||||
@@ -1385,7 +1397,7 @@ DsCounterAddress EmitAppendConsumeAddress(EmitterState& state, const IR::Instruc
|
||||
state.builder.AddFunction(
|
||||
{OpShiftRightLogical, state.uint_type, index, address, ConstantU32(state, 2)});
|
||||
state.builder.AddFunction({OpULessThan, state.bool_type, in_bounds,
|
||||
ConstantU32(state, inst.memory.offset + 3u), size});
|
||||
ConstantU32(state, inst.memory.offset + 3u), size});
|
||||
return {index, size, in_bounds};
|
||||
}
|
||||
|
||||
@@ -1400,7 +1412,7 @@ uint32_t EmitGdsElementInBounds(EmitterState& state, uint32_t index) {
|
||||
uint32_t EmitGdsElementPointer(EmitterState& state, uint32_t index) {
|
||||
const auto pointer = state.builder.AllocateId();
|
||||
state.builder.AddFunction({OpAccessChain, state.ptr_storage_buffer_uint, pointer,
|
||||
state.gds_variable, ConstantU32(state, 0), index});
|
||||
state.gds_variable, ConstantU32(state, 0), index});
|
||||
return pointer;
|
||||
}
|
||||
|
||||
@@ -1431,7 +1443,7 @@ ExecMaskInfo EmitExecMaskInfo(EmitterState& state) {
|
||||
if (state.per_invocation_masks) {
|
||||
const auto ballot = state.builder.AllocateId();
|
||||
state.builder.AddFunction({OpGroupNonUniformBallot, state.vec4_uint_type, ballot,
|
||||
ConstantU32(state, ScopeSubgroup), EmitExecActiveBool(state)});
|
||||
ConstantU32(state, ScopeSubgroup), EmitExecActiveBool(state)});
|
||||
exec_lo = state.builder.AllocateId();
|
||||
state.builder.AddFunction({OpCompositeExtract, state.uint_type, exec_lo, ballot, 0});
|
||||
if (state.wave_size == 64u) {
|
||||
@@ -1482,28 +1494,27 @@ void EmitDsAppendConsume(EmitterState& state, const IR::Instruction& inst, uint3
|
||||
const auto do_atomic = state.builder.AllocateId();
|
||||
state.builder.AddFunction({OpIEqual, state.bool_type, first_lane, subid, exec.first_lane});
|
||||
const auto first_active = EmitLogicalAndBool(state, first_lane, exec.any_active);
|
||||
state.builder.AddFunction(
|
||||
{OpLogicalAnd, state.bool_type, do_atomic, first_active, in_bounds});
|
||||
state.builder.AddFunction({OpLogicalAnd, state.bool_type, do_atomic, first_active, in_bounds});
|
||||
|
||||
const auto atomic_value = EmitValueOrZeroIfCondition(state, do_atomic, [&]() {
|
||||
const auto pointer = gds ? EmitGdsElementPointer(state, address.index)
|
||||
: EmitLdsElementPointer(state, address.index);
|
||||
const auto result = state.builder.AllocateId();
|
||||
state.builder.AddFunction({atomic_opcode, state.uint_type, result, pointer,
|
||||
ConstantU32(state, gds ? ScopeDevice : ScopeWorkgroup),
|
||||
ConstantU32(state, MemorySemanticsNone), exec.active_count});
|
||||
ConstantU32(state, gds ? ScopeDevice : ScopeWorkgroup),
|
||||
ConstantU32(state, MemorySemanticsNone), exec.active_count});
|
||||
if (gds) {
|
||||
EmitDeviceAtomicMemoryBarrier(state);
|
||||
} else {
|
||||
const auto semantics = MemorySemanticsAcquireRelease | MemorySemanticsWorkgroupMemory;
|
||||
state.builder.AddFunction({OpMemoryBarrier, ConstantU32(state, ScopeWorkgroup),
|
||||
ConstantU32(state, semantics)});
|
||||
ConstantU32(state, semantics)});
|
||||
}
|
||||
return result;
|
||||
});
|
||||
const auto broadcast = state.builder.AllocateId();
|
||||
state.builder.AddFunction({OpGroupNonUniformShuffle, state.uint_type, broadcast,
|
||||
ConstantU32(state, ScopeSubgroup), atomic_value, exec.first_lane});
|
||||
ConstantU32(state, ScopeSubgroup), atomic_value, exec.first_lane});
|
||||
const auto value = EmitSelectU32Value(state, exec.any_active, broadcast, ConstantU32(state, 0));
|
||||
EmitStoreU32(state, inst.dst, value);
|
||||
}
|
||||
@@ -1523,8 +1534,8 @@ void EmitDsFloatMinMaxF32(EmitterState& state, const IR::Instruction& inst, bool
|
||||
const auto value_u32 = state.builder.AllocateId();
|
||||
state.builder.AddFunction({OpBitcast, state.float_type, old_f32, old_u32});
|
||||
state.builder.AddFunction({max_value ? OpFOrdGreaterThan : OpFOrdLessThan,
|
||||
state.bool_type, store_src, max_value ? old_f32 : cmp_f32,
|
||||
max_value ? cmp_f32 : old_f32});
|
||||
state.bool_type, store_src, max_value ? old_f32 : cmp_f32,
|
||||
max_value ? cmp_f32 : old_f32});
|
||||
state.builder.AddFunction(
|
||||
{OpSelect, state.float_type, value_f32, store_src, data_f32, old_f32});
|
||||
state.builder.AddFunction({OpBitcast, state.uint_type, value_u32, value_f32});
|
||||
@@ -1575,14 +1586,13 @@ uint32_t EmitDsSwizzleTargetLane(EmitterState& state, uint32_t subid, uint32_t c
|
||||
const auto xored = state.builder.AllocateId();
|
||||
const auto base = state.builder.AllocateId();
|
||||
const auto target = state.builder.AllocateId();
|
||||
state.builder.AddFunction(
|
||||
{OpBitwiseAnd, state.uint_type, lane, subid, ConstantU32(state, 31)});
|
||||
state.builder.AddFunction({OpBitwiseAnd, state.uint_type, lane, subid, ConstantU32(state, 31)});
|
||||
state.builder.AddFunction(
|
||||
{OpBitwiseAnd, state.uint_type, masked, lane, ConstantU32(state, control & 0x1fu)});
|
||||
state.builder.AddFunction(
|
||||
{OpBitwiseOr, state.uint_type, ored, masked, ConstantU32(state, (control >> 5u) & 0x1fu)});
|
||||
state.builder.AddFunction({OpBitwiseXor, state.uint_type, xored, ored,
|
||||
ConstantU32(state, (control >> 10u) & 0x1fu)});
|
||||
state.builder.AddFunction(
|
||||
{OpBitwiseXor, state.uint_type, xored, ored, ConstantU32(state, (control >> 10u) & 0x1fu)});
|
||||
state.builder.AddFunction(
|
||||
{OpBitwiseAnd, state.uint_type, base, subid, ConstantU32(state, 0xffffffe0u)});
|
||||
state.builder.AddFunction({OpBitwiseOr, state.uint_type, target, base, xored});
|
||||
@@ -1596,7 +1606,7 @@ void EmitDsSwizzleB32(EmitterState& state, const IR::Instruction& inst) {
|
||||
const auto target = EmitDsSwizzleTargetLane(state, subid, control);
|
||||
const auto value = state.builder.AllocateId();
|
||||
state.builder.AddFunction({OpGroupNonUniformShuffle, state.uint_type, value,
|
||||
ConstantU32(state, ScopeSubgroup), source, target});
|
||||
ConstantU32(state, ScopeSubgroup), source, target});
|
||||
const auto exec_active = EmitLaneIndexActiveBool(state, target);
|
||||
const auto subgroup_active = EmitSubgroupLaneActiveBool(state, target);
|
||||
const auto source_active = state.builder.AllocateId();
|
||||
|
||||
@@ -784,10 +784,10 @@ private:
|
||||
if (incoming.empty()) {
|
||||
return ScalarProvenance::Undefined;
|
||||
}
|
||||
if (incoming.size() == 1) {
|
||||
return incoming[0];
|
||||
}
|
||||
if (*phi == ScalarProvenance::Undefined) {
|
||||
if (incoming.size() == 1) {
|
||||
return incoming[0];
|
||||
}
|
||||
*phi = AddValue({ScalarValueOp::Phi, block.start_pc});
|
||||
}
|
||||
m_graph.values[*phi].phi_args = std::move(incoming);
|
||||
|
||||
@@ -528,6 +528,42 @@ public:
|
||||
return false;
|
||||
}
|
||||
|
||||
uint64_t ClampRangeSize(uint64_t virtual_addr, uint64_t size) {
|
||||
Common::LockGuard lock(m_mutex);
|
||||
|
||||
if (virtual_addr == 0 || size == 0 || size > UINT64_MAX - virtual_addr) {
|
||||
return 0;
|
||||
}
|
||||
|
||||
auto vma = std::upper_bound(
|
||||
m_ranges.begin(), m_ranges.end(), virtual_addr,
|
||||
[](uint64_t value, const Range& range) { return value < range.start; });
|
||||
if (vma == m_ranges.begin()) {
|
||||
return 0;
|
||||
}
|
||||
--vma;
|
||||
|
||||
const auto vma_end = End(vma->start, vma->size);
|
||||
if (virtual_addr < vma->start || virtual_addr >= vma_end ||
|
||||
!IsCommittedRangeType(vma->type)) {
|
||||
return 0;
|
||||
}
|
||||
|
||||
uint64_t clamped_size = std::min(size, vma_end - virtual_addr);
|
||||
uint64_t expected = virtual_addr + clamped_size;
|
||||
++vma;
|
||||
|
||||
while (vma != m_ranges.end() && vma->start == expected && IsCommittedRangeType(vma->type) &&
|
||||
clamped_size < size) {
|
||||
const auto chunk = std::min(size - clamped_size, vma->size);
|
||||
clamped_size += chunk;
|
||||
expected += chunk;
|
||||
++vma;
|
||||
}
|
||||
|
||||
return clamped_size;
|
||||
}
|
||||
|
||||
uint64_t CountPageTableEntries(bool gpu) {
|
||||
Common::LockGuard lock(m_mutex);
|
||||
|
||||
@@ -884,6 +920,23 @@ bool TryReadBacking(uint64_t vaddr, void* data, uint64_t size) {
|
||||
g_direct_memory_backing->TryReadBacking(vaddr, data, size);
|
||||
}
|
||||
|
||||
uint64_t ClampRangeSize(uint64_t vaddr, uint64_t size) {
|
||||
EXIT_IF(g_virtual_ranges == nullptr);
|
||||
|
||||
const auto clamped_size = g_virtual_ranges->ClampRangeSize(vaddr, size);
|
||||
if (clamped_size == 0) {
|
||||
EXIT("Memory: attempted to access invalid address 0x%016" PRIx64 " with size 0x%016" PRIx64
|
||||
"\n",
|
||||
vaddr, size);
|
||||
}
|
||||
if (clamped_size != size) {
|
||||
LOGF("Memory: clamped buffer range addr=0x%016" PRIx64 " size=0x%016" PRIx64
|
||||
" to 0x%016" PRIx64 "\n",
|
||||
vaddr, size, clamped_size);
|
||||
}
|
||||
return clamped_size;
|
||||
}
|
||||
|
||||
void WriteBacking(uint64_t vaddr, const void* data, uint64_t size) noexcept {
|
||||
if (!TryWriteBacking(vaddr, data, size)) {
|
||||
EXIT("Memory: required direct-backing write failed, addr=0x%016" PRIx64
|
||||
|
||||
+8
-7
@@ -99,13 +99,14 @@ struct KernelMemoryPoolBlockStats {
|
||||
static_assert(sizeof(KernelMemoryPoolBlockStats) == 16,
|
||||
"KernelMemoryPoolBlockStats struct size is incorrect");
|
||||
|
||||
void RegisterCallbacks(callback_func_t alloc_func, callback_func_t free_func);
|
||||
void SetFlexibleMemorySize(uint64_t size);
|
||||
bool TryWriteBacking(uint64_t vaddr, const void* data, uint64_t size);
|
||||
bool TryReadBacking(uint64_t vaddr, void* data, uint64_t size);
|
||||
void WriteBacking(uint64_t vaddr, const void* data, uint64_t size) noexcept;
|
||||
void InvalidateMemory(uint64_t vaddr, uint64_t size);
|
||||
void InstallGpuResources(Graphics::GpuResourceManager* resources) noexcept;
|
||||
void RegisterCallbacks(callback_func_t alloc_func, callback_func_t free_func);
|
||||
void SetFlexibleMemorySize(uint64_t size);
|
||||
bool TryWriteBacking(uint64_t vaddr, const void* data, uint64_t size);
|
||||
bool TryReadBacking(uint64_t vaddr, void* data, uint64_t size);
|
||||
[[nodiscard]] uint64_t ClampRangeSize(uint64_t vaddr, uint64_t size);
|
||||
void WriteBacking(uint64_t vaddr, const void* data, uint64_t size) noexcept;
|
||||
void InvalidateMemory(uint64_t vaddr, uint64_t size);
|
||||
void InstallGpuResources(Graphics::GpuResourceManager* resources) noexcept;
|
||||
[[nodiscard]] bool HandleGpuFault(Graphics::PageFaultAccess access, uint64_t fault_vaddr) noexcept;
|
||||
|
||||
int KYTY_SYSV_ABI KernelMapNamedFlexibleMemory(void** addr_in_out, size_t len, int prot, int flags,
|
||||
|
||||
+56
-22
@@ -34,7 +34,7 @@ namespace LibNet {
|
||||
|
||||
LIB_VERSION("Net", 1, "Net", 1, 1);
|
||||
|
||||
static thread_local int g_net_errno = 0;
|
||||
static thread_local int g_net_errno = 0;
|
||||
static constexpr uint32_t g_in6addr_any[4] {};
|
||||
|
||||
namespace Net = Network::Net;
|
||||
@@ -654,6 +654,8 @@ namespace LibHttp2 {
|
||||
LIB_VERSION("Http2", 1, "Http2", 1, 1);
|
||||
|
||||
constexpr int HTTP2_ERROR_INVALID_ID = -2122641152; /* 0x817B1100 */
|
||||
constexpr int HTTP2_ERROR_BEFORE_SEND = -2122641307; /* 0x817B1065 */
|
||||
constexpr int HTTP2_ERROR_TIMEOUT = -2122641304; /* 0x817B1068 */
|
||||
constexpr int HTTP2_ERROR_NULL_POINTER = -2122640859; /* 0x817B1225 */
|
||||
|
||||
struct Http2Options {
|
||||
@@ -694,14 +696,14 @@ struct Http2Request {
|
||||
std::string url;
|
||||
uint64_t content_length = 0;
|
||||
std::vector<std::pair<std::string, std::string>> headers;
|
||||
bool sent = false;
|
||||
int status_code = 204;
|
||||
std::string response_headers = "HTTP/2 204 No Content\r\n\r\n";
|
||||
std::string response_body;
|
||||
size_t read_offset = 0;
|
||||
int async_result = 0;
|
||||
int async_event = 0;
|
||||
Http2Options options;
|
||||
int send_result = HTTP2_ERROR_BEFORE_SEND;
|
||||
int status_code = 0;
|
||||
std::string response_headers;
|
||||
std::string response_body;
|
||||
size_t read_offset = 0;
|
||||
int async_result = HTTP2_ERROR_BEFORE_SEND;
|
||||
int async_event = 0;
|
||||
Http2Options options;
|
||||
};
|
||||
|
||||
struct Http2AsyncResult {
|
||||
@@ -1114,9 +1116,9 @@ static int KYTY_SYSV_ABI Http2SendRequest(int req_id, const void* post_data, siz
|
||||
return HTTP2_ERROR_INVALID_ID;
|
||||
}
|
||||
|
||||
request->second.sent = true;
|
||||
request->second.send_result = HTTP2_ERROR_TIMEOUT;
|
||||
|
||||
return 0;
|
||||
return request->second.send_result;
|
||||
}
|
||||
|
||||
static int KYTY_SYSV_ABI Http2SendRequestAsync(int req_id, const void* post_data, size_t size,
|
||||
@@ -1136,8 +1138,8 @@ static int KYTY_SYSV_ABI Http2SendRequestAsync(int req_id, const void* post_data
|
||||
return HTTP2_ERROR_INVALID_ID;
|
||||
}
|
||||
|
||||
request->second.sent = true;
|
||||
request->second.async_result = 0;
|
||||
request->second.send_result = HTTP2_ERROR_TIMEOUT;
|
||||
request->second.async_result = request->second.send_result;
|
||||
request->second.async_event = 0;
|
||||
|
||||
return 0;
|
||||
@@ -1159,11 +1161,10 @@ static int KYTY_SYSV_ABI Http2WaitAsync(int req_id, Http2AsyncResult* result, ui
|
||||
return HTTP2_ERROR_INVALID_ID;
|
||||
}
|
||||
|
||||
request->second.sent = true;
|
||||
*result = {};
|
||||
result->event_type = request->second.async_event;
|
||||
result->req_id = req_id;
|
||||
result->result = request->second.async_result;
|
||||
*result = {};
|
||||
result->event_type = request->second.async_event;
|
||||
result->req_id = req_id;
|
||||
result->result = request->second.async_result;
|
||||
|
||||
return 0;
|
||||
}
|
||||
@@ -1178,13 +1179,19 @@ static int KYTY_SYSV_ABI Http2GetStatusCode(int req_id, int* status_code) {
|
||||
return HTTP2_ERROR_NULL_POINTER;
|
||||
}
|
||||
|
||||
*status_code = 0;
|
||||
|
||||
auto request = g_http2_requests.find(req_id);
|
||||
if (request == g_http2_requests.end()) {
|
||||
return HTTP2_ERROR_INVALID_ID;
|
||||
}
|
||||
|
||||
*status_code = request->second.status_code;
|
||||
const int send_result = request->second.send_result;
|
||||
if (send_result != 0) {
|
||||
return send_result;
|
||||
}
|
||||
|
||||
*status_code = request->second.status_code;
|
||||
return 0;
|
||||
}
|
||||
|
||||
@@ -1200,14 +1207,21 @@ static int KYTY_SYSV_ABI Http2GetResponseContentLength(int req_id, int* result,
|
||||
return HTTP2_ERROR_NULL_POINTER;
|
||||
}
|
||||
|
||||
*result = 0;
|
||||
*content_length = 0;
|
||||
|
||||
auto request = g_http2_requests.find(req_id);
|
||||
if (request == g_http2_requests.end()) {
|
||||
return HTTP2_ERROR_INVALID_ID;
|
||||
}
|
||||
|
||||
*result = 0; // SCE_HTTP2_CONTENTLEN_EXIST
|
||||
*content_length = request->second.response_body.size();
|
||||
const int send_result = request->second.send_result;
|
||||
if (send_result != 0) {
|
||||
*result = -1;
|
||||
return send_result;
|
||||
}
|
||||
|
||||
*content_length = request->second.response_body.size();
|
||||
return 0;
|
||||
}
|
||||
|
||||
@@ -1223,14 +1237,21 @@ static int KYTY_SYSV_ABI Http2GetAllResponseHeaders(int req_id, char** header,
|
||||
return HTTP2_ERROR_NULL_POINTER;
|
||||
}
|
||||
|
||||
*header = nullptr;
|
||||
*header_size = 0;
|
||||
|
||||
auto request = g_http2_requests.find(req_id);
|
||||
if (request == g_http2_requests.end()) {
|
||||
return HTTP2_ERROR_INVALID_ID;
|
||||
}
|
||||
|
||||
const int send_result = request->second.send_result;
|
||||
if (send_result != 0) {
|
||||
return send_result;
|
||||
}
|
||||
|
||||
*header = const_cast<char*>(request->second.response_headers.c_str());
|
||||
*header_size = request->second.response_headers.size();
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
@@ -1250,6 +1271,11 @@ static int KYTY_SYSV_ABI Http2ReadData(int req_id, void* data, size_t size) {
|
||||
return HTTP2_ERROR_INVALID_ID;
|
||||
}
|
||||
|
||||
const int send_result = request->second.send_result;
|
||||
if (send_result != 0) {
|
||||
return send_result;
|
||||
}
|
||||
|
||||
const auto& body = request->second.response_body;
|
||||
const auto remaining =
|
||||
request->second.read_offset < body.size() ? body.size() - request->second.read_offset : 0;
|
||||
@@ -1280,6 +1306,13 @@ static int KYTY_SYSV_ABI Http2ReadDataAsync(int req_id, void* data, size_t size,
|
||||
return HTTP2_ERROR_INVALID_ID;
|
||||
}
|
||||
|
||||
const int send_result = request->second.send_result;
|
||||
if (send_result != 0) {
|
||||
request->second.async_result = send_result;
|
||||
request->second.async_event = 1;
|
||||
return 0;
|
||||
}
|
||||
|
||||
const auto& body = request->second.response_body;
|
||||
const auto remaining =
|
||||
request->second.read_offset < body.size() ? body.size() - request->second.read_offset : 0;
|
||||
@@ -1398,6 +1431,7 @@ LIB_DEFINE(InitNet_1_NpManager) {
|
||||
LIB_FUNC("O80NrhUOPGY", NpManager::NpCheckPremium);
|
||||
LIB_FUNC("eQH7nWPcAgc", NpManager::NpGetState);
|
||||
LIB_FUNC("e-ZuhGEoeC4", NpManager::NpGetNpReachabilityState);
|
||||
LIB_FUNC("Oad3rvY-NJQ", NpManager::NpHasSignedUp);
|
||||
}
|
||||
|
||||
} // namespace LibNpManager
|
||||
|
||||
+19
-6
@@ -19,7 +19,7 @@
|
||||
|
||||
// POSIX uses plain int file descriptors for sockets; provide the Winsock spellings
|
||||
// the shared (non-guarded) code paths reference.
|
||||
using SOCKET = int;
|
||||
using SOCKET = int;
|
||||
static constexpr SOCKET INVALID_SOCKET = -1;
|
||||
#endif
|
||||
|
||||
@@ -820,10 +820,10 @@ struct NetEtherAddr {
|
||||
};
|
||||
|
||||
#if defined(_WIN32)
|
||||
using NativeSocket = SOCKET;
|
||||
using NativeSocket = SOCKET;
|
||||
static constexpr NativeSocket INVALID_NATIVE_SOCKET = INVALID_SOCKET;
|
||||
#else
|
||||
using NativeSocket = int;
|
||||
using NativeSocket = int;
|
||||
static constexpr NativeSocket INVALID_NATIVE_SOCKET = -1;
|
||||
#endif
|
||||
|
||||
@@ -1738,7 +1738,8 @@ int KYTY_SYSV_ABI Accept(int s, void* addr, uint32_t* addrlen) {
|
||||
#if defined(_WIN32)
|
||||
sockaddr_storage host_addr {};
|
||||
int host_addrlen = sizeof(host_addr);
|
||||
NativeSocket accepted = ::accept(socket, reinterpret_cast<sockaddr*>(&host_addr), &host_addrlen);
|
||||
NativeSocket accepted =
|
||||
::accept(socket, reinterpret_cast<sockaddr*>(&host_addr), &host_addrlen);
|
||||
if (accepted == INVALID_NATIVE_SOCKET) {
|
||||
return SetPosixSocketError();
|
||||
}
|
||||
@@ -3753,8 +3754,6 @@ int KYTY_SYSV_ABI NpGetState(int user_id, uint32_t* state) {
|
||||
int KYTY_SYSV_ABI NpGetNpReachabilityState(int user_id, uint32_t* state) {
|
||||
PRINT_NAME();
|
||||
|
||||
constexpr int np_error_invalid_argument = -2141913085; /* 0x80550003 */
|
||||
|
||||
if (state == nullptr) {
|
||||
return np_error_invalid_argument;
|
||||
}
|
||||
@@ -3767,6 +3766,20 @@ int KYTY_SYSV_ABI NpGetNpReachabilityState(int user_id, uint32_t* state) {
|
||||
return OK;
|
||||
}
|
||||
|
||||
int KYTY_SYSV_ABI NpHasSignedUp(int user_id, bool* has_signed_up) {
|
||||
PRINT_NAME();
|
||||
|
||||
if (has_signed_up == nullptr) {
|
||||
return np_error_invalid_argument;
|
||||
}
|
||||
|
||||
LOGF("\t user_id = %d\n", user_id);
|
||||
|
||||
*has_signed_up = false;
|
||||
|
||||
return OK;
|
||||
}
|
||||
|
||||
} // namespace NpManager
|
||||
|
||||
} // namespace Libs::Network
|
||||
|
||||
@@ -184,6 +184,7 @@ int KYTY_SYSV_ABI NpCheckPremium(int req_id, const NpCheckPremiumParameter* par
|
||||
NpCheckPremiumResult* result);
|
||||
int KYTY_SYSV_ABI NpGetState(int user_id, uint32_t* state);
|
||||
int KYTY_SYSV_ABI NpGetNpReachabilityState(int user_id, uint32_t* state);
|
||||
int KYTY_SYSV_ABI NpHasSignedUp(int user_id, bool* has_signed_up);
|
||||
|
||||
} // namespace NpManager
|
||||
|
||||
|
||||
@@ -186,6 +186,39 @@ void TestCfgPhi() {
|
||||
"acyclic control-flow descriptor phi was not classified dynamic");
|
||||
}
|
||||
|
||||
void TestNestedLoopPhiConvergence() {
|
||||
Program program;
|
||||
program.blocks.resize(4);
|
||||
program.blocks[0].predecessors = {1};
|
||||
program.blocks[0].successors = {1};
|
||||
program.blocks[1].predecessors = {0, 3};
|
||||
program.blocks[1].successors = {0, 2};
|
||||
program.blocks[2].predecessors = {1};
|
||||
program.blocks[2].successors = {3};
|
||||
program.blocks[3].predecessors = {2};
|
||||
program.blocks[3].successors = {1};
|
||||
Instruction increment;
|
||||
increment.op = Opcode::IAddU32;
|
||||
increment.dst = Sgpr(0);
|
||||
increment.src[0] = Sgpr(0);
|
||||
increment.src[1] = Imm(1);
|
||||
increment.src_count = 2;
|
||||
program.blocks[0].instructions = {increment};
|
||||
program.blocks[2].instructions = {BufferUse(4, 0)};
|
||||
|
||||
std::string error;
|
||||
Check(BuildScalarProvenance(program, &error), error.c_str());
|
||||
const auto* source =
|
||||
GetDescriptorSource(program, program.blocks[2].instructions[0].memory.resource_source);
|
||||
Check(source != nullptr, "nested-loop descriptor source was not attached");
|
||||
const auto value_id = source->dwords[0];
|
||||
const auto& phi = Value(program, value_id);
|
||||
Check(phi.op == ScalarValueOp::Phi && phi.phi_args.size() == 2 &&
|
||||
((phi.phi_args[0] == value_id && phi.phi_args[1] != value_id) ||
|
||||
(phi.phi_args[1] == value_id && phi.phi_args[0] != value_id)),
|
||||
"nested loop did not retain its recursive scalar provenance phi");
|
||||
}
|
||||
|
||||
void TestDiamondReadPathsAreDynamic() {
|
||||
std::array<uint32_t, 1> left = {0x11111111u};
|
||||
std::array<uint32_t, 1> right = {0x22222222u};
|
||||
@@ -1045,6 +1078,7 @@ int main() {
|
||||
try {
|
||||
TestPerUseDescriptorDefinitions();
|
||||
TestCfgPhi();
|
||||
TestNestedLoopPhiConvergence();
|
||||
TestDiamondReadPathsAreDynamic();
|
||||
TestEquivalentConstantPhiIsStatic();
|
||||
TestWideMoveInvalidatesAndCopiesBothDwords();
|
||||
|
||||
@@ -4772,6 +4772,52 @@ public:
|
||||
"successive near-capacity image transfers replaced the shared "
|
||||
"download buffer");
|
||||
|
||||
constexpr uint64_t tile_alias_offset = 0x2000000;
|
||||
constexpr uint64_t tile_alias_size = 0x400000;
|
||||
constexpr uint32_t tile_alias_extent = 1024;
|
||||
std::memset(memory + tile_alias_offset, 0,
|
||||
static_cast<size_t>(tile_alias_size));
|
||||
auto render_target_alias = MakeLinearDesc(
|
||||
base + tile_alias_offset, tile_alias_size,
|
||||
vk::Format::eR8G8B8A8Unorm,
|
||||
Prospero::GpuEnumValue(Prospero::BufferFormat::k8_8_8_8UNorm),
|
||||
Prospero::ImageType::kColor2D,
|
||||
{tile_alias_extent, tile_alias_extent, 1}, 1, 4, 1);
|
||||
render_target_alias.type = BindingType::Storage;
|
||||
render_target_alias.info.tile_mode =
|
||||
Prospero::GpuEnumValue(Prospero::TileMode::kRenderTarget);
|
||||
render_target_alias.view_info.usage =
|
||||
vk::ImageUsageFlagBits::eStorage;
|
||||
const auto render_target_alias_image =
|
||||
texture_cache.FindImage(render_target_alias);
|
||||
|
||||
auto standard_4kb_alias = render_target_alias;
|
||||
standard_4kb_alias.type = BindingType::Texture;
|
||||
standard_4kb_alias.info.tile_mode =
|
||||
Prospero::GpuEnumValue(Prospero::TileMode::kStandard4KB);
|
||||
standard_4kb_alias.view_info.usage =
|
||||
vk::ImageUsageFlagBits::eSampled;
|
||||
const auto standard_4kb_alias_image =
|
||||
texture_cache.FindImage(standard_4kb_alias);
|
||||
auto repeated_standard_4kb_alias = standard_4kb_alias;
|
||||
const auto repeated_standard_4kb_alias_image =
|
||||
texture_cache.FindImage(repeated_standard_4kb_alias);
|
||||
Require(
|
||||
name, "equal-size tile-mode alias",
|
||||
render_target_alias_image && standard_4kb_alias_image &&
|
||||
standard_4kb_alias_image != render_target_alias_image &&
|
||||
repeated_standard_4kb_alias_image ==
|
||||
standard_4kb_alias_image &&
|
||||
texture_cache.GetImage(render_target_alias_image)
|
||||
.info.tile_mode ==
|
||||
Prospero::GpuEnumValue(
|
||||
Prospero::TileMode::kRenderTarget) &&
|
||||
texture_cache.GetImage(standard_4kb_alias_image)
|
||||
.info.tile_mode ==
|
||||
Prospero::GpuEnumValue(
|
||||
Prospero::TileMode::kStandard4KB),
|
||||
"equal address/size lookup reused an incompatible tiled backing");
|
||||
|
||||
for (auto &output : ms_observer_outputs) {
|
||||
DestroyBuffer(&output);
|
||||
}
|
||||
@@ -12083,6 +12129,84 @@ TestCase BufferLoadVariants() {
|
||||
O::BufferLoadDwordx4, O::VMovB32, O::BufferStoreDword, O::SEndpgm}};
|
||||
}
|
||||
|
||||
TestCase BufferLoadDwordx4SnapshotsOverlappingAddress() {
|
||||
using O = ShaderOpcode;
|
||||
|
||||
std::vector<u32> code;
|
||||
AppendVMovU32(&code, 21, 0);
|
||||
AppendVMovU32(&code, 22, 0);
|
||||
code.push_back(EncodeMubuf0(0x0eu, 0, true, true));
|
||||
code.push_back(EncodeMubuf1(21, 0, 21));
|
||||
for (u32 i = 0; i < 4; i++) {
|
||||
AppendStoreVgpr(&code, 21 + i, 4 + i);
|
||||
}
|
||||
AppendEnd(&code);
|
||||
|
||||
TestCase test;
|
||||
test.name = "BufferLoadDwordx4SnapshotsOverlappingAddress";
|
||||
test.code = std::move(code);
|
||||
test.initial = {0x11111111u, 0x22222222u, 0x33333333u, 0x44444444u,
|
||||
0, 0, 0, 0};
|
||||
test.expected = {0x11111111u, 0x22222222u, 0x33333333u, 0x44444444u,
|
||||
0x11111111u, 0x22222222u, 0x33333333u, 0x44444444u};
|
||||
test.opcodes = {O::VMovB32, O::BufferLoadDwordx4, O::BufferStoreDword,
|
||||
O::SEndpgm};
|
||||
test.user_data = MakeStructuredStorageBufferData(16, 2);
|
||||
test.has_user_data = true;
|
||||
return test;
|
||||
}
|
||||
|
||||
TestCase BufferLoadDwordx2SnapshotsOverlappingAddress() {
|
||||
using O = ShaderOpcode;
|
||||
|
||||
std::vector<u32> code;
|
||||
AppendVMovU32(&code, 21, 0);
|
||||
AppendVMovU32(&code, 22, 0);
|
||||
code.push_back(EncodeMubuf0(0x0du, 0, true, true));
|
||||
code.push_back(EncodeMubuf1(21, 0, 21));
|
||||
for (u32 i = 0; i < 2; i++) {
|
||||
AppendStoreVgpr(&code, 21 + i, 2 + i);
|
||||
}
|
||||
AppendEnd(&code);
|
||||
|
||||
TestCase test;
|
||||
test.name = "BufferLoadDwordx2SnapshotsOverlappingAddress";
|
||||
test.code = std::move(code);
|
||||
test.initial = {0x11111111u, 0x22222222u, 0, 0};
|
||||
test.expected = {0x11111111u, 0x22222222u, 0x11111111u, 0x22222222u};
|
||||
test.opcodes = {O::VMovB32, O::BufferLoadDwordx2, O::BufferStoreDword,
|
||||
O::SEndpgm};
|
||||
test.user_data = MakeStructuredStorageBufferData(8, 2);
|
||||
test.has_user_data = true;
|
||||
return test;
|
||||
}
|
||||
|
||||
TestCase BufferLoadDwordx3SnapshotsOverlappingAddress() {
|
||||
using O = ShaderOpcode;
|
||||
|
||||
std::vector<u32> code;
|
||||
AppendVMovU32(&code, 21, 0);
|
||||
AppendVMovU32(&code, 22, 0);
|
||||
code.push_back(EncodeMubuf0(0x0fu, 0, true, true));
|
||||
code.push_back(EncodeMubuf1(21, 0, 21));
|
||||
for (u32 i = 0; i < 3; i++) {
|
||||
AppendStoreVgpr(&code, 21 + i, 3 + i);
|
||||
}
|
||||
AppendEnd(&code);
|
||||
|
||||
TestCase test;
|
||||
test.name = "BufferLoadDwordx3SnapshotsOverlappingAddress";
|
||||
test.code = std::move(code);
|
||||
test.initial = {0x11111111u, 0x22222222u, 0x33333333u, 0, 0, 0};
|
||||
test.expected = {0x11111111u, 0x22222222u, 0x33333333u,
|
||||
0x11111111u, 0x22222222u, 0x33333333u};
|
||||
test.opcodes = {O::VMovB32, O::BufferLoadDwordx3, O::BufferStoreDword,
|
||||
O::SEndpgm};
|
||||
test.user_data = MakeStructuredStorageBufferData(12, 2);
|
||||
test.has_user_data = true;
|
||||
return test;
|
||||
}
|
||||
|
||||
TestCase BufferStoreVariants() {
|
||||
using O = ShaderOpcode;
|
||||
|
||||
@@ -12151,6 +12275,69 @@ TestCase BufferFormatVariants() {
|
||||
return load;
|
||||
}
|
||||
|
||||
TestCase BufferLoadFormatXyzwSnapshotsOverlappingAddress() {
|
||||
using O = ShaderOpcode;
|
||||
|
||||
std::vector<u32> code;
|
||||
AppendVMovU32(&code, 21, 0);
|
||||
AppendVMovU32(&code, 22, 0);
|
||||
code.push_back(EncodeMubuf0(0x03u, 0, true, true));
|
||||
code.push_back(EncodeMubuf1(21, 0, 21));
|
||||
for (u32 i = 0; i < 4; i++) {
|
||||
AppendStoreVgpr(&code, 21 + i, 4 + i);
|
||||
}
|
||||
AppendEnd(&code);
|
||||
|
||||
TestCase test;
|
||||
test.name = "BufferLoadFormatXyzwSnapshotsOverlappingAddress";
|
||||
test.code = std::move(code);
|
||||
test.initial = {0x3f800000u, 0x40000000u, 0x40400000u, 0x40800000u,
|
||||
0, 0, 0, 0};
|
||||
test.expected = {0x3f800000u, 0x40000000u, 0x40400000u, 0x40800000u,
|
||||
0x3f800000u, 0x40000000u, 0x40400000u, 0x40800000u};
|
||||
test.opcodes = {O::VMovB32, O::BufferLoadFormatXyzw, O::BufferStoreDword,
|
||||
O::SEndpgm};
|
||||
test.user_data = MakeStructuredStorageBufferData(
|
||||
16, 2, false,
|
||||
BufferFormat(Prospero::BufferFormat::k32_32_32_32Float));
|
||||
test.has_user_data = true;
|
||||
return test;
|
||||
}
|
||||
|
||||
TestCase BufferLoadFormatXyzwInactiveExecPreservesOverlappingAddress() {
|
||||
using O = ShaderOpcode;
|
||||
|
||||
std::vector<u32> code;
|
||||
AppendVMovLiteral(&code, 21, 0x11111111u);
|
||||
AppendVMovLiteral(&code, 22, 0x22222222u);
|
||||
AppendVMovLiteral(&code, 23, 0x33333333u);
|
||||
AppendVMovLiteral(&code, 24, 0x44444444u);
|
||||
code.push_back(EncodeSop1(0x04, 126, InlineU32(0)));
|
||||
code.push_back(EncodeMubuf0(0x03u, 0, true, true));
|
||||
code.push_back(EncodeMubuf1(21, 0, 21));
|
||||
code.push_back(EncodeSMovB32(126, InlineU32(1)));
|
||||
code.push_back(EncodeSMovB32(127, InlineU32(0)));
|
||||
for (u32 i = 0; i < 4; i++) {
|
||||
AppendStoreVgpr(&code, 21 + i, 4 + i);
|
||||
}
|
||||
AppendEnd(&code);
|
||||
|
||||
TestCase test;
|
||||
test.name = "BufferLoadFormatXyzwInactiveExecPreservesOverlappingAddress";
|
||||
test.code = std::move(code);
|
||||
test.initial = {0xaaaaaaaa, 0xbbbbbbbb, 0xcccccccc, 0xdddddddd,
|
||||
0, 0, 0, 0};
|
||||
test.expected = {0xaaaaaaaau, 0xbbbbbbbbu, 0xccccccccu, 0xddddddddu,
|
||||
0x11111111u, 0x22222222u, 0x33333333u, 0x44444444u};
|
||||
test.opcodes = {O::VMovB32, O::SMovB64, O::BufferLoadFormatXyzw,
|
||||
O::SMovB32, O::BufferStoreDword, O::SEndpgm};
|
||||
test.user_data = MakeStructuredStorageBufferData(
|
||||
16, 2, false,
|
||||
BufferFormat(Prospero::BufferFormat::k32_32_32_32Float));
|
||||
test.has_user_data = true;
|
||||
return test;
|
||||
}
|
||||
|
||||
TestCase BufferFormatStoreVariants() {
|
||||
using O = ShaderOpcode;
|
||||
|
||||
@@ -12459,6 +12646,63 @@ TestCase TBufferLoadVariants() {
|
||||
O::BufferStoreDword, O::SEndpgm}};
|
||||
}
|
||||
|
||||
TestCase TBufferLoadFormatXyzwSnapshotsOverlappingAddress() {
|
||||
using O = ShaderOpcode;
|
||||
|
||||
std::vector<u32> code;
|
||||
AppendVMovU32(&code, 21, 0);
|
||||
AppendVMovU32(&code, 22, 0);
|
||||
constexpr auto format =
|
||||
BufferFormat(Prospero::BufferFormat::k32_32_32_32Float);
|
||||
code.push_back(
|
||||
EncodeMtbuf0(0x03u, format & 0xfu, (format >> 4u) & 0x7u, 0, true, true));
|
||||
code.push_back(EncodeMtbuf1(0x03u, 21, 0, 21));
|
||||
for (u32 i = 0; i < 4; i++) {
|
||||
AppendStoreVgpr(&code, 21 + i, 4 + i);
|
||||
}
|
||||
AppendEnd(&code);
|
||||
|
||||
TestCase test;
|
||||
test.name = "TBufferLoadFormatXyzwSnapshotsOverlappingAddress";
|
||||
test.code = std::move(code);
|
||||
test.initial = {0x3f800000u, 0x40000000u, 0x40400000u, 0x40800000u,
|
||||
0, 0, 0, 0};
|
||||
test.expected = {0x3f800000u, 0x40000000u, 0x40400000u, 0x40800000u,
|
||||
0x3f800000u, 0x40000000u, 0x40400000u, 0x40800000u};
|
||||
test.opcodes = {O::VMovB32, O::TBufferLoadFormatXyzw,
|
||||
O::BufferStoreDword, O::SEndpgm};
|
||||
test.user_data = MakeStructuredStorageBufferData(16, 2);
|
||||
test.has_user_data = true;
|
||||
return test;
|
||||
}
|
||||
|
||||
TestCase TBufferLoadFormatXyzwPackedSnapshotsOverlappingAddress() {
|
||||
using O = ShaderOpcode;
|
||||
|
||||
std::vector<u32> code;
|
||||
AppendVMovU32(&code, 21, 0);
|
||||
AppendVMovU32(&code, 22, 0);
|
||||
constexpr auto format = BufferFormat(Prospero::BufferFormat::k8_8_8_8UInt);
|
||||
code.push_back(
|
||||
EncodeMtbuf0(0x03u, format & 0xfu, (format >> 4u) & 0x7u, 0, true, true));
|
||||
code.push_back(EncodeMtbuf1(0x03u, 21, 0, 21));
|
||||
for (u32 i = 0; i < 4; i++) {
|
||||
AppendStoreVgpr(&code, 21 + i, 4 + i);
|
||||
}
|
||||
AppendEnd(&code);
|
||||
|
||||
TestCase test;
|
||||
test.name = "TBufferLoadFormatXyzwPackedSnapshotsOverlappingAddress";
|
||||
test.code = std::move(code);
|
||||
test.initial = {0x44332211u, 0, 0, 0, 0, 0, 0, 0};
|
||||
test.expected = {0x44332211u, 0, 0, 0, 0x11u, 0x22u, 0x33u, 0x44u};
|
||||
test.opcodes = {O::VMovB32, O::TBufferLoadFormatXyzw,
|
||||
O::BufferStoreDword, O::SEndpgm};
|
||||
test.user_data = MakeStructuredStorageBufferData(4, 8);
|
||||
test.has_user_data = true;
|
||||
return test;
|
||||
}
|
||||
|
||||
TestCase TBufferStoreFormatX8UintWritesOneByte() {
|
||||
using O = ShaderOpcode;
|
||||
|
||||
@@ -13593,23 +13837,36 @@ TestCase DsAppendUsesEncodedGdsSelector() {
|
||||
using O = ShaderOpcode;
|
||||
|
||||
std::vector<u32> code;
|
||||
AppendSMovLiteral(&code, 124, 0x00000001u);
|
||||
AppendSMovLiteral(&code, 124, 0x00000008u);
|
||||
code.push_back(EncodeDs0(0x3e, 0, true));
|
||||
code.push_back(EncodeDs1(0, 0, 0));
|
||||
code.push_back(EncodeDs0(0x3d, 0, true));
|
||||
code.push_back(EncodeDs1(1, 0, 0));
|
||||
code.push_back(EncodeDs0(0x3e, 4, true));
|
||||
code.push_back(EncodeDs1(2, 0, 0));
|
||||
code.push_back(EncodeDs0(0x3d, 4, true));
|
||||
code.push_back(EncodeDs1(3, 0, 0));
|
||||
AppendSMovLiteral(&code, 124, 0x00080008u);
|
||||
code.push_back(EncodeDs0(0x3e, 4, true));
|
||||
code.push_back(EncodeDs1(4, 0, 0));
|
||||
code.push_back(EncodeDs0(0x3d, 4, true));
|
||||
code.push_back(EncodeDs1(5, 0, 0));
|
||||
AppendStoreVgpr(&code, 0, 0);
|
||||
AppendStoreVgpr(&code, 1, 1);
|
||||
AppendStoreVgpr(&code, 2, 2);
|
||||
AppendStoreVgpr(&code, 3, 3);
|
||||
AppendStoreVgpr(&code, 4, 4);
|
||||
AppendStoreVgpr(&code, 5, 5);
|
||||
AppendEnd(&code);
|
||||
|
||||
TestCase test{
|
||||
"DsAppendGdsSelector",
|
||||
code,
|
||||
{},
|
||||
{10, 74},
|
||||
{10, 74, 20, 84, 40, 104},
|
||||
{O::SMovB32, O::DsAppend, O::DsConsume, O::BufferStoreDword, O::SEndpgm}};
|
||||
test.gds_initial = {10};
|
||||
test.expected_gds = {10};
|
||||
test.gds_initial = {10, 20, 30, 40};
|
||||
test.expected_gds = {10, 20, 30, 40};
|
||||
return test;
|
||||
}
|
||||
|
||||
@@ -14780,8 +15037,13 @@ std::vector<TestCase> MakeCases() {
|
||||
AddCase(BufferStoreDwordAppliesHostOffset);
|
||||
AddCase(BufferOffsetsUsePackedLaneAndStorageFallback);
|
||||
AddCase(BufferLoadVariants);
|
||||
AddCase(BufferLoadDwordx2SnapshotsOverlappingAddress);
|
||||
AddCase(BufferLoadDwordx3SnapshotsOverlappingAddress);
|
||||
AddCase(BufferLoadDwordx4SnapshotsOverlappingAddress);
|
||||
AddCase(BufferStoreVariants);
|
||||
AddCase(BufferFormatVariants);
|
||||
AddCase(BufferLoadFormatXyzwSnapshotsOverlappingAddress);
|
||||
AddCase(BufferLoadFormatXyzwInactiveExecPreservesOverlappingAddress);
|
||||
AddCase(BufferFormatStoreVariants);
|
||||
AddCase(BufferStoreFormatXResource16UintWritesHalfword);
|
||||
AddCase(BufferLoadFormatXResource8UintZeroExtendsByte);
|
||||
@@ -14795,6 +15057,8 @@ std::vector<TestCase> MakeCases() {
|
||||
AddCase(BufferStoreFormatXAddTidUsesLaneIndex);
|
||||
AddCase(BufferStoreFormatXDropsOutOfRangeRecord);
|
||||
AddCase(TBufferLoadVariants);
|
||||
AddCase(TBufferLoadFormatXyzwSnapshotsOverlappingAddress);
|
||||
AddCase(TBufferLoadFormatXyzwPackedSnapshotsOverlappingAddress);
|
||||
AddCase(TBufferLoadFormatX8UintZeroExtendsByte);
|
||||
AddCase(TBufferLoadFormatX8888UintExtractsFirstByte);
|
||||
AddCase(TBufferLoadFormatXIdxenUsesDescriptorStride);
|
||||
@@ -15220,7 +15484,7 @@ void CheckRenderTargetFormatContract() {
|
||||
resource.kind = ShaderRecompiler::IR::ResourceKind::Image;
|
||||
} else if (std::strcmp(kind, "storage-no-write") == 0) {
|
||||
resource.written = false;
|
||||
} else if (std::strcmp(kind, "storage-atomic") == 0) {
|
||||
} else if (std::strcmp(kind, "storage-nonuint-atomic") == 0) {
|
||||
resource.atomic = true;
|
||||
} else if (std::strcmp(kind, "storage-compare") == 0) {
|
||||
resource.depth_compare = true;
|
||||
@@ -15437,6 +15701,12 @@ void CheckSampledColorViews() {
|
||||
Require("SampledColorViews", "write-only uint 2D-array storage resource",
|
||||
IsSupportedStorageImageResource(storage_resource),
|
||||
"basic write-only uint 2D-array storage resource was rejected");
|
||||
storage_resource.dimension = ShaderRecompiler::Decoder::ImageDimension::Dim2D;
|
||||
storage_resource.read = true;
|
||||
storage_resource.atomic = true;
|
||||
Require("SampledColorViews", "atomic uint 2D storage resource",
|
||||
IsSupportedStorageImageResource(storage_resource),
|
||||
"atomic uint storage resource was rejected");
|
||||
|
||||
char path[MAX_PATH]{};
|
||||
Require("SampledColorViews", "host",
|
||||
@@ -15446,7 +15716,7 @@ void CheckSampledColorViews() {
|
||||
{"sampled-invalid-selector", "sampled-incompatible-format",
|
||||
"sampled-invalid-high", "sampled-depth-format", "sampled-depth-swizzle",
|
||||
"storage-incompatible-format", "storage-kind", "storage-no-write",
|
||||
"storage-atomic", "storage-compare", "storage-mip", "storage-dimension",
|
||||
"storage-nonuint-atomic", "storage-compare", "storage-mip", "storage-dimension",
|
||||
"volume-mip-count", "volume-slice-range"}) {
|
||||
std::string command =
|
||||
std::string("\"") + path + "\" --image-view-death " + kind;
|
||||
@@ -16155,6 +16425,19 @@ ShaderTextureResource BasicUintVolumeStorageTextureDescriptor() {
|
||||
0x00700000u, 0x00000000u, 0x00000000u}};
|
||||
}
|
||||
|
||||
ShaderRecompiler::IR::ImageResource AtomicStorageTextureResource() {
|
||||
auto resource = BasicLinearStorageTextureResource();
|
||||
resource.kind = ShaderRecompiler::IR::ResourceKind::StorageImageUint;
|
||||
resource.read = true;
|
||||
resource.atomic = true;
|
||||
return resource;
|
||||
}
|
||||
|
||||
ShaderTextureResource AtomicStorageTextureDescriptor() {
|
||||
return {{0x304bb700u, 0xc1400000u, 0x0000001fu, 0x91b00204u, 0x00000000u,
|
||||
0x00700000u, 0x00000000u, 0x00000000u}};
|
||||
}
|
||||
|
||||
[[noreturn]] void RunStorageTextureDescriptorDeathCase(const char *kind) {
|
||||
auto resource = BasicStorageTextureResource();
|
||||
auto descriptor = BasicStorageTextureDescriptor();
|
||||
@@ -16216,6 +16499,12 @@ ShaderTextureResource BasicUintVolumeStorageTextureDescriptor() {
|
||||
} else if (std::strcmp(kind, "uint-resource-float-format") == 0) {
|
||||
resource = BasicUintArrayStorageTextureResource();
|
||||
descriptor = BasicArrayStorageTextureDescriptor();
|
||||
} else if (std::strcmp(kind, "atomic-format") == 0) {
|
||||
resource = AtomicStorageTextureResource();
|
||||
descriptor = AtomicStorageTextureDescriptor();
|
||||
descriptor.fields[1] =
|
||||
(descriptor.fields[1] & ~0x1ff00000u) |
|
||||
(Prospero::GpuEnumValue(Prospero::BufferFormat::k8UInt) << 20u);
|
||||
} else if (std::strcmp(kind, "depth-tile-read") == 0) {
|
||||
resource = Ppsa14053DepthTileStorageTextureResource();
|
||||
descriptor = Ppsa14053DepthTileStorageTextureDescriptor();
|
||||
@@ -16299,7 +16588,7 @@ void CheckBasicStorageTextureDescriptor() {
|
||||
"PPSA06228 R11G11B10 storage descriptor fixture is malformed");
|
||||
ValidateStorageTexture(BasicBgraStorageTextureResource(), r11g11b10,
|
||||
0x870000);
|
||||
ValidateStorageColorView(vk::Format::eB10G11R11UfloatPack32,
|
||||
ValidateStorageColorView(vk::Format::eB8G8R8A8Unorm,
|
||||
vk::Format::eB10G11R11UfloatPack32,
|
||||
r11g11b10.DstSelXYZW());
|
||||
|
||||
@@ -16590,6 +16879,18 @@ void CheckBasicStorageTextureDescriptor() {
|
||||
IsValidImageSwizzle(DstSel(4, 4, 4, 4)),
|
||||
"single-channel replicated destination selection was rejected");
|
||||
|
||||
const auto atomic = AtomicStorageTextureDescriptor();
|
||||
Require("BasicStorageTexture", "atomic R32_UINT descriptor",
|
||||
atomic.Width5() + 1u == 128 && atomic.Height5() + 1u == 1 &&
|
||||
atomic.Depth() + 1u == 1 &&
|
||||
atomic.Type() ==
|
||||
Prospero::GpuEnumValue(Prospero::ImageType::kColor2D) &&
|
||||
atomic.Format() ==
|
||||
Prospero::GpuEnumValue(Prospero::BufferFormat::k32UInt) &&
|
||||
atomic.DstSelXYZW() == DstSel(4, 0, 0, 1),
|
||||
"PPSA22102 image-atomic descriptor fixture is malformed");
|
||||
ValidateStorageTexture(AtomicStorageTextureResource(), atomic, 0x10000);
|
||||
|
||||
char path[MAX_PATH]{};
|
||||
Require("BasicStorageTexture", "host",
|
||||
GetModuleFileNameA(nullptr, path, MAX_PATH) != 0,
|
||||
@@ -16598,7 +16899,7 @@ void CheckBasicStorageTextureDescriptor() {
|
||||
{"resource", "type", "tile", "mip", "swizzle", "linear-rgb1-read",
|
||||
"bgra-read", "r16-float-read", "r8-unorm-read", "yzwx-read",
|
||||
"reserved-swizzle", "array-base-out-of-range", "array-mip-view",
|
||||
"reserved", "uint-format", "uint-resource-float-format",
|
||||
"reserved", "uint-format", "uint-resource-float-format", "atomic-format",
|
||||
"depth-tile-read", "depth-tile-extent", "depth-tile-fmask"}) {
|
||||
std::string command = std::string("\"") + path +
|
||||
"\" --storage-texture-descriptor-death " + kind;
|
||||
|
||||
@@ -405,6 +405,10 @@ void TestDirectMapQueryOffsetAndPartialMunmap() {
|
||||
"TryReadBacking should reject a range crossing an unmapped span");
|
||||
Check(test, rejected_read == transaction_sentinel,
|
||||
"failed backing reads must not modify a destination prefix");
|
||||
Check(test,
|
||||
Libs::LibKernel::Memory::ClampRangeSize(base + SceKernelPageSize - 0xf30, 0x1560) ==
|
||||
0xf30,
|
||||
"ClampRangeSize did not stop at an unmapped span");
|
||||
|
||||
info = Query(test, base + SceKernelPageSize, SceKernelVqFindNext);
|
||||
ExpectRange(test, info, base + SceKernelPageSize * 2, base + SceKernelPageSize * 4,
|
||||
@@ -473,6 +477,11 @@ void TestMunmapAcrossAdjacentFlexibleMappings() {
|
||||
&right, SceKernelPageSize, SceKernelProtCpuRw, SceKernelMapFixed, "adjacent_right"),
|
||||
"KernelMapNamedFlexibleMemory(right)");
|
||||
|
||||
Check(test,
|
||||
Libs::LibKernel::Memory::ClampRangeSize(base + SceKernelPageSize - 0x100, 0x200) ==
|
||||
0x200,
|
||||
"ClampRangeSize did not cross adjacent committed mappings");
|
||||
|
||||
CheckOk(test, Libs::LibKernel::Memory::KernelMunmap(base, SceKernelPageSize * 2),
|
||||
"KernelMunmap(adjacent mappings)");
|
||||
Check(test, AvailableFlexibleMemory(test) == baseline,
|
||||
|
||||
Reference in New Issue
Block a user