mirror of
https://github.com/KytyPS5/KytyPS5.git
synced 2026-10-29 14:26:53 +01:00
format src and tests with clang-format
This commit is contained in:
@@ -49,8 +49,7 @@ public:
|
|||||||
|
|
||||||
template <typename Function>
|
template <typename Function>
|
||||||
void ForEachItemBelow(Tick tick, Function&& function) {
|
void ForEachItemBelow(Tick tick, Function&& function) {
|
||||||
constexpr bool ReturnsBool =
|
constexpr bool ReturnsBool = std::is_same_v<std::invoke_result_t<Function, Object>, bool>;
|
||||||
std::is_same_v<std::invoke_result_t<Function, Object>, bool>;
|
|
||||||
for (auto* item = m_first; item != nullptr;) {
|
for (auto* item = m_first; item != nullptr;) {
|
||||||
if (item->tick > tick) {
|
if (item->tick > tick) {
|
||||||
return;
|
return;
|
||||||
|
|||||||
@@ -33,8 +33,7 @@ static bool OnOwnStack() {
|
|||||||
}
|
}
|
||||||
void* base = nullptr;
|
void* base = nullptr;
|
||||||
size_t size = 0;
|
size_t size = 0;
|
||||||
const bool ok =
|
const bool ok = pthread_attr_getstack(&attr, &base, &size) == 0 && base != nullptr && size != 0;
|
||||||
pthread_attr_getstack(&attr, &base, &size) == 0 && base != nullptr && size != 0;
|
|
||||||
pthread_attr_destroy(&attr);
|
pthread_attr_destroy(&attr);
|
||||||
if (!ok) {
|
if (!ok) {
|
||||||
return false;
|
return false;
|
||||||
|
|||||||
@@ -172,8 +172,7 @@ sys_file_t* SysFileCreate(const std::filesystem::path& file_name) {
|
|||||||
return ret;
|
return ret;
|
||||||
}
|
}
|
||||||
|
|
||||||
sys_file_t* SysFileOpenR(const std::filesystem::path& file_name,
|
sys_file_t* SysFileOpenR(const std::filesystem::path& file_name, sys_file_cache_type_t cache_type) {
|
||||||
sys_file_cache_type_t cache_type) {
|
|
||||||
auto* ret = new sys_file_t;
|
auto* ret = new sys_file_t;
|
||||||
|
|
||||||
ret->type = SYS_FILE_FILE;
|
ret->type = SYS_FILE_FILE;
|
||||||
@@ -218,8 +217,7 @@ sys_file_t* SysFileCreate() {
|
|||||||
return ret;
|
return ret;
|
||||||
}
|
}
|
||||||
|
|
||||||
sys_file_t* SysFileOpenW(const std::filesystem::path& file_name,
|
sys_file_t* SysFileOpenW(const std::filesystem::path& file_name, sys_file_cache_type_t cache_type) {
|
||||||
sys_file_cache_type_t cache_type) {
|
|
||||||
auto* ret = new sys_file_t;
|
auto* ret = new sys_file_t;
|
||||||
|
|
||||||
auto real_name = get_internal_name(file_name);
|
auto real_name = get_internal_name(file_name);
|
||||||
|
|||||||
@@ -136,8 +136,8 @@ static void* map_anonymous(uintptr_t addr, size_t size, int protect, int flags)
|
|||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
const auto hint = (top - step) & ~(LOW_ARENA_GRAIN - 1);
|
const auto hint = (top - step) & ~(LOW_ARENA_GRAIN - 1);
|
||||||
void* ptr = mmap(reinterpret_cast<void*>(hint), size, protect,
|
void* ptr = mmap(reinterpret_cast<void*>(hint), size, protect, flags | MAP_FIXED_NOREPLACE,
|
||||||
flags | MAP_FIXED_NOREPLACE, -1, 0); // NOLINT
|
-1, 0); // NOLINT
|
||||||
if (ptr != MAP_FAILED) {
|
if (ptr != MAP_FAILED) {
|
||||||
return ptr;
|
return ptr;
|
||||||
}
|
}
|
||||||
@@ -194,8 +194,8 @@ uint64_t SysVirtualAllocAligned(uint64_t address, uint64_t size, VirtualMemory::
|
|||||||
if (ptr != MAP_FAILED && ((ret_addr & (alignment - 1)) != 0)) {
|
if (ptr != MAP_FAILED && ((ret_addr & (alignment - 1)) != 0)) {
|
||||||
munmap(ptr, size);
|
munmap(ptr, size);
|
||||||
|
|
||||||
ptr = map_anonymous(addr, size + alignment, protect,
|
ptr =
|
||||||
MAP_PRIVATE | MAP_ANON | MAP_NORESERVE);
|
map_anonymous(addr, size + alignment, protect, MAP_PRIVATE | MAP_ANON | MAP_NORESERVE);
|
||||||
ret_addr = reinterpret_cast<uintptr_t>(ptr);
|
ret_addr = reinterpret_cast<uintptr_t>(ptr);
|
||||||
if (ptr != MAP_FAILED) {
|
if (ptr != MAP_FAILED) {
|
||||||
#if defined(__APPLE__)
|
#if defined(__APPLE__)
|
||||||
|
|||||||
@@ -5,9 +5,9 @@
|
|||||||
|
|
||||||
#include <algorithm>
|
#include <algorithm>
|
||||||
#include <atomic>
|
#include <atomic>
|
||||||
|
#include <cerrno>
|
||||||
#include <chrono> // IWYU pragma: keep
|
#include <chrono> // IWYU pragma: keep
|
||||||
#include <condition_variable> // IWYU pragma: keep
|
#include <condition_variable> // IWYU pragma: keep
|
||||||
#include <cerrno>
|
|
||||||
#include <mutex>
|
#include <mutex>
|
||||||
#include <vector>
|
#include <vector>
|
||||||
|
|
||||||
|
|||||||
@@ -20,9 +20,7 @@ class UniqueFunction {
|
|||||||
public:
|
public:
|
||||||
explicit Callable(Function function): m_function(std::move(function)) {}
|
explicit Callable(Function function): m_function(std::move(function)) {}
|
||||||
|
|
||||||
Result Invoke(Args&&... args) override {
|
Result Invoke(Args&&... args) override { return m_function(std::forward<Args>(args)...); }
|
||||||
return m_function(std::forward<Args>(args)...);
|
|
||||||
}
|
|
||||||
|
|
||||||
private:
|
private:
|
||||||
Function m_function;
|
Function m_function;
|
||||||
|
|||||||
@@ -962,9 +962,8 @@ void CommandProcessor::DrawIndexOffset(uint32_t index_offset, uint32_t index_cou
|
|||||||
auto* index_addr = reinterpret_cast<const void*>(
|
auto* index_addr = reinterpret_cast<const void*>(
|
||||||
m_index_base_addr + static_cast<uint64_t>(index_offset) * index_size);
|
m_index_base_addr + static_cast<uint64_t>(index_offset) * index_size);
|
||||||
|
|
||||||
m_renderer.GetRenderExecutor().DrawIndex(m_submit_id, CurrentBuffer(),
|
m_renderer.GetRenderExecutor().DrawIndex(m_submit_id, CurrentBuffer(), m_index_type_and_size,
|
||||||
m_index_type_and_size, index_count, index_addr,
|
index_count, index_addr, flags, 1, m_num_instances);
|
||||||
flags, 1, m_num_instances);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
void CommandProcessor::DrawIndirect(uint32_t data_offset, uint32_t draw_initiator, bool indexed) {
|
void CommandProcessor::DrawIndirect(uint32_t data_offset, uint32_t draw_initiator, bool indexed) {
|
||||||
@@ -1190,8 +1189,8 @@ void CommandProcessor::DispatchDirect(uint32_t thread_group_x, uint32_t thread_g
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
m_renderer.GetRenderExecutor().DispatchDirect(
|
m_renderer.GetRenderExecutor().DispatchDirect(m_submit_id, CurrentBuffer(), thread_group_x,
|
||||||
m_submit_id, CurrentBuffer(), thread_group_x, thread_group_y, thread_group_z, mode);
|
thread_group_y, thread_group_z, mode);
|
||||||
}
|
}
|
||||||
|
|
||||||
constexpr uint32_t DispatchInitiatorUseThreadDimensions = 1u << 5u;
|
constexpr uint32_t DispatchInitiatorUseThreadDimensions = 1u << 5u;
|
||||||
@@ -1237,9 +1236,9 @@ void CommandProcessor::DrawIndexAuto(uint32_t index_count, uint32_t flags,
|
|||||||
uint32_t first_vertex, uint32_t first_instance) {
|
uint32_t first_vertex, uint32_t first_instance) {
|
||||||
CheckBuffer();
|
CheckBuffer();
|
||||||
|
|
||||||
m_renderer.GetRenderExecutor().DrawAuto(
|
m_renderer.GetRenderExecutor().DrawAuto(m_submit_id, CurrentBuffer(), index_count, flags,
|
||||||
m_submit_id, CurrentBuffer(), index_count, flags, render_target_slice_offset,
|
render_target_slice_offset, instance_count,
|
||||||
instance_count, first_vertex, first_instance);
|
first_vertex, first_instance);
|
||||||
}
|
}
|
||||||
|
|
||||||
void CommandProcessor::WaitFlipDone(uint32_t video_out_handle, uint32_t display_buffer_index) {
|
void CommandProcessor::WaitFlipDone(uint32_t video_out_handle, uint32_t display_buffer_index) {
|
||||||
@@ -1317,8 +1316,8 @@ void CommandProcessor::WriteAtEndOfPipe(uint32_t cache_policy, uint32_t event_wr
|
|||||||
if (eop_event_type == 0x2f && cache_action == 0x00 && event_index == 0x06) {
|
if (eop_event_type == 0x2f && cache_action == 0x00 && event_index == 0x06) {
|
||||||
auto* dst = static_cast<uint32_t*>(dst_gpu_addr);
|
auto* dst = static_cast<uint32_t*>(dst_gpu_addr);
|
||||||
SynchronizeGpu();
|
SynchronizeGpu();
|
||||||
Sync::ReadGds(m_renderer.GetBufferCache().GetGdsBuffer(), dst,
|
Sync::ReadGds(m_renderer.GetBufferCache().GetGdsBuffer(), dst, value & 0xffffu,
|
||||||
value & 0xffffu, value >> 16u);
|
value >> 16u);
|
||||||
Sync::WriteAtEndOfPipeGds32(m_submit_id, CurrentBuffer(), dst, value & 0xffffu,
|
Sync::WriteAtEndOfPipeGds32(m_submit_id, CurrentBuffer(), dst, value & 0xffffu,
|
||||||
value >> 16u);
|
value >> 16u);
|
||||||
return;
|
return;
|
||||||
@@ -1486,8 +1485,7 @@ void CommandProcessor::EmitGlobalBarrier() {
|
|||||||
barrier.srcStageMask = vk::PipelineStageFlagBits2::eAllCommands;
|
barrier.srcStageMask = vk::PipelineStageFlagBits2::eAllCommands;
|
||||||
barrier.srcAccessMask = vk::AccessFlagBits2::eMemoryWrite;
|
barrier.srcAccessMask = vk::AccessFlagBits2::eMemoryWrite;
|
||||||
barrier.dstStageMask = vk::PipelineStageFlagBits2::eAllCommands;
|
barrier.dstStageMask = vk::PipelineStageFlagBits2::eAllCommands;
|
||||||
barrier.dstAccessMask =
|
barrier.dstAccessMask = vk::AccessFlagBits2::eMemoryRead | vk::AccessFlagBits2::eMemoryWrite;
|
||||||
vk::AccessFlagBits2::eMemoryRead | vk::AccessFlagBits2::eMemoryWrite;
|
|
||||||
|
|
||||||
vk::DependencyInfo dependency {};
|
vk::DependencyInfo dependency {};
|
||||||
dependency.memoryBarrierCount = 1;
|
dependency.memoryBarrierCount = 1;
|
||||||
|
|||||||
+6
-8
@@ -135,8 +135,8 @@ void Buffer::Write(uint64_t offset, const void* source, uint64_t size) {
|
|||||||
void Buffer::Flush(uint64_t offset, uint64_t size) {
|
void Buffer::Flush(uint64_t offset, uint64_t size) {
|
||||||
EXIT_IF(m_mapped.empty() || offset > m_size || size > m_size - offset);
|
EXIT_IF(m_mapped.empty() || offset > m_size || size > m_size - offset);
|
||||||
if (!m_is_coherent && size != 0) {
|
if (!m_is_coherent && size != 0) {
|
||||||
const auto result = vmaFlushAllocation(m_graphics->allocator, m_buffer->memory.allocation,
|
const auto result =
|
||||||
offset, size);
|
vmaFlushAllocation(m_graphics->allocator, m_buffer->memory.allocation, offset, size);
|
||||||
EXIT_NOT_IMPLEMENTED(static_cast<vk::Result>(result) != vk::Result::eSuccess);
|
EXIT_NOT_IMPLEMENTED(static_cast<vk::Result>(result) != vk::Result::eSuccess);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -144,8 +144,8 @@ void Buffer::Flush(uint64_t offset, uint64_t size) {
|
|||||||
vk::BufferMemoryBarrier Buffer::Barrier(uint64_t offset, uint64_t size, vk::AccessFlags source,
|
vk::BufferMemoryBarrier Buffer::Barrier(uint64_t offset, uint64_t size, vk::AccessFlags source,
|
||||||
vk::AccessFlags destination) const {
|
vk::AccessFlags destination) const {
|
||||||
if (Handle() == nullptr || size == 0 || offset > m_size || size > m_size - offset) {
|
if (Handle() == nullptr || size == 0 || offset > m_size || size > m_size - offset) {
|
||||||
EXIT("Buffer: invalid DMA barrier, handle=%p offset=0x%016" PRIx64
|
EXIT("Buffer: invalid DMA barrier, handle=%p offset=0x%016" PRIx64 " size=0x%016" PRIx64
|
||||||
" size=0x%016" PRIx64 " capacity=0x%016" PRIx64 "\n",
|
" capacity=0x%016" PRIx64 "\n",
|
||||||
static_cast<const void*>(Handle()), offset, size, m_size);
|
static_cast<const void*>(Handle()), offset, size, m_size);
|
||||||
}
|
}
|
||||||
vk::BufferMemoryBarrier barrier {};
|
vk::BufferMemoryBarrier barrier {};
|
||||||
@@ -175,8 +175,7 @@ void Buffer::CopyFrom(CommandBuffer& command, const Buffer& source, uint64_t sou
|
|||||||
command.EndRendering();
|
command.EndRendering();
|
||||||
const vk::BufferMemoryBarrier before[] = {
|
const vk::BufferMemoryBarrier before[] = {
|
||||||
source.Barrier(source_offset, size, source_before, vk::AccessFlagBits::eTransferRead),
|
source.Barrier(source_offset, size, source_before, vk::AccessFlagBits::eTransferRead),
|
||||||
Barrier(destination_offset, size, destination_before,
|
Barrier(destination_offset, size, destination_before, vk::AccessFlagBits::eTransferWrite),
|
||||||
vk::AccessFlagBits::eTransferWrite),
|
|
||||||
};
|
};
|
||||||
const auto host_access = vk::AccessFlagBits::eHostRead | vk::AccessFlagBits::eHostWrite;
|
const auto host_access = vk::AccessFlagBits::eHostRead | vk::AccessFlagBits::eHostWrite;
|
||||||
auto before_stage = vk::PipelineStageFlags {vk::PipelineStageFlagBits::eAllCommands};
|
auto before_stage = vk::PipelineStageFlags {vk::PipelineStageFlagBits::eAllCommands};
|
||||||
@@ -214,8 +213,7 @@ void Buffer::Fill(uint64_t offset, uint64_t size, uint32_t value) {
|
|||||||
vk::PipelineStageFlagBits::eTransfer, vk::DependencyFlagBits::eByRegion,
|
vk::PipelineStageFlagBits::eTransfer, vk::DependencyFlagBits::eByRegion,
|
||||||
0, nullptr, 1, &before, 0, nullptr);
|
0, nullptr, 1, &before, 0, nullptr);
|
||||||
native.fillBuffer(Handle(), offset, size, value);
|
native.fillBuffer(Handle(), offset, size, value);
|
||||||
const auto after =
|
const auto after = Barrier(offset, size, vk::AccessFlagBits::eTransferWrite,
|
||||||
Barrier(offset, size, vk::AccessFlagBits::eTransferWrite,
|
|
||||||
vk::AccessFlagBits::eMemoryRead | vk::AccessFlagBits::eMemoryWrite);
|
vk::AccessFlagBits::eMemoryRead | vk::AccessFlagBits::eMemoryWrite);
|
||||||
native.pipelineBarrier(vk::PipelineStageFlagBits::eTransfer,
|
native.pipelineBarrier(vk::PipelineStageFlagBits::eTransfer,
|
||||||
vk::PipelineStageFlagBits::eAllCommands,
|
vk::PipelineStageFlagBits::eAllCommands,
|
||||||
|
|||||||
+7
-8
@@ -54,16 +54,15 @@ public:
|
|||||||
[[nodiscard]] bool IsInBounds(uint64_t address, uint64_t size) const noexcept;
|
[[nodiscard]] bool IsInBounds(uint64_t address, uint64_t size) const noexcept;
|
||||||
void Write(uint64_t offset, const void* source, uint64_t size);
|
void Write(uint64_t offset, const void* source, uint64_t size);
|
||||||
void Flush(uint64_t offset, uint64_t size);
|
void Flush(uint64_t offset, uint64_t size);
|
||||||
void CopyFrom(
|
void CopyFrom(CommandBuffer& command, const Buffer& source, uint64_t source_offset,
|
||||||
CommandBuffer& command, const Buffer& source, uint64_t source_offset,
|
|
||||||
uint64_t destination_offset, uint64_t size,
|
uint64_t destination_offset, uint64_t size,
|
||||||
vk::AccessFlags source_before = vk::AccessFlagBits::eMemoryWrite,
|
vk::AccessFlags source_before = vk::AccessFlagBits::eMemoryWrite,
|
||||||
vk::AccessFlags destination_before =
|
vk::AccessFlags destination_before = vk::AccessFlagBits::eMemoryRead |
|
||||||
vk::AccessFlagBits::eMemoryRead | vk::AccessFlagBits::eMemoryWrite,
|
vk::AccessFlagBits::eMemoryWrite,
|
||||||
vk::AccessFlags source_after =
|
vk::AccessFlags source_after = vk::AccessFlagBits::eMemoryRead |
|
||||||
vk::AccessFlagBits::eMemoryRead | vk::AccessFlagBits::eMemoryWrite,
|
vk::AccessFlagBits::eMemoryWrite,
|
||||||
vk::AccessFlags destination_after =
|
vk::AccessFlags destination_after = vk::AccessFlagBits::eMemoryRead |
|
||||||
vk::AccessFlagBits::eMemoryRead | vk::AccessFlagBits::eMemoryWrite);
|
vk::AccessFlagBits::eMemoryWrite);
|
||||||
void Fill(uint64_t offset, uint64_t size, uint32_t value);
|
void Fill(uint64_t offset, uint64_t size, uint32_t value);
|
||||||
|
|
||||||
protected:
|
protected:
|
||||||
|
|||||||
@@ -2,8 +2,8 @@
|
|||||||
#define EMULATOR_SRC_GRAPHICS_HOST_GPU_RENDERER_COLORRENDERTARGET_H_
|
#define EMULATOR_SRC_GRAPHICS_HOST_GPU_RENDERER_COLORRENDERTARGET_H_
|
||||||
|
|
||||||
#include "graphics/guest_gpu/gpu_defs.h"
|
#include "graphics/guest_gpu/gpu_defs.h"
|
||||||
#include "graphics/host_gpu/renderer/renderTarget.h"
|
|
||||||
#include "graphics/host_gpu/renderer/cache/textureCache.h"
|
#include "graphics/host_gpu/renderer/cache/textureCache.h"
|
||||||
|
#include "graphics/host_gpu/renderer/renderTarget.h"
|
||||||
#include "graphics/host_gpu/vulkanCommon.h"
|
#include "graphics/host_gpu/vulkanCommon.h"
|
||||||
|
|
||||||
#include <cstdint>
|
#include <cstdint>
|
||||||
|
|||||||
@@ -331,8 +331,7 @@ void CommandScheduler::WaitPriorityOperations(uint64_t tick) {
|
|||||||
EXIT_IF(g_deferred_callback_scheduler == this);
|
EXIT_IF(g_deferred_callback_scheduler == this);
|
||||||
std::unique_lock lock(m_operation_mutex);
|
std::unique_lock lock(m_operation_mutex);
|
||||||
m_operation_available.wait(lock, [this, tick] {
|
m_operation_available.wait(lock, [this, tick] {
|
||||||
const bool active_before_or_at =
|
const bool active_before_or_at = m_priority_active && m_priority_active_tick <= tick;
|
||||||
m_priority_active && m_priority_active_tick <= tick;
|
|
||||||
const bool queued_before_or_at =
|
const bool queued_before_or_at =
|
||||||
!m_priority_operations.empty() && m_priority_operations.front().tick <= tick;
|
!m_priority_operations.empty() && m_priority_operations.front().tick <= tick;
|
||||||
return !active_before_or_at && !queued_before_or_at;
|
return !active_before_or_at && !queued_before_or_at;
|
||||||
|
|||||||
@@ -8,8 +8,8 @@
|
|||||||
#include "graphics/host_gpu/renderer/colorRenderTarget.h"
|
#include "graphics/host_gpu/renderer/colorRenderTarget.h"
|
||||||
#include "graphics/host_gpu/renderer/debug.h"
|
#include "graphics/host_gpu/renderer/debug.h"
|
||||||
#include "graphics/host_gpu/renderer/depthRenderTarget.h"
|
#include "graphics/host_gpu/renderer/depthRenderTarget.h"
|
||||||
#include "graphics/host_gpu/renderer/pipeline/descriptorCache.h"
|
|
||||||
#include "graphics/host_gpu/renderer/image/imageView.h"
|
#include "graphics/host_gpu/renderer/image/imageView.h"
|
||||||
|
#include "graphics/host_gpu/renderer/pipeline/descriptorCache.h"
|
||||||
#include "graphics/host_gpu/renderer/render.h"
|
#include "graphics/host_gpu/renderer/render.h"
|
||||||
#include "graphics/host_gpu/renderer/renderContext.h"
|
#include "graphics/host_gpu/renderer/renderContext.h"
|
||||||
#include "graphics/host_gpu/vma.h"
|
#include "graphics/host_gpu/vma.h"
|
||||||
@@ -270,8 +270,8 @@ void CommandBuffer::BeginRendering(const RenderState& state) const {
|
|||||||
colors[i].sType = vk::StructureType::eRenderingAttachmentInfo;
|
colors[i].sType = vk::StructureType::eRenderingAttachmentInfo;
|
||||||
colors[i].imageView = attachment.image_view;
|
colors[i].imageView = attachment.image_view;
|
||||||
colors[i].imageLayout = attachment.image_layout;
|
colors[i].imageLayout = attachment.image_layout;
|
||||||
colors[i].loadOp = attachment.is_clear ? vk::AttachmentLoadOp::eClear
|
colors[i].loadOp =
|
||||||
: vk::AttachmentLoadOp::eLoad;
|
attachment.is_clear ? vk::AttachmentLoadOp::eClear : vk::AttachmentLoadOp::eLoad;
|
||||||
colors[i].storeOp = vk::AttachmentStoreOp::eStore;
|
colors[i].storeOp = vk::AttachmentStoreOp::eStore;
|
||||||
colors[i].clearValue.color.uint32 = attachment.clear_value;
|
colors[i].clearValue.color.uint32 = attachment.clear_value;
|
||||||
}
|
}
|
||||||
@@ -281,8 +281,8 @@ void CommandBuffer::BeginRendering(const RenderState& state) const {
|
|||||||
depth.sType = vk::StructureType::eRenderingAttachmentInfo;
|
depth.sType = vk::StructureType::eRenderingAttachmentInfo;
|
||||||
depth.imageView = depth_stencil.image_view;
|
depth.imageView = depth_stencil.image_view;
|
||||||
depth.imageLayout = depth_stencil.image_layout;
|
depth.imageLayout = depth_stencil.image_layout;
|
||||||
depth.loadOp = depth_stencil.depth_clear ? vk::AttachmentLoadOp::eClear
|
depth.loadOp =
|
||||||
: vk::AttachmentLoadOp::eLoad;
|
depth_stencil.depth_clear ? vk::AttachmentLoadOp::eClear : vk::AttachmentLoadOp::eLoad;
|
||||||
depth.storeOp = vk::AttachmentStoreOp::eStore;
|
depth.storeOp = vk::AttachmentStoreOp::eStore;
|
||||||
depth.clearValue.depthStencil.depth = std::bit_cast<float>(depth_stencil.clear_value[0]);
|
depth.clearValue.depthStencil.depth = std::bit_cast<float>(depth_stencil.clear_value[0]);
|
||||||
|
|
||||||
@@ -290,8 +290,8 @@ void CommandBuffer::BeginRendering(const RenderState& state) const {
|
|||||||
stencil.sType = vk::StructureType::eRenderingAttachmentInfo;
|
stencil.sType = vk::StructureType::eRenderingAttachmentInfo;
|
||||||
stencil.imageView = depth_stencil.image_view;
|
stencil.imageView = depth_stencil.image_view;
|
||||||
stencil.imageLayout = depth_stencil.image_layout;
|
stencil.imageLayout = depth_stencil.image_layout;
|
||||||
stencil.loadOp = depth_stencil.stencil_clear ? vk::AttachmentLoadOp::eClear
|
stencil.loadOp =
|
||||||
: vk::AttachmentLoadOp::eLoad;
|
depth_stencil.stencil_clear ? vk::AttachmentLoadOp::eClear : vk::AttachmentLoadOp::eLoad;
|
||||||
stencil.storeOp = vk::AttachmentStoreOp::eStore;
|
stencil.storeOp = vk::AttachmentStoreOp::eStore;
|
||||||
stencil.clearValue.depthStencil.stencil = depth_stencil.clear_value[1];
|
stencil.clearValue.depthStencil.stencil = depth_stencil.clear_value[1];
|
||||||
|
|
||||||
|
|||||||
@@ -2,9 +2,9 @@
|
|||||||
#define EMULATOR_SRC_GRAPHICS_HOST_GPU_RENDERER_DEPTHRENDERTARGET_H_
|
#define EMULATOR_SRC_GRAPHICS_HOST_GPU_RENDERER_DEPTHRENDERTARGET_H_
|
||||||
|
|
||||||
#include "common/assert.h"
|
#include "common/assert.h"
|
||||||
|
#include "graphics/host_gpu/renderer/cache/textureCache.h"
|
||||||
#include "graphics/host_gpu/renderer/image/imageView.h"
|
#include "graphics/host_gpu/renderer/image/imageView.h"
|
||||||
#include "graphics/host_gpu/renderer/renderTarget.h"
|
#include "graphics/host_gpu/renderer/renderTarget.h"
|
||||||
#include "graphics/host_gpu/renderer/cache/textureCache.h"
|
|
||||||
#include "graphics/host_gpu/vulkanCommon.h"
|
#include "graphics/host_gpu/vulkanCommon.h"
|
||||||
|
|
||||||
#include <cstdint>
|
#include <cstdint>
|
||||||
|
|||||||
@@ -182,8 +182,8 @@ void BlitHelper::ReinterpretColorAsMsDepth(Image& source, Image& destination) {
|
|||||||
auto command = command_buffer.Handle();
|
auto command = command_buffer.Handle();
|
||||||
source.Transit(vk::ImageLayout::eShaderReadOnlyOptimal, vk::AccessFlagBits2::eShaderRead, {},
|
source.Transit(vk::ImageLayout::eShaderReadOnlyOptimal, vk::AccessFlagBits2::eShaderRead, {},
|
||||||
command);
|
command);
|
||||||
destination.Transit(ColorToMsDepthLayout,
|
destination.Transit(ColorToMsDepthLayout, vk::AccessFlagBits2::eDepthStencilAttachmentWrite, {},
|
||||||
vk::AccessFlagBits2::eDepthStencilAttachmentWrite, {}, command);
|
command);
|
||||||
|
|
||||||
vk::RenderingAttachmentInfo depth_attachment {};
|
vk::RenderingAttachmentInfo depth_attachment {};
|
||||||
depth_attachment.sType = vk::StructureType::eRenderingAttachmentInfo;
|
depth_attachment.sType = vk::StructureType::eRenderingAttachmentInfo;
|
||||||
|
|||||||
@@ -21,8 +21,7 @@ struct GuestRange {
|
|||||||
|
|
||||||
[[nodiscard]] constexpr bool Empty() const noexcept { return address == 0 || size == 0; }
|
[[nodiscard]] constexpr bool Empty() const noexcept { return address == 0 || size == 0; }
|
||||||
[[nodiscard]] constexpr bool Valid() const noexcept {
|
[[nodiscard]] constexpr bool Valid() const noexcept {
|
||||||
return !Empty() && address < TRACKER_ADDRESS_SIZE &&
|
return !Empty() && address < TRACKER_ADDRESS_SIZE && size <= TRACKER_ADDRESS_SIZE - address;
|
||||||
size <= TRACKER_ADDRESS_SIZE - address;
|
|
||||||
}
|
}
|
||||||
[[nodiscard]] constexpr uint64_t End() const noexcept { return address + size; }
|
[[nodiscard]] constexpr uint64_t End() const noexcept { return address + size; }
|
||||||
auto operator<=>(const GuestRange&) const = default;
|
auto operator<=>(const GuestRange&) const = default;
|
||||||
@@ -352,8 +351,7 @@ inline bool ImageInfo::IsDepth() const noexcept {
|
|||||||
}
|
}
|
||||||
const auto transfer_bytes = DepthAspectTransferBytes(info.pixel_format);
|
const auto transfer_bytes = DepthAspectTransferBytes(info.pixel_format);
|
||||||
return transfer_bytes == info.bytes_per_block ||
|
return transfer_bytes == info.bytes_per_block ||
|
||||||
(info.bytes_per_block == sizeof(uint16_t) &&
|
(info.bytes_per_block == sizeof(uint16_t) && transfer_bytes == sizeof(uint32_t));
|
||||||
transfer_bytes == sizeof(uint32_t));
|
|
||||||
}
|
}
|
||||||
|
|
||||||
[[nodiscard]] inline VideoOutCompression
|
[[nodiscard]] inline VideoOutCompression
|
||||||
@@ -472,16 +470,11 @@ IsSupportedDisplayRenderTargetTileMode(uint32_t tile_mode) noexcept {
|
|||||||
const auto unorm8 = [](uint32_t value) { return static_cast<float>(value & 0xffu) / 255.0f; };
|
const auto unorm8 = [](uint32_t value) { return static_cast<float>(value & 0xffu) / 255.0f; };
|
||||||
const auto srgb8 = [](uint32_t value) {
|
const auto srgb8 = [](uint32_t value) {
|
||||||
const auto encoded = static_cast<float>(value & 0xffu) / 255.0f;
|
const auto encoded = static_cast<float>(value & 0xffu) / 255.0f;
|
||||||
return encoded <= 0.04045f ? encoded / 12.92f
|
return encoded <= 0.04045f ? encoded / 12.92f : std::pow((encoded + 0.055f) / 1.055f, 2.4f);
|
||||||
: std::pow((encoded + 0.055f) / 1.055f, 2.4f);
|
|
||||||
};
|
};
|
||||||
switch (format) {
|
switch (format) {
|
||||||
case vk::Format::eR32Uint:
|
case vk::Format::eR32Uint: next.uint32[0] = packed; break;
|
||||||
next.uint32[0] = packed;
|
case vk::Format::eR32Sint: next.int32[0] = static_cast<int32_t>(packed); break;
|
||||||
break;
|
|
||||||
case vk::Format::eR32Sint:
|
|
||||||
next.int32[0] = static_cast<int32_t>(packed);
|
|
||||||
break;
|
|
||||||
case vk::Format::eR8G8B8A8Srgb:
|
case vk::Format::eR8G8B8A8Srgb:
|
||||||
next.float32[0] = srgb8(packed);
|
next.float32[0] = srgb8(packed);
|
||||||
next.float32[1] = srgb8(packed >> 8u);
|
next.float32[1] = srgb8(packed >> 8u);
|
||||||
|
|||||||
@@ -70,15 +70,14 @@ namespace {
|
|||||||
}
|
}
|
||||||
case vk::ImageType::e3D:
|
case vk::ImageType::e3D:
|
||||||
switch (info.type) {
|
switch (info.type) {
|
||||||
case vk::ImageViewType::e3D:
|
case vk::ImageViewType::e3D: return info.base_layer == 0 && info.layer_count == 1;
|
||||||
return info.base_layer == 0 && info.layer_count == 1;
|
|
||||||
case vk::ImageViewType::e2D:
|
case vk::ImageViewType::e2D:
|
||||||
return static_cast<bool>(
|
return static_cast<bool>(image.flags &
|
||||||
image.flags & vk::ImageCreateFlagBits::e2DArrayCompatible) &&
|
vk::ImageCreateFlagBits::e2DArrayCompatible) &&
|
||||||
info.level_count == 1 && info.layer_count == 1;
|
info.level_count == 1 && info.layer_count == 1;
|
||||||
case vk::ImageViewType::e2DArray:
|
case vk::ImageViewType::e2DArray:
|
||||||
return static_cast<bool>(
|
return static_cast<bool>(image.flags &
|
||||||
image.flags & vk::ImageCreateFlagBits::e2DArrayCompatible) &&
|
vk::ImageCreateFlagBits::e2DArrayCompatible) &&
|
||||||
info.level_count == 1;
|
info.level_count == 1;
|
||||||
default: return false;
|
default: return false;
|
||||||
}
|
}
|
||||||
@@ -327,8 +326,7 @@ bool FormatsCompatible(vk::Format base, vk::Format view) noexcept {
|
|||||||
vk::ImageView Image::FindView(const ImageViewInfo& view_info) {
|
vk::ImageView Image::FindView(const ImageViewInfo& view_info) {
|
||||||
const auto& image = backing;
|
const auto& image = backing;
|
||||||
auto normalized = view_info;
|
auto normalized = view_info;
|
||||||
const bool is_storage =
|
const bool is_storage = static_cast<bool>(normalized.usage & vk::ImageUsageFlagBits::eStorage);
|
||||||
static_cast<bool>(normalized.usage & vk::ImageUsageFlagBits::eStorage);
|
|
||||||
normalized.aspect = FullAspectMask(image.format);
|
normalized.aspect = FullAspectMask(image.format);
|
||||||
if (normalized.aspect & vk::ImageAspectFlagBits::eDepth &&
|
if (normalized.aspect & vk::ImageAspectFlagBits::eDepth &&
|
||||||
IsDepthViewFormat(normalized.format)) {
|
IsDepthViewFormat(normalized.format)) {
|
||||||
@@ -340,12 +338,11 @@ vk::ImageView Image::FindView(const ImageViewInfo& view_info) {
|
|||||||
normalized.format = image.format;
|
normalized.format = image.format;
|
||||||
normalized.aspect = vk::ImageAspectFlagBits::eStencil;
|
normalized.aspect = vk::ImageAspectFlagBits::eStencil;
|
||||||
}
|
}
|
||||||
normalized.usage =
|
normalized.usage = is_storage ? vk::ImageUsageFlagBits::eStorage : vk::ImageUsageFlags {};
|
||||||
is_storage ? vk::ImageUsageFlagBits::eStorage : vk::ImageUsageFlags {};
|
|
||||||
const bool format_compatible = normalized.format != vk::Format::eUndefined &&
|
const bool format_compatible = normalized.format != vk::Format::eUndefined &&
|
||||||
IsCompatibleViewFormat(image.format, normalized.format);
|
IsCompatibleViewFormat(image.format, normalized.format);
|
||||||
const bool slice_view = image.image_type == vk::ImageType::e3D &&
|
const bool slice_view =
|
||||||
(normalized.type == vk::ImageViewType::e2D ||
|
image.image_type == vk::ImageType::e3D && (normalized.type == vk::ImageViewType::e2D ||
|
||||||
normalized.type == vk::ImageViewType::e2DArray);
|
normalized.type == vk::ImageViewType::e2DArray);
|
||||||
const bool levels_valid = normalized.level_count != 0 &&
|
const bool levels_valid = normalized.level_count != 0 &&
|
||||||
normalized.base_level < image.mip_levels &&
|
normalized.base_level < image.mip_levels &&
|
||||||
@@ -353,15 +350,14 @@ vk::ImageView Image::FindView(const ImageViewInfo& view_info) {
|
|||||||
const auto view_layers = slice_view && levels_valid
|
const auto view_layers = slice_view && levels_valid
|
||||||
? std::max(image.extent.depth >> normalized.base_level, 1u)
|
? std::max(image.extent.depth >> normalized.base_level, 1u)
|
||||||
: image.layers;
|
: image.layers;
|
||||||
const bool ranges_valid = levels_valid &&
|
const bool ranges_valid = levels_valid && normalized.layer_count != 0 &&
|
||||||
normalized.layer_count != 0 && normalized.base_layer < view_layers &&
|
normalized.base_layer < view_layers &&
|
||||||
normalized.layer_count <= view_layers - normalized.base_layer;
|
normalized.layer_count <= view_layers - normalized.base_layer;
|
||||||
const bool mapping_valid =
|
const bool mapping_valid =
|
||||||
IsComponentSwizzle(normalized.mapping.r) && IsComponentSwizzle(normalized.mapping.g) &&
|
IsComponentSwizzle(normalized.mapping.r) && IsComponentSwizzle(normalized.mapping.g) &&
|
||||||
IsComponentSwizzle(normalized.mapping.b) && IsComponentSwizzle(normalized.mapping.a);
|
IsComponentSwizzle(normalized.mapping.b) && IsComponentSwizzle(normalized.mapping.a);
|
||||||
if (image.image == nullptr || !format_compatible || !ranges_valid || !mapping_valid ||
|
if (image.image == nullptr || !format_compatible || !ranges_valid || !mapping_valid ||
|
||||||
!IsValidViewType(image, normalized) ||
|
!IsValidViewType(image, normalized) || !IsValidAspect(image, normalized.aspect)) {
|
||||||
!IsValidAspect(image, normalized.aspect)) {
|
|
||||||
EXIT("invalid image view: image_format=%d view_format=%d type=%d aspect=0x%x "
|
EXIT("invalid image view: image_format=%d view_format=%d type=%d aspect=0x%x "
|
||||||
"mip=%u+%u layer=%u+%u usage=0x%x image_levels=%u image_layers=%u\n",
|
"mip=%u+%u layer=%u+%u usage=0x%x image_levels=%u image_layers=%u\n",
|
||||||
static_cast<int>(image.format), static_cast<int>(normalized.format),
|
static_cast<int>(image.format), static_cast<int>(normalized.format),
|
||||||
|
|||||||
@@ -397,10 +397,10 @@ TextureUploadLayout TextureCalcUploadLayout(uint32_t fmt, uint64_t width, uint64
|
|||||||
return layout;
|
return layout;
|
||||||
}
|
}
|
||||||
|
|
||||||
std::vector<vk::BufferImageCopy>
|
std::vector<vk::BufferImageCopy> TextureBuildImageCopies(const TextureUploadLayout& layout,
|
||||||
TextureBuildImageCopies(const TextureUploadLayout& layout, uint32_t width, uint32_t height,
|
uint32_t width, uint32_t height,
|
||||||
uint32_t depth, uint64_t levels, bool array_texture,
|
uint32_t depth, uint64_t levels,
|
||||||
bool volume_texture) {
|
bool array_texture, bool volume_texture) {
|
||||||
uint32_t mip_width = width;
|
uint32_t mip_width = width;
|
||||||
uint32_t mip_height = height;
|
uint32_t mip_height = height;
|
||||||
uint32_t mip_pitch = volume_texture && static_cast<Prospero::TileMode>(layout.tile) !=
|
uint32_t mip_pitch = volume_texture && static_cast<Prospero::TileMode>(layout.tile) !=
|
||||||
@@ -418,10 +418,9 @@ TextureBuildImageCopies(const TextureUploadLayout& layout, uint32_t width, uint3
|
|||||||
for (uint32_t z = 0; z < mip_depth; z++) {
|
for (uint32_t z = 0; z < mip_depth; z++) {
|
||||||
const auto slice_offset = z * layout.slice_stride;
|
const auto slice_offset = z * layout.slice_stride;
|
||||||
vk::BufferImageCopy region {};
|
vk::BufferImageCopy region {};
|
||||||
region.bufferOffset =
|
region.bufferOffset = layout.level_sizes[i].offset + slice_offset;
|
||||||
layout.level_sizes[i].offset + slice_offset;
|
region.imageSubresource = {vk::ImageAspectFlagBits::eColor, i, array_texture ? z : 0,
|
||||||
region.imageSubresource = {vk::ImageAspectFlagBits::eColor, i,
|
1};
|
||||||
array_texture ? z : 0, 1};
|
|
||||||
region.imageOffset.z = volume_texture ? static_cast<int>(z) : 0;
|
region.imageOffset.z = volume_texture ? static_cast<int>(z) : 0;
|
||||||
region.imageExtent = {mip_width, mip_height, 1};
|
region.imageExtent = {mip_width, mip_height, 1};
|
||||||
const bool linear =
|
const bool linear =
|
||||||
@@ -434,8 +433,7 @@ TextureBuildImageCopies(const TextureUploadLayout& layout, uint32_t width, uint3
|
|||||||
return ((value + block - 1u) / block) * block;
|
return ((value + block - 1u) / block) * block;
|
||||||
};
|
};
|
||||||
const auto pitch = align(mip_pitch, layout.texel_block);
|
const auto pitch = align(mip_pitch, layout.texel_block);
|
||||||
region.bufferRowLength =
|
region.bufferRowLength = pitch > align(mip_width, layout.texel_block) ? pitch : 0;
|
||||||
pitch > align(mip_width, layout.texel_block) ? pitch : 0;
|
|
||||||
}
|
}
|
||||||
regions.push_back(region);
|
regions.push_back(region);
|
||||||
}
|
}
|
||||||
@@ -480,8 +478,7 @@ static bool SetGpuTileSize(uint64_t offset, uint64_t length, uint64_t capacity,
|
|||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
|
|
||||||
bool TextureBuildGpuTileInfos(uint64_t size,
|
bool TextureBuildGpuTileInfos(uint64_t size, const std::vector<vk::BufferImageCopy>& regions,
|
||||||
const std::vector<vk::BufferImageCopy>& regions,
|
|
||||||
const TextureUploadLayout& layout, uint32_t fmt, uint32_t depth,
|
const TextureUploadLayout& layout, uint32_t fmt, uint32_t depth,
|
||||||
uint64_t levels, std::vector<GpuTileInfo>& out_infos) {
|
uint64_t levels, std::vector<GpuTileInfo>& out_infos) {
|
||||||
if (size == 0 || levels == 0 || levels > 16 || depth == 0 ||
|
if (size == 0 || levels == 0 || levels > 16 || depth == 0 ||
|
||||||
@@ -522,9 +519,8 @@ bool TextureBuildGpuTileInfos(uint64_t size,
|
|||||||
for (uint32_t z = 0; z < mip_depth; z += block.block_depth) {
|
for (uint32_t z = 0; z < mip_depth; z += block.block_depth) {
|
||||||
const uint32_t copy_depth = std::min(block.block_depth, mip_depth - z);
|
const uint32_t copy_depth = std::min(block.block_depth, mip_depth - z);
|
||||||
const auto& region = regions[region_base + z];
|
const auto& region = regions[region_base + z];
|
||||||
const auto pitch = region.bufferRowLength != 0
|
const auto pitch =
|
||||||
? region.bufferRowLength
|
region.bufferRowLength != 0 ? region.bufferRowLength : region.imageExtent.width;
|
||||||
: region.imageExtent.width;
|
|
||||||
const auto logical_height = region.bufferImageHeight != 0
|
const auto logical_height = region.bufferImageHeight != 0
|
||||||
? region.bufferImageHeight
|
? region.bufferImageHeight
|
||||||
: region.imageExtent.height;
|
: region.imageExtent.height;
|
||||||
@@ -544,16 +540,13 @@ bool TextureBuildGpuTileInfos(uint64_t size,
|
|||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
info.linear_slice_stride = linear_stride;
|
info.linear_slice_stride = linear_stride;
|
||||||
info.width = std::max(
|
info.width =
|
||||||
(region.imageExtent.width + element.wide - 1u) / element.wide, 1u);
|
std::max((region.imageExtent.width + element.wide - 1u) / element.wide, 1u);
|
||||||
info.height = std::max(
|
info.height = std::max((logical_height + element.tall - 1u) / element.tall, 1u);
|
||||||
(logical_height + element.tall - 1u) / element.tall, 1u);
|
|
||||||
info.depth = copy_depth;
|
info.depth = copy_depth;
|
||||||
info.surface_z = block.block_depth == 1
|
info.surface_z =
|
||||||
? static_cast<uint32_t>(region.imageOffset.z)
|
block.block_depth == 1 ? static_cast<uint32_t>(region.imageOffset.z) : 0;
|
||||||
: 0;
|
info.pitch = std::max((pitch + element.wide - 1u) / element.wide, 1u);
|
||||||
info.pitch =
|
|
||||||
std::max((pitch + element.wide - 1u) / element.wide, 1u);
|
|
||||||
info.tail_x = tail ? volume.tail_x[level] : 0;
|
info.tail_x = tail ? volume.tail_x[level] : 0;
|
||||||
info.tail_y = tail ? volume.tail_y[level] : 0;
|
info.tail_y = tail ? volume.tail_y[level] : 0;
|
||||||
info.tail = tail;
|
info.tail = tail;
|
||||||
@@ -581,9 +574,8 @@ bool TextureBuildGpuTileInfos(uint64_t size,
|
|||||||
const auto level_depth = GetTextureLevelDepth(depth, level, layout.volume_texture);
|
const auto level_depth = GetTextureLevelDepth(depth, level, layout.volume_texture);
|
||||||
for (uint32_t z = 0; z < level_depth; z++) {
|
for (uint32_t z = 0; z < level_depth; z++) {
|
||||||
const auto& region = regions[region_index++];
|
const auto& region = regions[region_index++];
|
||||||
const auto pitch = region.bufferRowLength != 0
|
const auto pitch =
|
||||||
? region.bufferRowLength
|
region.bufferRowLength != 0 ? region.bufferRowLength : region.imageExtent.width;
|
||||||
: region.imageExtent.width;
|
|
||||||
const auto logical_height = region.bufferImageHeight != 0
|
const auto logical_height = region.bufferImageHeight != 0
|
||||||
? region.bufferImageHeight
|
? region.bufferImageHeight
|
||||||
: region.imageExtent.height;
|
: region.imageExtent.height;
|
||||||
@@ -597,16 +589,14 @@ bool TextureBuildGpuTileInfos(uint64_t size,
|
|||||||
info.tiled_size)) {
|
info.tiled_size)) {
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
info.width = std::max(
|
info.width =
|
||||||
(region.imageExtent.width + element.wide - 1u) / element.wide, 1u);
|
std::max((region.imageExtent.width + element.wide - 1u) / element.wide, 1u);
|
||||||
info.height = std::max(
|
info.height = std::max((logical_height + element.tall - 1u) / element.tall, 1u);
|
||||||
(logical_height + element.tall - 1u) / element.tall, 1u);
|
|
||||||
info.surface_z = base_family == TileBlockFamily::RenderTarget64KB ||
|
info.surface_z = base_family == TileBlockFamily::RenderTarget64KB ||
|
||||||
base_family == TileBlockFamily::Depth64KB
|
base_family == TileBlockFamily::Depth64KB
|
||||||
? region.imageSubresource.baseArrayLayer
|
? region.imageSubresource.baseArrayLayer
|
||||||
: 0;
|
: 0;
|
||||||
info.pitch =
|
info.pitch = std::max((pitch + element.wide - 1u) / element.wide, 1u);
|
||||||
std::max((pitch + element.wide - 1u) / element.wide, 1u);
|
|
||||||
info.tail = tail;
|
info.tail = tail;
|
||||||
info.tail_x = tail ? level_size.x : 0;
|
info.tail_x = tail ? level_size.x : 0;
|
||||||
info.tail_y = tail ? level_size.y : 0;
|
info.tail_y = tail ? level_size.y : 0;
|
||||||
|
|||||||
@@ -40,12 +40,11 @@ TextureUploadLayout TextureCalcUploadLayout(uint32_t fmt, uint64_t width, uint64
|
|||||||
uint64_t tile, uint64_t upload_size,
|
uint64_t tile, uint64_t upload_size,
|
||||||
bool allow_depth_tile, bool volume_texture,
|
bool allow_depth_tile, bool volume_texture,
|
||||||
const char* owner);
|
const char* owner);
|
||||||
std::vector<vk::BufferImageCopy>
|
std::vector<vk::BufferImageCopy> TextureBuildImageCopies(const TextureUploadLayout& layout,
|
||||||
TextureBuildImageCopies(const TextureUploadLayout& layout, uint32_t width, uint32_t height,
|
uint32_t width, uint32_t height,
|
||||||
uint32_t depth, uint64_t levels, bool array_texture,
|
uint32_t depth, uint64_t levels,
|
||||||
bool volume_texture);
|
bool array_texture, bool volume_texture);
|
||||||
bool TextureBuildGpuTileInfos(uint64_t size,
|
bool TextureBuildGpuTileInfos(uint64_t size, const std::vector<vk::BufferImageCopy>& regions,
|
||||||
const std::vector<vk::BufferImageCopy>& regions,
|
|
||||||
const TextureUploadLayout& layout, uint32_t fmt, uint32_t depth,
|
const TextureUploadLayout& layout, uint32_t fmt, uint32_t depth,
|
||||||
uint64_t levels, std::vector<GpuTileInfo>& infos);
|
uint64_t levels, std::vector<GpuTileInfo>& infos);
|
||||||
|
|
||||||
|
|||||||
@@ -14,9 +14,9 @@
|
|||||||
#include "gpu_tiler_shaders/gpu_tiler_standard64_spv.h"
|
#include "gpu_tiler_shaders/gpu_tiler_standard64_spv.h"
|
||||||
#include "gpu_tiler_shaders/gpu_tiler_swap_bgra16_spv.h"
|
#include "gpu_tiler_shaders/gpu_tiler_swap_bgra16_spv.h"
|
||||||
#include "graphics/host_gpu/graphicContext.h"
|
#include "graphics/host_gpu/graphicContext.h"
|
||||||
|
#include "graphics/host_gpu/renderer/cache/streamBuffer.h"
|
||||||
#include "graphics/host_gpu/renderer/commandScheduler.h"
|
#include "graphics/host_gpu/renderer/commandScheduler.h"
|
||||||
#include "graphics/host_gpu/renderer/image/image.h"
|
#include "graphics/host_gpu/renderer/image/image.h"
|
||||||
#include "graphics/host_gpu/renderer/cache/streamBuffer.h"
|
|
||||||
|
|
||||||
#include <algorithm>
|
#include <algorithm>
|
||||||
#include <array>
|
#include <array>
|
||||||
|
|||||||
@@ -116,8 +116,8 @@ PipelineCache::GraphicsPipeline& PipelineCache::CreateGraphicsPipeline(
|
|||||||
if (attachment_samples == 0) {
|
if (attachment_samples == 0) {
|
||||||
attachment_samples = depth.samples;
|
attachment_samples = depth.samples;
|
||||||
} else if (attachment_samples != depth.samples) {
|
} else if (attachment_samples != depth.samples) {
|
||||||
EXIT("mixed color/depth sample counts are unsupported: %u and %u\n",
|
EXIT("mixed color/depth sample counts are unsupported: %u and %u\n", attachment_samples,
|
||||||
attachment_samples, depth.samples);
|
depth.samples);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
EXIT_IF(attachment_samples == 0 ||
|
EXIT_IF(attachment_samples == 0 ||
|
||||||
@@ -203,9 +203,8 @@ PipelineCache::GraphicsPipeline& PipelineCache::CreateGraphicsPipeline(
|
|||||||
LogPipelineTrace("CreatePipelineInternal begin", vs_id.hash0, vs_id.crc32, ps_id.hash0,
|
LogPipelineTrace("CreatePipelineInternal begin", vs_id.hash0, vs_id.crc32, ps_id.hash0,
|
||||||
ps_id.crc32);
|
ps_id.crc32);
|
||||||
CreatePipelineInternal(m_graphics, m_descriptor_cache, *cached, rendering, vs_input_info,
|
CreatePipelineInternal(m_graphics, m_descriptor_cache, *cached, rendering, vs_input_info,
|
||||||
vs_spirv, ps_input_info,
|
vs_spirv, ps_input_info, ps_spirv, static_params, vs_id.hash0,
|
||||||
ps_spirv, static_params, vs_id.hash0, vs_id.crc32, ps_id.hash0,
|
vs_id.crc32, ps_id.hash0, ps_id.crc32, ps_active);
|
||||||
ps_id.crc32, ps_active);
|
|
||||||
LogPipelineTrace("CreatePipelineInternal done", vs_id.hash0, vs_id.crc32, ps_id.hash0,
|
LogPipelineTrace("CreatePipelineInternal done", vs_id.hash0, vs_id.crc32, ps_id.hash0,
|
||||||
ps_id.crc32);
|
ps_id.crc32);
|
||||||
|
|
||||||
|
|||||||
@@ -118,11 +118,12 @@ public:
|
|||||||
ShaderId cs_shader_id;
|
ShaderId cs_shader_id;
|
||||||
};
|
};
|
||||||
|
|
||||||
GraphicsPipeline& CreateGraphicsPipeline(
|
GraphicsPipeline&
|
||||||
RenderColorInfo* colors, uint32_t color_count, RenderDepthInfo& depth,
|
CreateGraphicsPipeline(RenderColorInfo* colors, uint32_t color_count, RenderDepthInfo& depth,
|
||||||
ShaderVertexInputInfo& vs_input_info, RenderCommandBuffer& command,
|
ShaderVertexInputInfo& vs_input_info, RenderCommandBuffer& command,
|
||||||
ShaderPixelInputInfo* ps_input_info, vk::PrimitiveTopology topology, bool ps_active,
|
ShaderPixelInputInfo* ps_input_info, vk::PrimitiveTopology topology,
|
||||||
std::span<const uint32_t> vs_spirv, std::span<const uint32_t> ps_spirv);
|
bool ps_active, std::span<const uint32_t> vs_spirv,
|
||||||
|
std::span<const uint32_t> ps_spirv);
|
||||||
ComputePipeline& CreateComputePipeline(ShaderComputeInputInfo& input_info,
|
ComputePipeline& CreateComputePipeline(ShaderComputeInputInfo& input_info,
|
||||||
const HW::ComputeShaderInfo& cs_regs,
|
const HW::ComputeShaderInfo& cs_regs,
|
||||||
std::span<const uint32_t> cs_spirv);
|
std::span<const uint32_t> cs_spirv);
|
||||||
@@ -211,16 +212,13 @@ private:
|
|||||||
|
|
||||||
void LogPipelineTrace(const char* phase, uint32_t vs_hash0, uint32_t vs_crc32, uint32_t ps_hash0,
|
void LogPipelineTrace(const char* phase, uint32_t vs_hash0, uint32_t vs_crc32, uint32_t ps_hash0,
|
||||||
uint32_t ps_crc32);
|
uint32_t ps_crc32);
|
||||||
void CreatePipelineInternal(GraphicContext& graphics, DescriptorCache& descriptor_cache,
|
void CreatePipelineInternal(
|
||||||
PipelineCache::GraphicsPipeline& pipeline,
|
GraphicContext& graphics, DescriptorCache& descriptor_cache,
|
||||||
const PipelineRenderingState& rendering,
|
PipelineCache::GraphicsPipeline& pipeline, const PipelineRenderingState& rendering,
|
||||||
const ShaderVertexInputInfo& vs_input_info,
|
const ShaderVertexInputInfo& vs_input_info, std::span<const uint32_t> vs_shader,
|
||||||
std::span<const uint32_t> vs_shader,
|
const ShaderPixelInputInfo* ps_input_info, std::span<const uint32_t> ps_shader,
|
||||||
const ShaderPixelInputInfo* ps_input_info,
|
const PipelineStaticParameters& static_params, uint32_t vs_hash0, uint32_t vs_crc32,
|
||||||
std::span<const uint32_t> ps_shader,
|
uint32_t ps_hash0, uint32_t ps_crc32, bool ps_active);
|
||||||
const PipelineStaticParameters& static_params, uint32_t vs_hash0,
|
|
||||||
uint32_t vs_crc32, uint32_t ps_hash0, uint32_t ps_crc32,
|
|
||||||
bool ps_active);
|
|
||||||
void CreatePipelineInternal(GraphicContext& graphics, DescriptorCache& descriptor_cache,
|
void CreatePipelineInternal(GraphicContext& graphics, DescriptorCache& descriptor_cache,
|
||||||
PipelineCache::ComputePipeline& pipeline,
|
PipelineCache::ComputePipeline& pipeline,
|
||||||
const ShaderComputeInputInfo& input_info,
|
const ShaderComputeInputInfo& input_info,
|
||||||
|
|||||||
@@ -8,10 +8,10 @@
|
|||||||
#include "graphics/host_gpu/renderer/debug.h"
|
#include "graphics/host_gpu/renderer/debug.h"
|
||||||
#include "graphics/host_gpu/renderer/pipeline/descriptorCache.h"
|
#include "graphics/host_gpu/renderer/pipeline/descriptorCache.h"
|
||||||
#include "graphics/host_gpu/renderer/pipeline/pipelineCache.h"
|
#include "graphics/host_gpu/renderer/pipeline/pipelineCache.h"
|
||||||
|
#include "graphics/host_gpu/renderer/pipeline/shaderSubgroup.h"
|
||||||
#include "graphics/host_gpu/renderer/render.h"
|
#include "graphics/host_gpu/renderer/render.h"
|
||||||
#include "graphics/host_gpu/renderer/renderContext.h"
|
#include "graphics/host_gpu/renderer/renderContext.h"
|
||||||
#include "graphics/host_gpu/renderer/renderTarget.h"
|
#include "graphics/host_gpu/renderer/renderTarget.h"
|
||||||
#include "graphics/host_gpu/renderer/pipeline/shaderSubgroup.h"
|
|
||||||
#include "graphics/host_gpu/vulkanCommon.h"
|
#include "graphics/host_gpu/vulkanCommon.h"
|
||||||
#include "graphics/shader/recompiler/ir/ShaderIR.h"
|
#include "graphics/shader/recompiler/ir/ShaderIR.h"
|
||||||
#include "graphics/shader/shader.h"
|
#include "graphics/shader/shader.h"
|
||||||
@@ -386,8 +386,7 @@ static vk::BlendOp GetBlendOp(uint32_t op) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
static void CreateLayout(DescriptorCache& descriptor_cache,
|
static void CreateLayout(DescriptorCache& descriptor_cache,
|
||||||
std::span<vk::DescriptorSetLayout> set_layouts,
|
std::span<vk::DescriptorSetLayout> set_layouts, uint32_t& set_layouts_num,
|
||||||
uint32_t& set_layouts_num,
|
|
||||||
std::span<vk::PushConstantRange> push_constant_info,
|
std::span<vk::PushConstantRange> push_constant_info,
|
||||||
uint32_t& push_constant_info_num,
|
uint32_t& push_constant_info_num,
|
||||||
const ShaderRecompiler::IR::Program& program,
|
const ShaderRecompiler::IR::Program& program,
|
||||||
@@ -412,8 +411,7 @@ static void CreateLayout(DescriptorCache& descriptor_cache,
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
static void ConfigureSubgroupSize(const GraphicContext& graphics,
|
static void ConfigureSubgroupSize(const GraphicContext& graphics, vk::ShaderStageFlagBits vk_stage,
|
||||||
vk::ShaderStageFlagBits vk_stage,
|
|
||||||
const ShaderRecompiler::IR::Program& program,
|
const ShaderRecompiler::IR::Program& program,
|
||||||
vk::PipelineShaderStageRequiredSubgroupSizeCreateInfo& required,
|
vk::PipelineShaderStageRequiredSubgroupSizeCreateInfo& required,
|
||||||
vk::PipelineShaderStageCreateInfo& stage) {
|
vk::PipelineShaderStageCreateInfo& stage) {
|
||||||
@@ -456,16 +454,13 @@ static void ConfigureSubgroupSize(const GraphicContext&
|
|||||||
}
|
}
|
||||||
|
|
||||||
// NOLINTNEXTLINE(readability-function-cognitive-complexity)
|
// NOLINTNEXTLINE(readability-function-cognitive-complexity)
|
||||||
void CreatePipelineInternal(GraphicContext& graphics, DescriptorCache& descriptor_cache,
|
void CreatePipelineInternal(
|
||||||
PipelineCache::GraphicsPipeline& pipeline,
|
GraphicContext& graphics, DescriptorCache& descriptor_cache,
|
||||||
const PipelineRenderingState& rendering,
|
PipelineCache::GraphicsPipeline& pipeline, const PipelineRenderingState& rendering,
|
||||||
const ShaderVertexInputInfo& vs_input_info,
|
const ShaderVertexInputInfo& vs_input_info, std::span<const uint32_t> vs_shader,
|
||||||
std::span<const uint32_t> vs_shader,
|
const ShaderPixelInputInfo* ps_input_info, std::span<const uint32_t> ps_shader,
|
||||||
const ShaderPixelInputInfo* ps_input_info,
|
const PipelineStaticParameters& static_params, uint32_t vs_hash0, uint32_t vs_crc32,
|
||||||
std::span<const uint32_t> ps_shader,
|
uint32_t ps_hash0, uint32_t ps_crc32, bool ps_active) {
|
||||||
const PipelineStaticParameters& static_params, uint32_t vs_hash0,
|
|
||||||
uint32_t vs_crc32, uint32_t ps_hash0, uint32_t ps_crc32,
|
|
||||||
bool ps_active) {
|
|
||||||
EXIT_IF(ps_active && ps_input_info == nullptr);
|
EXIT_IF(ps_active && ps_input_info == nullptr);
|
||||||
|
|
||||||
vk::ShaderModule vert_shader_module = nullptr;
|
vk::ShaderModule vert_shader_module = nullptr;
|
||||||
@@ -511,8 +506,7 @@ void CreatePipelineInternal(GraphicContext& graphics, DescriptorCache& descripto
|
|||||||
vert_shader_stage_info.pName = "main";
|
vert_shader_stage_info.pName = "main";
|
||||||
vert_shader_stage_info.pSpecializationInfo = nullptr;
|
vert_shader_stage_info.pSpecializationInfo = nullptr;
|
||||||
EXIT_IF(!vs_input_info.stage);
|
EXIT_IF(!vs_input_info.stage);
|
||||||
ConfigureSubgroupSize(graphics, vk::ShaderStageFlagBits::eVertex,
|
ConfigureSubgroupSize(graphics, vk::ShaderStageFlagBits::eVertex, *vs_input_info.stage.program,
|
||||||
*vs_input_info.stage.program,
|
|
||||||
vert_subgroup_size, vert_shader_stage_info);
|
vert_subgroup_size, vert_shader_stage_info);
|
||||||
|
|
||||||
vk::PipelineShaderStageCreateInfo frag_shader_stage_info {};
|
vk::PipelineShaderStageCreateInfo frag_shader_stage_info {};
|
||||||
@@ -527,8 +521,8 @@ void CreatePipelineInternal(GraphicContext& graphics, DescriptorCache& descripto
|
|||||||
if (ps_active) {
|
if (ps_active) {
|
||||||
EXIT_IF(!ps_input_info->stage);
|
EXIT_IF(!ps_input_info->stage);
|
||||||
ConfigureSubgroupSize(graphics, vk::ShaderStageFlagBits::eFragment,
|
ConfigureSubgroupSize(graphics, vk::ShaderStageFlagBits::eFragment,
|
||||||
*ps_input_info->stage.program,
|
*ps_input_info->stage.program, frag_subgroup_size,
|
||||||
frag_subgroup_size, frag_shader_stage_info);
|
frag_shader_stage_info);
|
||||||
}
|
}
|
||||||
|
|
||||||
vk::PipelineShaderStageCreateInfo shader_stages[] = {vert_shader_stage_info,
|
vk::PipelineShaderStageCreateInfo shader_stages[] = {vert_shader_stage_info,
|
||||||
@@ -838,15 +832,13 @@ void CreatePipelineInternal(GraphicContext& graphics, DescriptorCache& descripto
|
|||||||
|
|
||||||
EXIT_IF(!vs_input_info.stage);
|
EXIT_IF(!vs_input_info.stage);
|
||||||
CreateLayout(descriptor_cache, set_layouts, set_layouts_num, push_constant_info,
|
CreateLayout(descriptor_cache, set_layouts, set_layouts_num, push_constant_info,
|
||||||
push_constant_info_num,
|
push_constant_info_num, *vs_input_info.stage.program,
|
||||||
*vs_input_info.stage.program, vk::ShaderStageFlagBits::eVertex,
|
vk::ShaderStageFlagBits::eVertex, DescriptorCache::Stage::Vertex);
|
||||||
DescriptorCache::Stage::Vertex);
|
|
||||||
if (ps_active) {
|
if (ps_active) {
|
||||||
EXIT_IF(!ps_input_info->stage);
|
EXIT_IF(!ps_input_info->stage);
|
||||||
CreateLayout(descriptor_cache, set_layouts, set_layouts_num, push_constant_info,
|
CreateLayout(descriptor_cache, set_layouts, set_layouts_num, push_constant_info,
|
||||||
push_constant_info_num,
|
push_constant_info_num, *ps_input_info->stage.program,
|
||||||
*ps_input_info->stage.program, vk::ShaderStageFlagBits::eFragment,
|
vk::ShaderStageFlagBits::eFragment, DescriptorCache::Stage::Pixel);
|
||||||
DescriptorCache::Stage::Pixel);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
vk::PipelineLayoutCreateInfo pipeline_layout_info {};
|
vk::PipelineLayoutCreateInfo pipeline_layout_info {};
|
||||||
@@ -1012,8 +1004,7 @@ void CreatePipelineInternal(GraphicContext& graphics, DescriptorCache& descripto
|
|||||||
comp_shader_stage_info.pName = "main";
|
comp_shader_stage_info.pName = "main";
|
||||||
comp_shader_stage_info.pSpecializationInfo = nullptr;
|
comp_shader_stage_info.pSpecializationInfo = nullptr;
|
||||||
EXIT_IF(!input_info.stage);
|
EXIT_IF(!input_info.stage);
|
||||||
ConfigureSubgroupSize(graphics, vk::ShaderStageFlagBits::eCompute,
|
ConfigureSubgroupSize(graphics, vk::ShaderStageFlagBits::eCompute, *input_info.stage.program,
|
||||||
*input_info.stage.program,
|
|
||||||
comp_subgroup_size, comp_shader_stage_info);
|
comp_subgroup_size, comp_shader_stage_info);
|
||||||
|
|
||||||
vk::DescriptorSetLayout set_layouts[1] = {};
|
vk::DescriptorSetLayout set_layouts[1] = {};
|
||||||
@@ -1024,9 +1015,8 @@ void CreatePipelineInternal(GraphicContext& graphics, DescriptorCache& descripto
|
|||||||
|
|
||||||
EXIT_IF(!input_info.stage);
|
EXIT_IF(!input_info.stage);
|
||||||
CreateLayout(descriptor_cache, set_layouts, set_layouts_num, push_constant_info,
|
CreateLayout(descriptor_cache, set_layouts, set_layouts_num, push_constant_info,
|
||||||
push_constant_info_num,
|
push_constant_info_num, *input_info.stage.program,
|
||||||
*input_info.stage.program, vk::ShaderStageFlagBits::eCompute,
|
vk::ShaderStageFlagBits::eCompute, DescriptorCache::Stage::Compute);
|
||||||
DescriptorCache::Stage::Compute);
|
|
||||||
|
|
||||||
vk::PipelineLayoutCreateInfo pipeline_layout_info {};
|
vk::PipelineLayoutCreateInfo pipeline_layout_info {};
|
||||||
pipeline_layout_info.sType = vk::StructureType::ePipelineLayoutCreateInfo;
|
pipeline_layout_info.sType = vk::StructureType::ePipelineLayoutCreateInfo;
|
||||||
|
|||||||
@@ -10,14 +10,14 @@
|
|||||||
#include "graphics/guest_gpu/graphicsRun.h"
|
#include "graphics/guest_gpu/graphicsRun.h"
|
||||||
#include "graphics/guest_gpu/hardwareContext.h"
|
#include "graphics/guest_gpu/hardwareContext.h"
|
||||||
#include "graphics/host_gpu/graphicContext.h"
|
#include "graphics/host_gpu/graphicContext.h"
|
||||||
|
#include "graphics/host_gpu/renderer/image/imageInfo.h"
|
||||||
#include "graphics/host_gpu/renderer/pipeline/descriptorCache.h"
|
#include "graphics/host_gpu/renderer/pipeline/descriptorCache.h"
|
||||||
#include "graphics/host_gpu/renderer/pipeline/descriptors.h"
|
#include "graphics/host_gpu/renderer/pipeline/descriptors.h"
|
||||||
#include "graphics/host_gpu/renderer/image/imageInfo.h"
|
|
||||||
#include "graphics/host_gpu/renderer/pipeline/pipelineCache.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/shaderResourceBarrier.h"
|
||||||
#include "graphics/host_gpu/renderer/pipeline/shaderSubgroup.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/host_gpu/vulkanCommon.h"
|
||||||
#include "graphics/shader/recompiler/ir/ResourceMaterialization.h"
|
#include "graphics/shader/recompiler/ir/ResourceMaterialization.h"
|
||||||
#include "graphics/shader/recompiler/ir/ShaderIR.h"
|
#include "graphics/shader/recompiler/ir/ShaderIR.h"
|
||||||
|
|||||||
@@ -14,8 +14,7 @@ namespace Libs::Graphics {
|
|||||||
RenderContext::RenderContext(GraphicContext& graphics)
|
RenderContext::RenderContext(GraphicContext& graphics)
|
||||||
: m_graphics(graphics), m_render_executor(*this), m_command_scheduler(*this, graphics),
|
: m_graphics(graphics), m_render_executor(*this), m_command_scheduler(*this, graphics),
|
||||||
m_descriptor_cache(graphics), m_pipeline_cache(graphics, m_descriptor_cache),
|
m_descriptor_cache(graphics), m_pipeline_cache(graphics, m_descriptor_cache),
|
||||||
m_sampler_cache(graphics),
|
m_sampler_cache(graphics), m_gpu_resources(graphics, m_command_scheduler) {
|
||||||
m_gpu_resources(graphics, m_command_scheduler) {
|
|
||||||
EXIT_NOT_IMPLEMENTED(!Common::Thread::IsMainThread());
|
EXIT_NOT_IMPLEMENTED(!Common::Thread::IsMainThread());
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -99,8 +98,7 @@ void RenderContext::TriggerEopEvent(uint32_t context_id) {
|
|||||||
registration.eq, static_cast<uintptr_t>(registration.id),
|
registration.eq, static_cast<uintptr_t>(registration.id),
|
||||||
LibKernel::EventQueue::KERNEL_EVFILT_GRAPHICS,
|
LibKernel::EventQueue::KERNEL_EVFILT_GRAPHICS,
|
||||||
reinterpret_cast<void*>(static_cast<uintptr_t>(context_id)));
|
reinterpret_cast<void*>(static_cast<uintptr_t>(context_id)));
|
||||||
if (result == LibKernel::KERNEL_ERROR_EBADF ||
|
if (result == LibKernel::KERNEL_ERROR_EBADF || result == LibKernel::KERNEL_ERROR_ENOENT) {
|
||||||
result == LibKernel::KERNEL_ERROR_ENOENT) {
|
|
||||||
DeleteEopEq(registration.eq, registration.id);
|
DeleteEopEq(registration.eq, registration.id);
|
||||||
continue;
|
continue;
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -6,12 +6,12 @@
|
|||||||
#include "common/common.h"
|
#include "common/common.h"
|
||||||
#include "common/threads.h"
|
#include "common/threads.h"
|
||||||
#include "graphics/host_gpu/renderer/cache/bufferCache.h"
|
#include "graphics/host_gpu/renderer/cache/bufferCache.h"
|
||||||
#include "graphics/host_gpu/renderer/commandScheduler.h"
|
|
||||||
#include "graphics/host_gpu/renderer/pipeline/descriptorCache.h"
|
|
||||||
#include "graphics/host_gpu/renderer/cache/gpuResourceManager.h"
|
#include "graphics/host_gpu/renderer/cache/gpuResourceManager.h"
|
||||||
#include "graphics/host_gpu/renderer/pipeline/pipelineCache.h"
|
|
||||||
#include "graphics/host_gpu/renderer/cache/samplerCache.h"
|
#include "graphics/host_gpu/renderer/cache/samplerCache.h"
|
||||||
#include "graphics/host_gpu/renderer/cache/textureCache.h"
|
#include "graphics/host_gpu/renderer/cache/textureCache.h"
|
||||||
|
#include "graphics/host_gpu/renderer/commandScheduler.h"
|
||||||
|
#include "graphics/host_gpu/renderer/pipeline/descriptorCache.h"
|
||||||
|
#include "graphics/host_gpu/renderer/pipeline/pipelineCache.h"
|
||||||
#include "kernel/eventQueue.h"
|
#include "kernel/eventQueue.h"
|
||||||
|
|
||||||
#include <memory>
|
#include <memory>
|
||||||
|
|||||||
@@ -252,8 +252,8 @@ uint64_t PrepareVideoOutFlip(CommandBuffer& buffer, int handle, int index, int f
|
|||||||
for (;;) {
|
for (;;) {
|
||||||
uint64_t request_id = 0;
|
uint64_t request_id = 0;
|
||||||
auto& video_out = buffer.GetContext().GetVideoOut();
|
auto& video_out = buffer.GetContext().GetVideoOut();
|
||||||
const auto result = video_out.SubmitFlipFromGpu(
|
const auto result =
|
||||||
buffer, handle, index, flip_mode, flip_arg, request_id);
|
video_out.SubmitFlipFromGpu(buffer, handle, index, flip_mode, flip_arg, request_id);
|
||||||
if (result == OK) {
|
if (result == OK) {
|
||||||
EXIT_IF(request_id == 0);
|
EXIT_IF(request_id == 0);
|
||||||
return request_id;
|
return request_id;
|
||||||
|
|||||||
@@ -122,8 +122,8 @@ uint64_t GraphicContext::GetDeviceMemoryUsage() const {
|
|||||||
physical_device_properties.deviceType == vk::PhysicalDeviceType::eDiscreteGpu;
|
physical_device_properties.deviceType == vk::PhysicalDeviceType::eDiscreteGpu;
|
||||||
uint64_t usage = 0;
|
uint64_t usage = 0;
|
||||||
for (uint32_t heap = 0; heap < physical_device_memory_properties.memoryHeapCount; heap++) {
|
for (uint32_t heap = 0; heap < physical_device_memory_properties.memoryHeapCount; heap++) {
|
||||||
const bool device_local = static_cast<bool>(
|
const bool device_local =
|
||||||
physical_device_memory_properties.memoryHeaps[heap].flags &
|
static_cast<bool>(physical_device_memory_properties.memoryHeaps[heap].flags &
|
||||||
vk::MemoryHeapFlagBits::eDeviceLocal);
|
vk::MemoryHeapFlagBits::eDeviceLocal);
|
||||||
if (!discrete || device_local) {
|
if (!discrete || device_local) {
|
||||||
usage += budgets[heap].usage;
|
usage += budgets[heap].usage;
|
||||||
@@ -160,8 +160,7 @@ uint64_t GraphicContext::GetTotalMemoryBudget() const {
|
|||||||
}
|
}
|
||||||
constexpr uint64_t system_reserve = 8ull * 1024 * 1024 * 1024;
|
constexpr uint64_t system_reserve = 8ull * 1024 * 1024 * 1024;
|
||||||
const auto available = budget > usage ? budget - usage : uint64_t {0};
|
const auto available = budget > usage ? budget - usage : uint64_t {0};
|
||||||
return std::max(local,
|
return std::max(local, available > system_reserve ? available - system_reserve : uint64_t {0});
|
||||||
available > system_reserve ? available - system_reserve : uint64_t {0});
|
|
||||||
}
|
}
|
||||||
|
|
||||||
void GraphicContext::CreateBuffer(uint64_t size, VulkanBuffer& buffer) {
|
void GraphicContext::CreateBuffer(uint64_t size, VulkanBuffer& buffer) {
|
||||||
|
|||||||
@@ -359,9 +359,9 @@ static void TriggerVideoOutEvents(VideoOutConfig& video_out, VideoOutEventKind k
|
|||||||
if (!registration || registration->generation != video_out.generation) {
|
if (!registration || registration->generation != video_out.generation) {
|
||||||
continue;
|
continue;
|
||||||
}
|
}
|
||||||
const auto result = EventQueue::KernelTriggerEvent(
|
const auto result =
|
||||||
registration->handle, VideoOutEventId(kind), EventQueue::KERNEL_EVFILT_VIDEO_OUT,
|
EventQueue::KernelTriggerEvent(registration->handle, VideoOutEventId(kind),
|
||||||
trigger_data);
|
EventQueue::KERNEL_EVFILT_VIDEO_OUT, trigger_data);
|
||||||
EXIT_NOT_IMPLEMENTED(result != OK && result != LibKernel::KERNEL_ERROR_EBADF &&
|
EXIT_NOT_IMPLEMENTED(result != OK && result != LibKernel::KERNEL_ERROR_EBADF &&
|
||||||
result != LibKernel::KERNEL_ERROR_ENOENT);
|
result != LibKernel::KERNEL_ERROR_ENOENT);
|
||||||
}
|
}
|
||||||
@@ -372,9 +372,8 @@ static void DeleteVideoOutEvents(const VideoOutEventQueues& queues, VideoOutEven
|
|||||||
if (!registration) {
|
if (!registration) {
|
||||||
continue;
|
continue;
|
||||||
}
|
}
|
||||||
const auto result =
|
const auto result = EventQueue::KernelDeleteEvent(
|
||||||
EventQueue::KernelDeleteEvent(registration->handle, VideoOutEventId(kind),
|
registration->handle, VideoOutEventId(kind), EventQueue::KERNEL_EVFILT_VIDEO_OUT);
|
||||||
EventQueue::KERNEL_EVFILT_VIDEO_OUT);
|
|
||||||
EXIT_NOT_IMPLEMENTED(result != OK && result != LibKernel::KERNEL_ERROR_EBADF &&
|
EXIT_NOT_IMPLEMENTED(result != OK && result != LibKernel::KERNEL_ERROR_EBADF &&
|
||||||
result != LibKernel::KERNEL_ERROR_ENOENT);
|
result != LibKernel::KERNEL_ERROR_ENOENT);
|
||||||
}
|
}
|
||||||
@@ -425,17 +424,15 @@ static int RegisterVideoOutEvent(int handle, EventQueue::KernelEqueue eq, VideoO
|
|||||||
bool add_queue = false;
|
bool add_queue = false;
|
||||||
{
|
{
|
||||||
Common::LockGuard event_lock(event_state->mutex);
|
Common::LockGuard event_lock(event_state->mutex);
|
||||||
const auto existing = std::find_if(queues.begin(), queues.end(), [&](const auto& candidate) {
|
const auto existing =
|
||||||
|
std::find_if(queues.begin(), queues.end(), [&](const auto& candidate) {
|
||||||
return candidate->handle == eq && candidate->generation == generation;
|
return candidate->handle == eq && candidate->generation == generation;
|
||||||
});
|
});
|
||||||
if (existing != queues.end()) {
|
if (existing != queues.end()) {
|
||||||
registration = *existing;
|
registration = *existing;
|
||||||
} else {
|
} else {
|
||||||
registration = std::make_shared<VideoOutEventRegistration>(
|
registration = std::make_shared<VideoOutEventRegistration>(VideoOutEventRegistration {
|
||||||
VideoOutEventRegistration {.handle = eq,
|
.handle = eq, .state = event_state, .generation = generation, .kind = kind});
|
||||||
.state = event_state,
|
|
||||||
.generation = generation,
|
|
||||||
.kind = kind});
|
|
||||||
queues.push_back(registration);
|
queues.push_back(registration);
|
||||||
add_queue = true;
|
add_queue = true;
|
||||||
}
|
}
|
||||||
@@ -814,8 +811,8 @@ void VideoOutDriver::Impl::PresentThread(std::stop_token token) {
|
|||||||
m_presenter.Present(*frame, true);
|
m_presenter.Present(*frame, true);
|
||||||
}
|
}
|
||||||
const auto frame_end = Common::Timer::QueryPerformanceCounter();
|
const auto frame_end = Common::Timer::QueryPerformanceCounter();
|
||||||
total_wait += static_cast<int64_t>(period) -
|
total_wait +=
|
||||||
static_cast<int64_t>(frame_end - frame_begin);
|
static_cast<int64_t>(period) - static_cast<int64_t>(frame_end - frame_begin);
|
||||||
continue;
|
continue;
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -841,8 +838,7 @@ void VideoOutDriver::Impl::PresentThread(std::stop_token token) {
|
|||||||
VblankEnd();
|
VblankEnd();
|
||||||
|
|
||||||
const auto frame_end = Common::Timer::QueryPerformanceCounter();
|
const auto frame_end = Common::Timer::QueryPerformanceCounter();
|
||||||
total_wait += static_cast<int64_t>(period) -
|
total_wait += static_cast<int64_t>(period) - static_cast<int64_t>(frame_end - frame_begin);
|
||||||
static_cast<int64_t>(frame_end - frame_begin);
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -1000,8 +996,8 @@ void FlipQueue::Prepare(uint64_t request_id, Graphics::CommandBuffer& buffer) {
|
|||||||
}
|
}
|
||||||
Graphics::Presenter::Frame* frame = nullptr;
|
Graphics::Presenter::Frame* frame = nullptr;
|
||||||
if (special) {
|
if (special) {
|
||||||
frame = &m_presenter.PrepareBlankFrame(width, height,
|
frame = &m_presenter.PrepareBlankFrame(width, height, index == VIDEO_OUT_BUFFER_INDEX_BLACK,
|
||||||
index == VIDEO_OUT_BUFFER_INDEX_BLACK, &buffer);
|
&buffer);
|
||||||
} else {
|
} else {
|
||||||
frame = &m_presenter.PrepareFrame(buffer, source_info);
|
frame = &m_presenter.PrepareFrame(buffer, source_info);
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -206,21 +206,18 @@ private:
|
|||||||
vk::Format m_format = vk::Format::eUndefined;
|
vk::Format m_format = vk::Format::eUndefined;
|
||||||
};
|
};
|
||||||
|
|
||||||
void Presenter::Frame::Configure(GraphicContext& graphics, vk::Extent2D extent,
|
void Presenter::Frame::Configure(GraphicContext& graphics, vk::Extent2D extent, vk::Format format) {
|
||||||
vk::Format format) {
|
|
||||||
if (extent.width == 0 || extent.height == 0 || format == vk::Format::eUndefined) {
|
if (extent.width == 0 || extent.height == 0 || format == vk::Format::eUndefined) {
|
||||||
EXIT("unsupported prepared frame, extent=%ux%u format=%d\n", extent.width, extent.height,
|
EXIT("unsupported prepared frame, extent=%ux%u format=%d\n", extent.width, extent.height,
|
||||||
static_cast<int>(format));
|
static_cast<int>(format));
|
||||||
}
|
}
|
||||||
const auto features = graphics.GetFormatProperties(format).optimalTilingFeatures;
|
const auto features = graphics.GetFormatProperties(format).optimalTilingFeatures;
|
||||||
const auto required = vk::FormatFeatureFlagBits::eBlitSrc |
|
const auto required =
|
||||||
vk::FormatFeatureFlagBits::eSampledImageFilterLinear |
|
vk::FormatFeatureFlagBits::eBlitSrc | vk::FormatFeatureFlagBits::eSampledImageFilterLinear |
|
||||||
vk::FormatFeatureFlagBits::eTransferSrc |
|
vk::FormatFeatureFlagBits::eTransferSrc | vk::FormatFeatureFlagBits::eTransferDst;
|
||||||
vk::FormatFeatureFlagBits::eTransferDst;
|
|
||||||
if ((features & required) != required) {
|
if ((features & required) != required) {
|
||||||
EXIT("prepared presentation format lacks optimal blit support: format=%d features=0x%x\n",
|
EXIT("prepared presentation format lacks optimal blit support: format=%d features=0x%x\n",
|
||||||
static_cast<int>(format),
|
static_cast<int>(format), static_cast<vk::FormatFeatureFlags::MaskType>(features));
|
||||||
static_cast<vk::FormatFeatureFlags::MaskType>(features));
|
|
||||||
}
|
}
|
||||||
|
|
||||||
auto& dst = image;
|
auto& dst = image;
|
||||||
@@ -267,8 +264,7 @@ void Presenter::Frame::Transit(vk::CommandBuffer command, vk::ImageLayout layout
|
|||||||
? vk::PipelineStageFlagBits2::eTransfer
|
? vk::PipelineStageFlagBits2::eTransfer
|
||||||
: vk::PipelineStageFlagBits2::eAllCommands;
|
: vk::PipelineStageFlagBits2::eAllCommands;
|
||||||
constexpr auto writes = vk::AccessFlagBits2::eTransferWrite |
|
constexpr auto writes = vk::AccessFlagBits2::eTransferWrite |
|
||||||
vk::AccessFlagBits2::eShaderWrite |
|
vk::AccessFlagBits2::eShaderWrite | vk::AccessFlagBits2::eMemoryWrite;
|
||||||
vk::AccessFlagBits2::eMemoryWrite;
|
|
||||||
if (image.state.layout == layout && image.state.access_mask == access &&
|
if (image.state.layout == layout && image.state.access_mask == access &&
|
||||||
!static_cast<bool>(image.state.access_mask & writes)) {
|
!static_cast<bool>(image.state.access_mask & writes)) {
|
||||||
return;
|
return;
|
||||||
@@ -299,35 +295,27 @@ void Presenter::Frame::Transit(vk::CommandBuffer command, vk::ImageLayout layout
|
|||||||
void Presenter::Frame::CopyFrom(CommandBuffer& command_buffer, Image& source) {
|
void Presenter::Frame::CopyFrom(CommandBuffer& command_buffer, Image& source) {
|
||||||
command_buffer.EndRendering();
|
command_buffer.EndRendering();
|
||||||
auto command = command_buffer.Handle();
|
auto command = command_buffer.Handle();
|
||||||
source.Transit(vk::ImageLayout::eTransferSrcOptimal,
|
source.Transit(vk::ImageLayout::eTransferSrcOptimal, vk::AccessFlagBits2::eTransferRead, {},
|
||||||
vk::AccessFlagBits2::eTransferRead, {}, command);
|
command);
|
||||||
Transit(command, vk::ImageLayout::eTransferDstOptimal,
|
Transit(command, vk::ImageLayout::eTransferDstOptimal, vk::AccessFlagBits2::eTransferWrite);
|
||||||
vk::AccessFlagBits2::eTransferWrite);
|
|
||||||
vk::ImageCopy copy {};
|
vk::ImageCopy copy {};
|
||||||
copy.srcSubresource = {vk::ImageAspectFlagBits::eColor, 0, 0,
|
copy.srcSubresource = {vk::ImageAspectFlagBits::eColor, 0, 0, source.backing.layers};
|
||||||
source.backing.layers};
|
|
||||||
copy.dstSubresource = {vk::ImageAspectFlagBits::eColor, 0, 0, image.layers};
|
copy.dstSubresource = {vk::ImageAspectFlagBits::eColor, 0, 0, image.layers};
|
||||||
copy.extent = {std::min(source.backing.extent.width, image.extent.width),
|
copy.extent = {std::min(source.backing.extent.width, image.extent.width),
|
||||||
std::min(source.backing.extent.height, image.extent.height), 1};
|
std::min(source.backing.extent.height, image.extent.height), 1};
|
||||||
EXIT_IF(copy.srcSubresource.layerCount != copy.dstSubresource.layerCount);
|
EXIT_IF(copy.srcSubresource.layerCount != copy.dstSubresource.layerCount);
|
||||||
command.copyImage(source.backing.image, vk::ImageLayout::eTransferSrcOptimal,
|
command.copyImage(source.backing.image, vk::ImageLayout::eTransferSrcOptimal, image.image,
|
||||||
image.image, vk::ImageLayout::eTransferDstOptimal, copy);
|
vk::ImageLayout::eTransferDstOptimal, copy);
|
||||||
Transit(command, vk::ImageLayout::eTransferSrcOptimal,
|
Transit(command, vk::ImageLayout::eTransferSrcOptimal, vk::AccessFlagBits2::eTransferRead);
|
||||||
vk::AccessFlagBits2::eTransferRead);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
void Presenter::Frame::Clear(CommandBuffer& command_buffer,
|
void Presenter::Frame::Clear(CommandBuffer& command_buffer, const vk::ClearColorValue& color) {
|
||||||
const vk::ClearColorValue& color) {
|
|
||||||
command_buffer.EndRendering();
|
command_buffer.EndRendering();
|
||||||
auto command = command_buffer.Handle();
|
auto command = command_buffer.Handle();
|
||||||
Transit(command, vk::ImageLayout::eTransferDstOptimal,
|
Transit(command, vk::ImageLayout::eTransferDstOptimal, vk::AccessFlagBits2::eTransferWrite);
|
||||||
vk::AccessFlagBits2::eTransferWrite);
|
const vk::ImageSubresourceRange range {vk::ImageAspectFlagBits::eColor, 0, 1, 0, 1};
|
||||||
const vk::ImageSubresourceRange range {
|
command.clearColorImage(image.image, vk::ImageLayout::eTransferDstOptimal, &color, 1, &range);
|
||||||
vk::ImageAspectFlagBits::eColor, 0, 1, 0, 1};
|
Transit(command, vk::ImageLayout::eTransferSrcOptimal, vk::AccessFlagBits2::eTransferRead);
|
||||||
command.clearColorImage(image.image, vk::ImageLayout::eTransferDstOptimal, &color, 1,
|
|
||||||
&range);
|
|
||||||
Transit(command, vk::ImageLayout::eTransferSrcOptimal,
|
|
||||||
vk::AccessFlagBits2::eTransferRead);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
class Swapchain final {
|
class Swapchain final {
|
||||||
@@ -441,16 +429,12 @@ void Swapchain::Create() {
|
|||||||
? vk::CompositeAlphaFlagBitsKHR::eOpaque
|
? vk::CompositeAlphaFlagBitsKHR::eOpaque
|
||||||
: vk::CompositeAlphaFlagBitsKHR::eInherit;
|
: vk::CompositeAlphaFlagBitsKHR::eInherit;
|
||||||
|
|
||||||
vk::SurfaceFormatKHR format {vk::Format::eR8G8B8A8Unorm,
|
vk::SurfaceFormatKHR format {vk::Format::eR8G8B8A8Unorm, vk::ColorSpaceKHR::eSrgbNonlinear};
|
||||||
vk::ColorSpaceKHR::eSrgbNonlinear};
|
if (surface.formats.size() != 1 || surface.formats.front().format != vk::Format::eUndefined) {
|
||||||
if (surface.formats.size() != 1 ||
|
|
||||||
surface.formats.front().format != vk::Format::eUndefined) {
|
|
||||||
const auto it = std::find_if(surface.formats.begin(), surface.formats.end(),
|
const auto it = std::find_if(surface.formats.begin(), surface.formats.end(),
|
||||||
[](const vk::SurfaceFormatKHR& candidate) {
|
[](const vk::SurfaceFormatKHR& candidate) {
|
||||||
return candidate.format ==
|
return candidate.format == vk::Format::eB8G8R8A8Unorm ||
|
||||||
vk::Format::eB8G8R8A8Unorm ||
|
candidate.format == vk::Format::eR8G8B8A8Unorm;
|
||||||
candidate.format ==
|
|
||||||
vk::Format::eR8G8B8A8Unorm;
|
|
||||||
});
|
});
|
||||||
if (it == surface.formats.end()) {
|
if (it == surface.formats.end()) {
|
||||||
EXIT("no supported UNORM swapchain format\n");
|
EXIT("no supported UNORM swapchain format\n");
|
||||||
@@ -458,8 +442,7 @@ void Swapchain::Create() {
|
|||||||
format = *it;
|
format = *it;
|
||||||
}
|
}
|
||||||
m_format = format.format;
|
m_format = format.format;
|
||||||
const auto swapchain_features =
|
const auto swapchain_features = graphics.GetFormatProperties(m_format).optimalTilingFeatures;
|
||||||
graphics.GetFormatProperties(m_format).optimalTilingFeatures;
|
|
||||||
if (!static_cast<bool>(swapchain_features & vk::FormatFeatureFlagBits::eBlitDst)) {
|
if (!static_cast<bool>(swapchain_features & vk::FormatFeatureFlagBits::eBlitDst)) {
|
||||||
EXIT("swapchain format cannot be a blit destination: format=%d\n",
|
EXIT("swapchain format cannot be a blit destination: format=%d\n",
|
||||||
static_cast<int>(m_format));
|
static_cast<int>(m_format));
|
||||||
@@ -503,8 +486,7 @@ void Swapchain::Create() {
|
|||||||
view.subresourceRange.baseMipLevel = 0;
|
view.subresourceRange.baseMipLevel = 0;
|
||||||
view.subresourceRange.layerCount = 1;
|
view.subresourceRange.layerCount = 1;
|
||||||
view.subresourceRange.levelCount = 1;
|
view.subresourceRange.levelCount = 1;
|
||||||
RequireVulkanSuccess(
|
RequireVulkanSuccess(graphics.device.createImageView(&view, nullptr, &m_image_views[i]),
|
||||||
graphics.device.createImageView(&view, nullptr, &m_image_views[i]),
|
|
||||||
"vkCreateImageView");
|
"vkCreateImageView");
|
||||||
EXIT_IF(m_image_views[i] == nullptr);
|
EXIT_IF(m_image_views[i] == nullptr);
|
||||||
}
|
}
|
||||||
@@ -683,10 +665,9 @@ void Swapchain::RecordPresentCommands(CommandBuffer& command, VulkanImage& sourc
|
|||||||
to_present.subresourceRange.levelCount = 1;
|
to_present.subresourceRange.levelCount = 1;
|
||||||
to_present.subresourceRange.baseArrayLayer = 0;
|
to_present.subresourceRange.baseArrayLayer = 0;
|
||||||
to_present.subresourceRange.layerCount = 1;
|
to_present.subresourceRange.layerCount = 1;
|
||||||
vk_command.pipelineBarrier(vk::PipelineStageFlagBits::eAllCommands,
|
vk_command.pipelineBarrier(
|
||||||
vk::PipelineStageFlagBits::eAllCommands,
|
vk::PipelineStageFlagBits::eAllCommands, vk::PipelineStageFlagBits::eAllCommands,
|
||||||
vk::DependencyFlagBits::eByRegion, 0,
|
vk::DependencyFlagBits::eByRegion, 0, nullptr, 0, nullptr, 1, &to_present);
|
||||||
nullptr, 0, nullptr, 1, &to_present);
|
|
||||||
command.End();
|
command.End();
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -752,8 +733,7 @@ Presenter::Frame& Presenter::PrepareFrame(CommandBuffer& buffer, const ImageInfo
|
|||||||
default: break;
|
default: break;
|
||||||
}
|
}
|
||||||
frame->Configure(m_impl->window.graphic_ctx,
|
frame->Configure(m_impl->window.graphic_ctx,
|
||||||
{image.backing.extent.width, image.backing.extent.height},
|
{image.backing.extent.width, image.backing.extent.height}, frame_format);
|
||||||
frame_format);
|
|
||||||
frame->CopyFrom(buffer, image);
|
frame->CopyFrom(buffer, image);
|
||||||
return *frame;
|
return *frame;
|
||||||
}
|
}
|
||||||
@@ -772,8 +752,7 @@ Presenter::Frame& Presenter::PrepareBlankFrame(uint32_t width, uint32_t height,
|
|||||||
frame->Clear(*producer, clear);
|
frame->Clear(*producer, clear);
|
||||||
} else {
|
} else {
|
||||||
if (frame->present_commands == nullptr) {
|
if (frame->present_commands == nullptr) {
|
||||||
frame->present_commands =
|
frame->present_commands = std::make_unique<CommandBuffer>(m_impl->present_scheduler);
|
||||||
std::make_unique<CommandBuffer>(m_impl->present_scheduler);
|
|
||||||
}
|
}
|
||||||
auto& command = *frame->present_commands;
|
auto& command = *frame->present_commands;
|
||||||
command.WaitForFenceAndReset();
|
command.WaitForFenceAndReset();
|
||||||
@@ -830,8 +809,7 @@ void Presenter::Present(Frame& frame, bool reuse) {
|
|||||||
continue;
|
continue;
|
||||||
}
|
}
|
||||||
if (frame.present_commands == nullptr) {
|
if (frame.present_commands == nullptr) {
|
||||||
frame.present_commands =
|
frame.present_commands = std::make_unique<CommandBuffer>(m_impl->present_scheduler);
|
||||||
std::make_unique<CommandBuffer>(m_impl->present_scheduler);
|
|
||||||
}
|
}
|
||||||
{
|
{
|
||||||
Common::LockGuard render_lock(m_impl->renderer.GetMutex());
|
Common::LockGuard render_lock(m_impl->renderer.GetMutex());
|
||||||
|
|||||||
@@ -32,11 +32,11 @@
|
|||||||
#include "graphics/host_gpu/vma.h"
|
#include "graphics/host_gpu/vma.h"
|
||||||
#include "graphics/host_gpu/vulkanCommon.h"
|
#include "graphics/host_gpu/vulkanCommon.h"
|
||||||
#include "graphics/presentation/presenter.h"
|
#include "graphics/presentation/presenter.h"
|
||||||
#include "kernel/memory.h"
|
|
||||||
#include "graphics/presentation/renderDoc.h"
|
#include "graphics/presentation/renderDoc.h"
|
||||||
#include "graphics/presentation/videoOut.h"
|
#include "graphics/presentation/videoOut.h"
|
||||||
#include "graphics/presentation/window.h"
|
#include "graphics/presentation/window.h"
|
||||||
#include "graphics/presentation/window/windowInternal.h"
|
#include "graphics/presentation/window/windowInternal.h"
|
||||||
|
#include "kernel/memory.h"
|
||||||
#include "libs/controller.h"
|
#include "libs/controller.h"
|
||||||
#include "loader/systemContent.h"
|
#include "loader/systemContent.h"
|
||||||
|
|
||||||
@@ -909,10 +909,9 @@ void WindowContext::CreateVulkan() {
|
|||||||
}
|
}
|
||||||
surface = native_surface;
|
surface = native_surface;
|
||||||
|
|
||||||
std::vector<const char*> device_extensions = {VK_KHR_SWAPCHAIN_EXTENSION_NAME,
|
std::vector<const char*> device_extensions = {
|
||||||
VK_EXT_DEPTH_CLIP_CONTROL_EXTENSION_NAME,
|
VK_KHR_SWAPCHAIN_EXTENSION_NAME, VK_EXT_DEPTH_CLIP_CONTROL_EXTENSION_NAME,
|
||||||
VK_KHR_PUSH_DESCRIPTOR_EXTENSION_NAME,
|
VK_KHR_PUSH_DESCRIPTOR_EXTENSION_NAME, "VK_KHR_maintenance1"};
|
||||||
"VK_KHR_maintenance1"};
|
|
||||||
|
|
||||||
#if defined(__APPLE__)
|
#if defined(__APPLE__)
|
||||||
// MoltenVK lacks VK_EXT_depth_clip_enable and VK_EXT_color_write_enable; the renderer
|
// MoltenVK lacks VK_EXT_depth_clip_enable and VK_EXT_color_write_enable; the renderer
|
||||||
@@ -932,8 +931,8 @@ void WindowContext::CreateVulkan() {
|
|||||||
|
|
||||||
uint32_t queue_family = static_cast<uint32_t>(-1);
|
uint32_t queue_family = static_cast<uint32_t>(-1);
|
||||||
|
|
||||||
VulkanFindPhysicalDevice(graphic_ctx.instance, surface, device_extensions,
|
VulkanFindPhysicalDevice(graphic_ctx.instance, surface, device_extensions, surface_capabilities,
|
||||||
surface_capabilities, graphic_ctx.physical_device, queue_family);
|
graphic_ctx.physical_device, queue_family);
|
||||||
|
|
||||||
if (graphic_ctx.physical_device == nullptr) {
|
if (graphic_ctx.physical_device == nullptr) {
|
||||||
EXIT("Could not find suitable device");
|
EXIT("Could not find suitable device");
|
||||||
@@ -949,9 +948,8 @@ void WindowContext::CreateVulkan() {
|
|||||||
auto available_extensions = EnumerateVulkan<vk::ExtensionProperties>(
|
auto available_extensions = EnumerateVulkan<vk::ExtensionProperties>(
|
||||||
"vkEnumerateDeviceExtensionProperties",
|
"vkEnumerateDeviceExtensionProperties",
|
||||||
[&](uint32_t* count, vk::ExtensionProperties* values) {
|
[&](uint32_t* count, vk::ExtensionProperties* values) {
|
||||||
return graphic_ctx.physical_device.enumerateDeviceExtensionProperties(nullptr,
|
return graphic_ctx.physical_device.enumerateDeviceExtensionProperties(
|
||||||
count,
|
nullptr, count, values);
|
||||||
values);
|
|
||||||
});
|
});
|
||||||
|
|
||||||
if (HasExtension(available_extensions, VK_EXT_MEMORY_BUDGET_EXTENSION_NAME)) {
|
if (HasExtension(available_extensions, VK_EXT_MEMORY_BUDGET_EXTENSION_NAME)) {
|
||||||
|
|||||||
@@ -1,7 +1,5 @@
|
|||||||
#include "graphics/presentation/window.h"
|
#include "graphics/presentation/window.h"
|
||||||
|
|
||||||
#include <cstdlib>
|
|
||||||
|
|
||||||
#include "SDL.h"
|
#include "SDL.h"
|
||||||
#include "SDL_error.h"
|
#include "SDL_error.h"
|
||||||
#include "SDL_events.h"
|
#include "SDL_events.h"
|
||||||
@@ -40,6 +38,7 @@
|
|||||||
|
|
||||||
#include <algorithm>
|
#include <algorithm>
|
||||||
#include <cstdio>
|
#include <cstdio>
|
||||||
|
#include <cstdlib>
|
||||||
#include <cstring>
|
#include <cstring>
|
||||||
#include <memory>
|
#include <memory>
|
||||||
#include <string>
|
#include <string>
|
||||||
@@ -251,9 +250,7 @@ static void GameEventKeyboard(WindowLoopState& game, const EventKeyboard& key) {
|
|||||||
if (key.down) {
|
if (key.down) {
|
||||||
switch (key.key_code) {
|
switch (key.key_code) {
|
||||||
case SDLK_ESCAPE: game.need_exit = true; break;
|
case SDLK_ESCAPE: game.need_exit = true; break;
|
||||||
case SDLK_SPACE:
|
case SDLK_SPACE: SetPause(game, !game.paused.load(std::memory_order_acquire)); break;
|
||||||
SetPause(game, !game.paused.load(std::memory_order_acquire));
|
|
||||||
break;
|
|
||||||
case SDLK_F1:
|
case SDLK_F1:
|
||||||
if (!key.repeat) {
|
if (!key.repeat) {
|
||||||
RenderDocRequestCapture();
|
RenderDocRequestCapture();
|
||||||
@@ -390,7 +387,9 @@ void WindowContext::Resize(uint32_t new_width, uint32_t new_height) {
|
|||||||
void WindowContext::ProcessWindowEvent(const SDL_WindowEvent& event) {
|
void WindowContext::ProcessWindowEvent(const SDL_WindowEvent& event) {
|
||||||
const auto& window_event = event;
|
const auto& window_event = event;
|
||||||
switch (window_event.event) {
|
switch (window_event.event) {
|
||||||
case SDL_WINDOWEVENT_SHOWN: LOGF("Window %" PRIu32 " shown\n", window_event.windowID); break;
|
case SDL_WINDOWEVENT_SHOWN:
|
||||||
|
LOGF("Window %" PRIu32 " shown\n", window_event.windowID);
|
||||||
|
break;
|
||||||
|
|
||||||
case SDL_WINDOWEVENT_HIDDEN:
|
case SDL_WINDOWEVENT_HIDDEN:
|
||||||
LOGF("Window %" PRIu32 " hidden\n", window_event.windowID);
|
LOGF("Window %" PRIu32 " hidden\n", window_event.windowID);
|
||||||
@@ -401,13 +400,13 @@ void WindowContext::ProcessWindowEvent(const SDL_WindowEvent& event) {
|
|||||||
break;
|
break;
|
||||||
|
|
||||||
case SDL_WINDOWEVENT_MOVED:
|
case SDL_WINDOWEVENT_MOVED:
|
||||||
LOGF("Window %" PRIu32 " moved to %" PRId32 ",%" PRId32 "\n",
|
LOGF("Window %" PRIu32 " moved to %" PRId32 ",%" PRId32 "\n", window_event.windowID,
|
||||||
window_event.windowID, window_event.data1, window_event.data2);
|
window_event.data1, window_event.data2);
|
||||||
break;
|
break;
|
||||||
|
|
||||||
case SDL_WINDOWEVENT_RESIZED:
|
case SDL_WINDOWEVENT_RESIZED:
|
||||||
LOGF("Window %" PRIu32 " resized to %" PRId32 "x%" PRId32 "\n",
|
LOGF("Window %" PRIu32 " resized to %" PRId32 "x%" PRId32 "\n", window_event.windowID,
|
||||||
window_event.windowID, window_event.data1, window_event.data2);
|
window_event.data1, window_event.data2);
|
||||||
|
|
||||||
LOGF("m: %d\n", static_cast<int>(SDL_ThreadID()));
|
LOGF("m: %d\n", static_cast<int>(SDL_ThreadID()));
|
||||||
Resize(window_event.data1, window_event.data2);
|
Resize(window_event.data1, window_event.data2);
|
||||||
@@ -807,8 +806,7 @@ static void WindowCreate(WindowContext& context) {
|
|||||||
window_flags |= static_cast<uint32_t>(SDL_WINDOW_BORDERLESS);
|
window_flags |= static_cast<uint32_t>(SDL_WINDOW_BORDERLESS);
|
||||||
}
|
}
|
||||||
#endif
|
#endif
|
||||||
context.window =
|
context.window = SDL_CreateWindow(KYTY_SDL_WINDOW_CAPTION, KYTY_SDL_WINDOWPOS_CENTERED,
|
||||||
SDL_CreateWindow(KYTY_SDL_WINDOW_CAPTION, KYTY_SDL_WINDOWPOS_CENTERED,
|
|
||||||
KYTY_SDL_WINDOWPOS_CENTERED, width, height, window_flags);
|
KYTY_SDL_WINDOWPOS_CENTERED, width, height, window_flags);
|
||||||
|
|
||||||
context.window_hidden = true;
|
context.window_hidden = true;
|
||||||
@@ -950,11 +948,11 @@ void WindowContext::UpdateTitle() {
|
|||||||
fps_frames = 0;
|
fps_frames = 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
auto fps = fmt::format("{}{}{}{}{}{}[{}] [{}], frame: {}, fps: {:f}", (has_title ? title : ""),
|
auto fps =
|
||||||
|
fmt::format("{}{}{}{}{}{}[{}] [{}], frame: {}, fps: {:f}", (has_title ? title : ""),
|
||||||
(has_title ? ", " : ""), (has_title_id ? title_id : ""),
|
(has_title ? ", " : ""), (has_title_id ? title_id : ""),
|
||||||
(has_title_id ? ", " : ""), (has_app_ver ? app_ver : ""),
|
(has_title_id ? ", " : ""), (has_app_ver ? app_ver : ""),
|
||||||
(has_app_ver ? " " : ""), device_name, processor_name,
|
(has_app_ver ? " " : ""), device_name, processor_name, frame_num, current_fps);
|
||||||
frame_num, current_fps);
|
|
||||||
|
|
||||||
#if defined(__APPLE__)
|
#if defined(__APPLE__)
|
||||||
// AppKit traps on title changes off the main thread; fire-and-forget keeps present pacing.
|
// AppKit traps on title changes off the main thread; fire-and-forget keeps present pacing.
|
||||||
|
|||||||
@@ -38,8 +38,7 @@ struct WindowContext {
|
|||||||
~WindowContext();
|
~WindowContext();
|
||||||
KYTY_CLASS_NO_COPY(WindowContext);
|
KYTY_CLASS_NO_COPY(WindowContext);
|
||||||
|
|
||||||
[[nodiscard]] static vk::PhysicalDeviceVulkan13Features
|
[[nodiscard]] static vk::PhysicalDeviceVulkan13Features RequiredVulkan13Features() noexcept;
|
||||||
RequiredVulkan13Features() noexcept;
|
|
||||||
void CreateVulkan();
|
void CreateVulkan();
|
||||||
void RecreateSurface();
|
void RecreateSurface();
|
||||||
void RefreshSurfaceCapabilities();
|
void RefreshSurfaceCapabilities();
|
||||||
|
|||||||
@@ -13,8 +13,8 @@
|
|||||||
#include "graphics/guest_gpu/graphicsRun.h"
|
#include "graphics/guest_gpu/graphicsRun.h"
|
||||||
#include "graphics/guest_gpu/hardwareContext.h"
|
#include "graphics/guest_gpu/hardwareContext.h"
|
||||||
#include "graphics/host_gpu/renderer/renderContext.h"
|
#include "graphics/host_gpu/renderer/renderContext.h"
|
||||||
#include "graphics/shader/recompiler/decompiler/ShaderDecoder.h"
|
|
||||||
#include "graphics/shader/recompiler/ShaderRecompiler.h"
|
#include "graphics/shader/recompiler/ShaderRecompiler.h"
|
||||||
|
#include "graphics/shader/recompiler/decompiler/ShaderDecoder.h"
|
||||||
#include "graphics/shader/shaderVertexMetadata.h"
|
#include "graphics/shader/shaderVertexMetadata.h"
|
||||||
#include "libs/errno.h"
|
#include "libs/errno.h"
|
||||||
#include "spirv-tools/libspirv.h"
|
#include "spirv-tools/libspirv.h"
|
||||||
@@ -828,8 +828,7 @@ static void ShaderGetStaticInputInfoPS(
|
|||||||
vs_info.stage.program != nullptr && !vs_info.stage.program->bindings.descriptors.empty()
|
vs_info.stage.program != nullptr && !vs_info.stage.program->bindings.descriptors.empty()
|
||||||
? 1
|
? 1
|
||||||
: 0;
|
: 0;
|
||||||
ps_info.push_constant_offset =
|
ps_info.push_constant_offset = vs_info.stage.program != nullptr
|
||||||
vs_info.stage.program != nullptr
|
|
||||||
? vs_info.stage.program->bindings.push_constant_offset +
|
? vs_info.stage.program->bindings.push_constant_offset +
|
||||||
vs_info.stage.program->bindings.push_constant_size
|
vs_info.stage.program->bindings.push_constant_size
|
||||||
: 0;
|
: 0;
|
||||||
@@ -1294,8 +1293,7 @@ static void DumpShaderRecompilerSpirv(const char* type, uint64_t shader_hash,
|
|||||||
|
|
||||||
static std::atomic_int id = 0;
|
static std::atomic_int id = 0;
|
||||||
|
|
||||||
const auto base_name =
|
const auto base_name = Config::GetShaderLogFolder() /
|
||||||
Config::GetShaderLogFolder() /
|
|
||||||
fmt::format("{:04d}_new_shader_{}_{:016x}", id++, type, shader_hash);
|
fmt::format("{:04d}_new_shader_{}_{:016x}", id++, type, shader_hash);
|
||||||
Common::File::CreateDirectories(base_name.parent_path());
|
Common::File::CreateDirectories(base_name.parent_path());
|
||||||
|
|
||||||
@@ -1345,8 +1343,7 @@ static void DumpShaderRecompilerOriginal(const char* type, uint64_t shader_hash,
|
|||||||
|
|
||||||
static std::atomic_int id = 0;
|
static std::atomic_int id = 0;
|
||||||
|
|
||||||
const auto base_name =
|
const auto base_name = Config::GetShaderLogFolder() / "original" /
|
||||||
Config::GetShaderLogFolder() / "original" /
|
|
||||||
fmt::format("{:04d}_new_shader_{}_{:016x}", id++, type, shader_hash);
|
fmt::format("{:04d}_new_shader_{}_{:016x}", id++, type, shader_hash);
|
||||||
Common::File::CreateDirectories(base_name.parent_path());
|
Common::File::CreateDirectories(base_name.parent_path());
|
||||||
|
|
||||||
|
|||||||
@@ -459,8 +459,7 @@ int KYTY_SYSV_ABI KernelAddUserEvent(KernelEqueue eq, int id) {
|
|||||||
int KYTY_SYSV_ABI KernelAddUserEventEdge(KernelEqueue eq, int id) {
|
int KYTY_SYSV_ABI KernelAddUserEventEdge(KernelEqueue eq, int id) {
|
||||||
PRINT_NAME();
|
PRINT_NAME();
|
||||||
|
|
||||||
LOGF("\t user event edge add: eq = 0x%016" PRIx64 ", id = %d\n", static_cast<uint64_t>(eq),
|
LOGF("\t user event edge add: eq = 0x%016" PRIx64 ", id = %d\n", static_cast<uint64_t>(eq), id);
|
||||||
id);
|
|
||||||
|
|
||||||
KernelEqueueEvent event {};
|
KernelEqueueEvent event {};
|
||||||
event.event.ident = static_cast<uintptr_t>(id);
|
event.event.ident = static_cast<uintptr_t>(id);
|
||||||
@@ -507,8 +506,7 @@ int KYTY_SYSV_ABI KernelTriggerUserEventForAll(int id, void* udata) {
|
|||||||
int KYTY_SYSV_ABI KernelDeleteUserEvent(KernelEqueue eq, int id) {
|
int KYTY_SYSV_ABI KernelDeleteUserEvent(KernelEqueue eq, int id) {
|
||||||
PRINT_NAME();
|
PRINT_NAME();
|
||||||
|
|
||||||
LOGF("\t user event delete: eq = 0x%016" PRIx64 ", id = %d\n", static_cast<uint64_t>(eq),
|
LOGF("\t user event delete: eq = 0x%016" PRIx64 ", id = %d\n", static_cast<uint64_t>(eq), id);
|
||||||
id);
|
|
||||||
|
|
||||||
return KernelDeleteEvent(eq, static_cast<uintptr_t>(id), KERNEL_EVFILT_USER);
|
return KernelDeleteEvent(eq, static_cast<uintptr_t>(id), KERNEL_EVFILT_USER);
|
||||||
}
|
}
|
||||||
@@ -577,8 +575,7 @@ int KYTY_SYSV_ABI KernelAddAmprSystemEvent(KernelEqueue eq, int id, void* udata)
|
|||||||
int KYTY_SYSV_ABI KernelDeleteAmprEvent(KernelEqueue eq, int id) {
|
int KYTY_SYSV_ABI KernelDeleteAmprEvent(KernelEqueue eq, int id) {
|
||||||
PRINT_NAME();
|
PRINT_NAME();
|
||||||
|
|
||||||
LOGF("\t AMPR event delete: eq = 0x%016" PRIx64 ", id = %d\n", static_cast<uint64_t>(eq),
|
LOGF("\t AMPR event delete: eq = 0x%016" PRIx64 ", id = %d\n", static_cast<uint64_t>(eq), id);
|
||||||
id);
|
|
||||||
|
|
||||||
if (eq != KERNEL_EQUEUE_INVALID) {
|
if (eq != KERNEL_EQUEUE_INVALID) {
|
||||||
(void)KernelDeleteEvent(eq, static_cast<uintptr_t>(id), KERNEL_EVFILT_USER);
|
(void)KernelDeleteEvent(eq, static_cast<uintptr_t>(id), KERNEL_EVFILT_USER);
|
||||||
@@ -590,8 +587,8 @@ int KYTY_SYSV_ABI KernelDeleteAmprEvent(KernelEqueue eq, int id) {
|
|||||||
int KYTY_SYSV_ABI KernelDeleteAmprSystemEvent(KernelEqueue eq, int id) {
|
int KYTY_SYSV_ABI KernelDeleteAmprSystemEvent(KernelEqueue eq, int id) {
|
||||||
PRINT_NAME();
|
PRINT_NAME();
|
||||||
|
|
||||||
LOGF("\t AMPR system event delete: eq = 0x%016" PRIx64 ", id = %d\n",
|
LOGF("\t AMPR system event delete: eq = 0x%016" PRIx64 ", id = %d\n", static_cast<uint64_t>(eq),
|
||||||
static_cast<uint64_t>(eq), id);
|
id);
|
||||||
|
|
||||||
return KernelDeleteAmprEvent(eq, id);
|
return KernelDeleteAmprEvent(eq, id);
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -206,8 +206,8 @@ bool ConfigurationItem::operator<(const QTreeWidgetItem& other) const {
|
|||||||
GetStatusText(other_item->m_info->game_status);
|
GetStatusText(other_item->m_info->game_status);
|
||||||
case GameVersionColumn:
|
case GameVersionColumn:
|
||||||
case FirmwareVersionColumn: {
|
case FirmwareVersionColumn: {
|
||||||
const auto& version = column == GameVersionColumn ? m_info->gameVersion
|
const auto& version =
|
||||||
: m_info->firmwareVer;
|
column == GameVersionColumn ? m_info->gameVersion : m_info->firmwareVer;
|
||||||
const auto& other_version = column == GameVersionColumn
|
const auto& other_version = column == GameVersionColumn
|
||||||
? other_item->m_info->gameVersion
|
? other_item->m_info->gameVersion
|
||||||
: other_item->m_info->firmwareVer;
|
: other_item->m_info->firmwareVer;
|
||||||
|
|||||||
@@ -1,6 +1,5 @@
|
|||||||
#include "configurationListWidget.h"
|
#include "configurationListWidget.h"
|
||||||
|
|
||||||
#include "patchesDialog.h"
|
|
||||||
#include "common.h"
|
#include "common.h"
|
||||||
#include "compatibilityDatabase.h"
|
#include "compatibilityDatabase.h"
|
||||||
#include "configuration.h"
|
#include "configuration.h"
|
||||||
@@ -8,6 +7,7 @@
|
|||||||
#include "configurationItem.h"
|
#include "configurationItem.h"
|
||||||
#include "gameListTreeWidget.h"
|
#include "gameListTreeWidget.h"
|
||||||
#include "mainDialog.h"
|
#include "mainDialog.h"
|
||||||
|
#include "patchesDialog.h"
|
||||||
#include "trophyViewerDialog.h"
|
#include "trophyViewerDialog.h"
|
||||||
|
|
||||||
#include <QAbstractItemModel>
|
#include <QAbstractItemModel>
|
||||||
|
|||||||
@@ -272,10 +272,18 @@ static bool FindTerminal(QString* program, QStringList* prefix) {
|
|||||||
};
|
};
|
||||||
|
|
||||||
static const TerminalSpec candidates[] = {
|
static const TerminalSpec candidates[] = {
|
||||||
{"x-terminal-emulator", "-e"}, {"gnome-terminal", "--"}, {"konsole", "-e"},
|
{"x-terminal-emulator", "-e"},
|
||||||
{"xfce4-terminal", "-x"}, {"mate-terminal", "--"}, {"tilix", "-e"},
|
{"gnome-terminal", "--"},
|
||||||
{"alacritty", "-e"}, {"kitty", nullptr}, {"foot", nullptr},
|
{"konsole", "-e"},
|
||||||
{"wezterm", "-e"}, {"urxvt", "-e"}, {"xterm", "-e"},
|
{"xfce4-terminal", "-x"},
|
||||||
|
{"mate-terminal", "--"},
|
||||||
|
{"tilix", "-e"},
|
||||||
|
{"alacritty", "-e"},
|
||||||
|
{"kitty", nullptr},
|
||||||
|
{"foot", nullptr},
|
||||||
|
{"wezterm", "-e"},
|
||||||
|
{"urxvt", "-e"},
|
||||||
|
{"xterm", "-e"},
|
||||||
};
|
};
|
||||||
|
|
||||||
const auto try_candidate = [program, prefix](const QString& executable, const char* separator) {
|
const auto try_candidate = [program, prefix](const QString& executable, const char* separator) {
|
||||||
@@ -379,9 +387,9 @@ void MainDialog::RunInterpreter(QProcess* process, const Configuration& info) {
|
|||||||
#if !defined(_WIN32)
|
#if !defined(_WIN32)
|
||||||
// Report immediate launch failures.
|
// Report immediate launch failures.
|
||||||
if (!process->waitForStarted(5000)) {
|
if (!process->waitForStarted(5000)) {
|
||||||
QMessageBox::critical(this, tr("Error"),
|
QMessageBox::critical(
|
||||||
tr("Failed to start:\n%1\n\n%2")
|
this, tr("Error"),
|
||||||
.arg(process->program(), process->errorString()));
|
tr("Failed to start:\n%1\n\n%2").arg(process->program(), process->errorString()));
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
#endif
|
#endif
|
||||||
|
|||||||
@@ -59,10 +59,8 @@ void PatchesDialog::Load() {
|
|||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
const auto patches = QJsonDocument::fromJson(file.readAll())
|
const auto patches =
|
||||||
.object()
|
QJsonDocument::fromJson(file.readAll()).object().value(QStringLiteral("patches")).toArray();
|
||||||
.value(QStringLiteral("patches"))
|
|
||||||
.toArray();
|
|
||||||
for (const auto& value: patches) {
|
for (const auto& value: patches) {
|
||||||
const auto patch = value.toObject();
|
const auto patch = value.toObject();
|
||||||
auto* item = new QListWidgetItem(patch.value(QStringLiteral("name")).toString(), m_patches);
|
auto* item = new QListWidgetItem(patch.value(QStringLiteral("name")).toString(), m_patches);
|
||||||
|
|||||||
@@ -439,7 +439,8 @@ int KYTY_SYSV_ABI SaveDataMount3(const SaveDataMount3* mount, SaveDataMountResul
|
|||||||
|
|
||||||
Common::LockGuard lock(g_mount_mutex);
|
Common::LockGuard lock(g_mount_mutex);
|
||||||
const std::string dir_name = mount->dir_name->data;
|
const std::string dir_name = mount->dir_name->data;
|
||||||
const std::string mount_dir = std::string(SAVE_DATA_DIR) + "/" + get_title_id() + "/" + dir_name;
|
const std::string mount_dir =
|
||||||
|
std::string(SAVE_DATA_DIR) + "/" + get_title_id() + "/" + dir_name;
|
||||||
const bool create = ((mount->mount_mode & 4u) != 0);
|
const bool create = ((mount->mount_mode & 4u) != 0);
|
||||||
const bool create2 = ((mount->mount_mode & 32u) != 0);
|
const bool create2 = ((mount->mount_mode & 32u) != 0);
|
||||||
const bool open = (!create && !create2 && ((mount->mount_mode & 3u) != 0));
|
const bool open = (!create && !create2 && ((mount->mount_mode & 3u) != 0));
|
||||||
@@ -595,7 +596,8 @@ int KYTY_SYSV_ABI SaveDataTransferringMount(const SaveDataTransferringMount* mou
|
|||||||
|
|
||||||
Common::LockGuard lock(g_mount_mutex);
|
Common::LockGuard lock(g_mount_mutex);
|
||||||
const std::string dir_name = mount->dir_name->data;
|
const std::string dir_name = mount->dir_name->data;
|
||||||
const std::string mount_dir = std::string(SAVE_DATA_DIR) + "/" + get_title_id() + "/" + dir_name;
|
const std::string mount_dir =
|
||||||
|
std::string(SAVE_DATA_DIR) + "/" + get_title_id() + "/" + dir_name;
|
||||||
const int slot = g_mount_slots.FindAvailable(dir_name);
|
const int slot = g_mount_slots.FindAvailable(dir_name);
|
||||||
if (slot == SaveDataMountSlots::BUSY) {
|
if (slot == SaveDataMountSlots::BUSY) {
|
||||||
return SAVE_DATA_ERROR_BUSY;
|
return SAVE_DATA_ERROR_BUSY;
|
||||||
|
|||||||
@@ -312,7 +312,9 @@ static bool DecodeShaNiInsn(const uint8_t* rip, ShaNiInsn& insn) {
|
|||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
|
|
||||||
static bool ShaNiModrmIsRegister(uint8_t modrm) { return (modrm & 0xc0u) == 0xc0u; }
|
static bool ShaNiModrmIsRegister(uint8_t modrm) {
|
||||||
|
return (modrm & 0xc0u) == 0xc0u;
|
||||||
|
}
|
||||||
|
|
||||||
static uint8_t ShaNiRegIndex(uint8_t modrm, uint8_t rex, bool reg_field) {
|
static uint8_t ShaNiRegIndex(uint8_t modrm, uint8_t rex, bool reg_field) {
|
||||||
if (reg_field) {
|
if (reg_field) {
|
||||||
|
|||||||
@@ -1,5 +1,4 @@
|
|||||||
#include "kernel/eventQueue.h"
|
#include "kernel/eventQueue.h"
|
||||||
|
|
||||||
#include "libs/errno.h"
|
#include "libs/errno.h"
|
||||||
|
|
||||||
#include <algorithm>
|
#include <algorithm>
|
||||||
@@ -25,13 +24,10 @@ void Check(bool value, const char *text) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
void CheckConcurrentResult(int result, const char* text) {
|
void CheckConcurrentResult(int result, const char* text) {
|
||||||
Check(result == OK || result == KERNEL_ERROR_EBADF ||
|
Check(result == OK || result == KERNEL_ERROR_EBADF || result == KERNEL_ERROR_ENOENT, text);
|
||||||
result == KERNEL_ERROR_ENOENT,
|
|
||||||
text);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
void CountDeletedEvent(EventQueue::KernelEqueue,
|
void CountDeletedEvent(EventQueue::KernelEqueue, EventQueue::KernelEqueueEvent* event) {
|
||||||
EventQueue::KernelEqueueEvent *event) {
|
|
||||||
auto* count = static_cast<std::atomic_uint32_t*>(event->filter.data);
|
auto* count = static_cast<std::atomic_uint32_t*>(event->filter.data);
|
||||||
count->fetch_add(1, std::memory_order_relaxed);
|
count->fetch_add(1, std::memory_order_relaxed);
|
||||||
}
|
}
|
||||||
@@ -40,8 +36,7 @@ struct DuplicateEventOwner {
|
|||||||
std::atomic_uint32_t delete_count {0};
|
std::atomic_uint32_t delete_count {0};
|
||||||
};
|
};
|
||||||
|
|
||||||
void QueueDuplicateEvent(EventQueue::KernelEqueueEvent *event,
|
void QueueDuplicateEvent(EventQueue::KernelEqueueEvent* event, void* trigger_data) {
|
||||||
void *trigger_data) {
|
|
||||||
auto next = event->event;
|
auto next = event->event;
|
||||||
next.data = reinterpret_cast<intptr_t>(trigger_data);
|
next.data = reinterpret_cast<intptr_t>(trigger_data);
|
||||||
if (event->triggered) {
|
if (event->triggered) {
|
||||||
@@ -57,8 +52,7 @@ void ResetDuplicateEvent(EventQueue::KernelEqueueEvent *event) {
|
|||||||
event->event.data = 0;
|
event->event.data = 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
void DeleteDuplicateEvent(EventQueue::KernelEqueue,
|
void DeleteDuplicateEvent(EventQueue::KernelEqueue, EventQueue::KernelEqueueEvent* event) {
|
||||||
EventQueue::KernelEqueueEvent *event) {
|
|
||||||
auto* owner = static_cast<DuplicateEventOwner*>(event->filter.data);
|
auto* owner = static_cast<DuplicateEventOwner*>(event->filter.data);
|
||||||
owner->delete_count.fetch_add(1, std::memory_order_relaxed);
|
owner->delete_count.fetch_add(1, std::memory_order_relaxed);
|
||||||
}
|
}
|
||||||
@@ -83,14 +77,11 @@ void TestDuplicateAddPreservesEventState() {
|
|||||||
original.filter.trigger_func = QueueDuplicateEvent;
|
original.filter.trigger_func = QueueDuplicateEvent;
|
||||||
original.filter.reset_func = ResetDuplicateEvent;
|
original.filter.reset_func = ResetDuplicateEvent;
|
||||||
original.filter.delete_event_func = DeleteDuplicateEvent;
|
original.filter.delete_event_func = DeleteDuplicateEvent;
|
||||||
Check(EventQueue::KernelAddEvent(queue, original) == OK,
|
Check(EventQueue::KernelAddEvent(queue, original) == OK, "add original duplicate event");
|
||||||
"add original duplicate event");
|
Check(EventQueue::KernelTriggerEvent(queue, 17, EventQueue::KERNEL_EVFILT_VIDEO_OUT,
|
||||||
Check(EventQueue::KernelTriggerEvent(queue, 17,
|
|
||||||
EventQueue::KERNEL_EVFILT_VIDEO_OUT,
|
|
||||||
reinterpret_cast<void*>(0x1234)) == OK,
|
reinterpret_cast<void*>(0x1234)) == OK,
|
||||||
"queue first trigger");
|
"queue first trigger");
|
||||||
Check(EventQueue::KernelTriggerEvent(queue, 17,
|
Check(EventQueue::KernelTriggerEvent(queue, 17, EventQueue::KERNEL_EVFILT_VIDEO_OUT,
|
||||||
EventQueue::KERNEL_EVFILT_VIDEO_OUT,
|
|
||||||
reinterpret_cast<void*>(0x5678)) == OK,
|
reinterpret_cast<void*>(0x5678)) == OK,
|
||||||
"queue pending trigger");
|
"queue pending trigger");
|
||||||
|
|
||||||
@@ -106,8 +97,7 @@ void TestDuplicateAddPreservesEventState() {
|
|||||||
duplicate.filter.data = replacement_owner.get();
|
duplicate.filter.data = replacement_owner.get();
|
||||||
duplicate.filter.owner = replacement_owner;
|
duplicate.filter.owner = replacement_owner;
|
||||||
duplicate.filter.trigger_func = PoisonDuplicateEvent;
|
duplicate.filter.trigger_func = PoisonDuplicateEvent;
|
||||||
Check(EventQueue::KernelAddEvent(queue, duplicate) == OK,
|
Check(EventQueue::KernelAddEvent(queue, duplicate) == OK, "update duplicate event");
|
||||||
"update duplicate event");
|
|
||||||
|
|
||||||
duplicate.filter.owner.reset();
|
duplicate.filter.owner.reset();
|
||||||
replacement_owner.reset();
|
replacement_owner.reset();
|
||||||
@@ -130,26 +120,20 @@ void TestDuplicateAddPreservesEventState() {
|
|||||||
"duplicate add updates pending user data");
|
"duplicate add updates pending user data");
|
||||||
|
|
||||||
EventQueue::KernelEvent timer_event {};
|
EventQueue::KernelEvent timer_event {};
|
||||||
Check(EventQueue::KernelWaitEqueue(queue, &timer_event, 1, &out, &timeout) ==
|
Check(EventQueue::KernelWaitEqueue(queue, &timer_event, 1, &out, &timeout) == OK && out == 1,
|
||||||
OK &&
|
|
||||||
out == 1,
|
|
||||||
"duplicate add updates deadline metadata");
|
"duplicate add updates deadline metadata");
|
||||||
Check(timer_event.data == 0 &&
|
Check(timer_event.data == 0 && timer_event.udata == reinterpret_cast<void*>(0x2222),
|
||||||
timer_event.udata == reinterpret_cast<void *>(0x2222),
|
|
||||||
"deadline trigger retains updated duplicate metadata");
|
"deadline trigger retains updated duplicate metadata");
|
||||||
|
|
||||||
auto retained_owner = weak_original.lock();
|
auto retained_owner = weak_original.lock();
|
||||||
Check(retained_owner != nullptr, "original owner alive before delete");
|
Check(retained_owner != nullptr, "original owner alive before delete");
|
||||||
Check(EventQueue::KernelDeleteEvent(queue, 17,
|
Check(EventQueue::KernelDeleteEvent(queue, 17, EventQueue::KERNEL_EVFILT_VIDEO_OUT) == OK,
|
||||||
EventQueue::KERNEL_EVFILT_VIDEO_OUT) ==
|
|
||||||
OK,
|
|
||||||
"delete duplicate event");
|
"delete duplicate event");
|
||||||
Check(retained_owner->delete_count.load(std::memory_order_relaxed) == 1,
|
Check(retained_owner->delete_count.load(std::memory_order_relaxed) == 1,
|
||||||
"duplicate add preserves delete callback");
|
"duplicate add preserves delete callback");
|
||||||
retained_owner.reset();
|
retained_owner.reset();
|
||||||
Check(weak_original.expired(), "original owner released on delete");
|
Check(weak_original.expired(), "original owner released on delete");
|
||||||
Check(EventQueue::KernelDeleteEqueue(queue) == OK,
|
Check(EventQueue::KernelDeleteEqueue(queue) == OK, "delete duplicate add queue");
|
||||||
"delete duplicate add queue");
|
|
||||||
}
|
}
|
||||||
|
|
||||||
struct SimulatedVideoOutEventState;
|
struct SimulatedVideoOutEventState;
|
||||||
@@ -161,13 +145,10 @@ struct SimulatedVideoOutRegistration {
|
|||||||
};
|
};
|
||||||
|
|
||||||
struct SimulatedVideoOutEventState {
|
struct SimulatedVideoOutEventState {
|
||||||
SimulatedVideoOutEventState(std::atomic_uint32_t &stage,
|
SimulatedVideoOutEventState(std::atomic_uint32_t& stage, std::atomic_uint32_t& destroy_count)
|
||||||
std::atomic_uint32_t &destroy_count)
|
|
||||||
: stage(stage), destroy_count(destroy_count) {}
|
: stage(stage), destroy_count(destroy_count) {}
|
||||||
|
|
||||||
~SimulatedVideoOutEventState() {
|
~SimulatedVideoOutEventState() { destroy_count.fetch_add(1, std::memory_order_relaxed); }
|
||||||
destroy_count.fetch_add(1, std::memory_order_relaxed);
|
|
||||||
}
|
|
||||||
|
|
||||||
std::mutex mutex;
|
std::mutex mutex;
|
||||||
std::vector<std::shared_ptr<SimulatedVideoOutRegistration>> queues;
|
std::vector<std::shared_ptr<SimulatedVideoOutRegistration>> queues;
|
||||||
@@ -178,8 +159,7 @@ struct SimulatedVideoOutEventState {
|
|||||||
|
|
||||||
void DetachSimulatedVideoOutEvent(EventQueue::KernelEqueue queue,
|
void DetachSimulatedVideoOutEvent(EventQueue::KernelEqueue queue,
|
||||||
EventQueue::KernelEqueueEvent* event) {
|
EventQueue::KernelEqueueEvent* event) {
|
||||||
auto *registration =
|
auto* registration = static_cast<SimulatedVideoOutRegistration*>(event->filter.data);
|
||||||
static_cast<SimulatedVideoOutRegistration *>(event->filter.data);
|
|
||||||
Check(registration != nullptr && registration->handle == queue,
|
Check(registration != nullptr && registration->handle == queue,
|
||||||
"simulated registration identity");
|
"simulated registration identity");
|
||||||
auto state = registration->state;
|
auto state = registration->state;
|
||||||
@@ -191,18 +171,15 @@ void DetachSimulatedVideoOutEvent(EventQueue::KernelEqueue queue,
|
|||||||
|
|
||||||
{
|
{
|
||||||
std::lock_guard lock(state->mutex);
|
std::lock_guard lock(state->mutex);
|
||||||
const auto entry =
|
const auto entry = std::find_if(
|
||||||
std::find_if(state->queues.begin(), state->queues.end(),
|
state->queues.begin(), state->queues.end(),
|
||||||
[registration](const auto &candidate) {
|
[registration](const auto& candidate) { return candidate.get() == registration; });
|
||||||
return candidate.get() == registration;
|
|
||||||
});
|
|
||||||
if (entry != state->queues.end()) {
|
if (entry != state->queues.end()) {
|
||||||
state->queues.erase(entry);
|
state->queues.erase(entry);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
event->filter.owner.reset();
|
event->filter.owner.reset();
|
||||||
Check(state->marker == 0xfedcba9876543210ull &&
|
Check(state->marker == 0xfedcba9876543210ull && registration->marker == 0x123456789abcdef0ull,
|
||||||
registration->marker == 0x123456789abcdef0ull,
|
|
||||||
"callback state survives simulated port destruction");
|
"callback state survives simulated port destruction");
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -213,12 +190,10 @@ void TestCallbackStateOutlivesPort() {
|
|||||||
|
|
||||||
std::atomic_uint32_t stage {0};
|
std::atomic_uint32_t stage {0};
|
||||||
std::atomic_uint32_t destroy_count {0};
|
std::atomic_uint32_t destroy_count {0};
|
||||||
auto port_state =
|
auto port_state = std::make_shared<SimulatedVideoOutEventState>(stage, destroy_count);
|
||||||
std::make_shared<SimulatedVideoOutEventState>(stage, destroy_count);
|
|
||||||
std::weak_ptr<SimulatedVideoOutEventState> weak_state = port_state;
|
std::weak_ptr<SimulatedVideoOutEventState> weak_state = port_state;
|
||||||
auto registration = std::make_shared<SimulatedVideoOutRegistration>();
|
auto registration = std::make_shared<SimulatedVideoOutRegistration>();
|
||||||
std::weak_ptr<SimulatedVideoOutRegistration> weak_registration =
|
std::weak_ptr<SimulatedVideoOutRegistration> weak_registration = registration;
|
||||||
registration;
|
|
||||||
registration->handle = queue;
|
registration->handle = queue;
|
||||||
registration->state = port_state;
|
registration->state = port_state;
|
||||||
port_state->queues.push_back(registration);
|
port_state->queues.push_back(registration);
|
||||||
@@ -229,13 +204,11 @@ void TestCallbackStateOutlivesPort() {
|
|||||||
event.filter.data = registration.get();
|
event.filter.data = registration.get();
|
||||||
event.filter.owner = registration;
|
event.filter.owner = registration;
|
||||||
event.filter.delete_event_func = DetachSimulatedVideoOutEvent;
|
event.filter.delete_event_func = DetachSimulatedVideoOutEvent;
|
||||||
Check(EventQueue::KernelAddEvent(queue, event) == OK,
|
Check(EventQueue::KernelAddEvent(queue, event) == OK, "add shared port state event");
|
||||||
"add shared port state event");
|
|
||||||
event.filter.owner.reset();
|
event.filter.owner.reset();
|
||||||
|
|
||||||
std::jthread close([&] {
|
std::jthread close([&] {
|
||||||
Check(EventQueue::KernelDeleteEqueue(queue) == OK,
|
Check(EventQueue::KernelDeleteEqueue(queue) == OK, "delete shared port state queue");
|
||||||
"delete shared port state queue");
|
|
||||||
});
|
});
|
||||||
while (stage.load(std::memory_order_acquire) != 1) {
|
while (stage.load(std::memory_order_acquire) != 1) {
|
||||||
std::this_thread::yield();
|
std::this_thread::yield();
|
||||||
@@ -249,10 +222,8 @@ void TestCallbackStateOutlivesPort() {
|
|||||||
registration.reset();
|
registration.reset();
|
||||||
detached.clear();
|
detached.clear();
|
||||||
port_state.reset();
|
port_state.reset();
|
||||||
Check(!weak_state.expired(),
|
Check(!weak_state.expired(), "callback state outlives simulated port object");
|
||||||
"callback state outlives simulated port object");
|
Check(!weak_registration.expired(), "registration outlives simulated port object");
|
||||||
Check(!weak_registration.expired(),
|
|
||||||
"registration outlives simulated port object");
|
|
||||||
|
|
||||||
stage.store(2, std::memory_order_release);
|
stage.store(2, std::memory_order_release);
|
||||||
close.join();
|
close.join();
|
||||||
@@ -263,8 +234,7 @@ void TestCallbackStateOutlivesPort() {
|
|||||||
}
|
}
|
||||||
|
|
||||||
struct OwnedCallbackPayload {
|
struct OwnedCallbackPayload {
|
||||||
OwnedCallbackPayload(std::atomic_uint32_t &stage,
|
OwnedCallbackPayload(std::atomic_uint32_t& stage, std::atomic_uint32_t& delete_count,
|
||||||
std::atomic_uint32_t &delete_count,
|
|
||||||
std::atomic_uint32_t& destroy_count)
|
std::atomic_uint32_t& destroy_count)
|
||||||
: stage(stage), delete_count(delete_count), destroy_count(destroy_count) {}
|
: stage(stage), delete_count(delete_count), destroy_count(destroy_count) {}
|
||||||
|
|
||||||
@@ -273,25 +243,20 @@ struct OwnedCallbackPayload {
|
|||||||
std::atomic_uint32_t& destroy_count;
|
std::atomic_uint32_t& destroy_count;
|
||||||
uint64_t marker = 0xc0dec0dec0dec0deull;
|
uint64_t marker = 0xc0dec0dec0dec0deull;
|
||||||
|
|
||||||
~OwnedCallbackPayload() {
|
~OwnedCallbackPayload() { destroy_count.fetch_add(1, std::memory_order_relaxed); }
|
||||||
destroy_count.fetch_add(1, std::memory_order_relaxed);
|
|
||||||
}
|
|
||||||
};
|
};
|
||||||
|
|
||||||
void DeleteOwnedEvent(EventQueue::KernelEqueue queue,
|
void DeleteOwnedEvent(EventQueue::KernelEqueue queue, EventQueue::KernelEqueueEvent* event) {
|
||||||
EventQueue::KernelEqueueEvent *event) {
|
|
||||||
auto* payload = static_cast<OwnedCallbackPayload*>(event->filter.data);
|
auto* payload = static_cast<OwnedCallbackPayload*>(event->filter.data);
|
||||||
Check(payload != nullptr, "owned callback payload");
|
Check(payload != nullptr, "owned callback payload");
|
||||||
Check(!EventQueue::KernelPinEqueue(queue),
|
Check(!EventQueue::KernelPinEqueue(queue), "owned callback runs after registry removal");
|
||||||
"owned callback runs after registry removal");
|
|
||||||
payload->delete_count.fetch_add(1, std::memory_order_relaxed);
|
payload->delete_count.fetch_add(1, std::memory_order_relaxed);
|
||||||
event->filter.owner.reset();
|
event->filter.owner.reset();
|
||||||
payload->stage.store(1, std::memory_order_release);
|
payload->stage.store(1, std::memory_order_release);
|
||||||
while (payload->stage.load(std::memory_order_acquire) != 2) {
|
while (payload->stage.load(std::memory_order_acquire) != 2) {
|
||||||
std::this_thread::yield();
|
std::this_thread::yield();
|
||||||
}
|
}
|
||||||
Check(payload->marker == 0xc0dec0dec0dec0deull,
|
Check(payload->marker == 0xc0dec0dec0dec0deull, "owned callback payload remains valid");
|
||||||
"owned callback payload remains valid");
|
|
||||||
}
|
}
|
||||||
|
|
||||||
void TestCallbackOwnsPayload() {
|
void TestCallbackOwnsPayload() {
|
||||||
@@ -302,11 +267,9 @@ void TestCallbackOwnsPayload() {
|
|||||||
std::atomic_uint32_t stage {0};
|
std::atomic_uint32_t stage {0};
|
||||||
std::atomic_uint32_t delete_count {0};
|
std::atomic_uint32_t delete_count {0};
|
||||||
std::atomic_uint32_t destroy_count {0};
|
std::atomic_uint32_t destroy_count {0};
|
||||||
auto registration =
|
auto registration = std::make_shared<OwnedCallbackPayload>(stage, delete_count, destroy_count);
|
||||||
std::make_shared<OwnedCallbackPayload>(stage, delete_count, destroy_count);
|
|
||||||
std::weak_ptr<OwnedCallbackPayload> weak_registration = registration;
|
std::weak_ptr<OwnedCallbackPayload> weak_registration = registration;
|
||||||
std::vector<std::shared_ptr<OwnedCallbackPayload>> port_registrations{
|
std::vector<std::shared_ptr<OwnedCallbackPayload>> port_registrations {registration};
|
||||||
registration};
|
|
||||||
{
|
{
|
||||||
EventQueue::KernelEqueueEvent event {};
|
EventQueue::KernelEqueueEvent event {};
|
||||||
event.event.ident = 2;
|
event.event.ident = 2;
|
||||||
@@ -314,14 +277,11 @@ void TestCallbackOwnsPayload() {
|
|||||||
event.filter.data = registration.get();
|
event.filter.data = registration.get();
|
||||||
event.filter.owner = registration;
|
event.filter.owner = registration;
|
||||||
event.filter.delete_event_func = DeleteOwnedEvent;
|
event.filter.delete_event_func = DeleteOwnedEvent;
|
||||||
Check(EventQueue::KernelAddEvent(queue, event) == OK,
|
Check(EventQueue::KernelAddEvent(queue, event) == OK, "add owned callback event");
|
||||||
"add owned callback event");
|
|
||||||
}
|
}
|
||||||
|
|
||||||
std::jthread close([&] {
|
std::jthread close(
|
||||||
Check(EventQueue::KernelDeleteEqueue(queue) == OK,
|
[&] { Check(EventQueue::KernelDeleteEqueue(queue) == OK, "delete owned callback queue"); });
|
||||||
"delete owned callback queue");
|
|
||||||
});
|
|
||||||
while (stage.load(std::memory_order_acquire) != 1) {
|
while (stage.load(std::memory_order_acquire) != 1) {
|
||||||
std::this_thread::yield();
|
std::this_thread::yield();
|
||||||
}
|
}
|
||||||
@@ -330,23 +290,19 @@ void TestCallbackOwnsPayload() {
|
|||||||
"owned callback queue removed while callback blocked");
|
"owned callback queue removed while callback blocked");
|
||||||
port_registrations.clear();
|
port_registrations.clear();
|
||||||
registration.reset();
|
registration.reset();
|
||||||
Check(!weak_registration.expired(),
|
Check(!weak_registration.expired(), "delete callback retains detached payload");
|
||||||
"delete callback retains detached payload");
|
|
||||||
|
|
||||||
stage.store(2, std::memory_order_release);
|
stage.store(2, std::memory_order_release);
|
||||||
close.join();
|
close.join();
|
||||||
Check(delete_count.load(std::memory_order_relaxed) == 1,
|
Check(delete_count.load(std::memory_order_relaxed) == 1, "owned callback runs exactly once");
|
||||||
"owned callback runs exactly once");
|
Check(weak_registration.expired(), "owned callback payload released with event");
|
||||||
Check(weak_registration.expired(),
|
|
||||||
"owned callback payload released with event");
|
|
||||||
Check(destroy_count.load(std::memory_order_relaxed) == 1,
|
Check(destroy_count.load(std::memory_order_relaxed) == 1,
|
||||||
"owned callback payload destroyed exactly once");
|
"owned callback payload destroyed exactly once");
|
||||||
}
|
}
|
||||||
|
|
||||||
void TestPinnedClose() {
|
void TestPinnedClose() {
|
||||||
EventQueue::KernelEqueue queue = EventQueue::KERNEL_EQUEUE_INVALID;
|
EventQueue::KernelEqueue queue = EventQueue::KERNEL_EQUEUE_INVALID;
|
||||||
Check(EventQueue::KernelCreateEqueue(&queue, "pinned-close") == OK,
|
Check(EventQueue::KernelCreateEqueue(&queue, "pinned-close") == OK, "create pinned queue");
|
||||||
"create pinned queue");
|
|
||||||
|
|
||||||
std::atomic_uint32_t delete_count {0};
|
std::atomic_uint32_t delete_count {0};
|
||||||
EventQueue::KernelEqueueEvent event {};
|
EventQueue::KernelEqueueEvent event {};
|
||||||
@@ -359,12 +315,10 @@ void TestPinnedClose() {
|
|||||||
auto owner = EventQueue::KernelPinEqueue(queue);
|
auto owner = EventQueue::KernelPinEqueue(queue);
|
||||||
Check(owner != nullptr, "pin live queue");
|
Check(owner != nullptr, "pin live queue");
|
||||||
Check(EventQueue::KernelDeleteEqueue(queue) == OK, "delete pinned queue");
|
Check(EventQueue::KernelDeleteEqueue(queue) == OK, "delete pinned queue");
|
||||||
Check(delete_count.load(std::memory_order_relaxed) == 1,
|
Check(delete_count.load(std::memory_order_relaxed) == 1, "close invokes callback once");
|
||||||
"close invokes callback once");
|
|
||||||
Check(!EventQueue::KernelPinEqueue(queue), "deleted queue leaves registry");
|
Check(!EventQueue::KernelPinEqueue(queue), "deleted queue leaves registry");
|
||||||
Check(EventQueue::KernelTriggerEvent(queue, 1,
|
Check(EventQueue::KernelTriggerEvent(queue, 1, EventQueue::KERNEL_EVFILT_VIDEO_OUT, nullptr) ==
|
||||||
EventQueue::KERNEL_EVFILT_VIDEO_OUT,
|
KERNEL_ERROR_EBADF,
|
||||||
nullptr) == KERNEL_ERROR_EBADF,
|
|
||||||
"stale trigger rejected");
|
"stale trigger rejected");
|
||||||
Check(EventQueue::KernelDeleteEqueue(queue) == KERNEL_ERROR_EBADF,
|
Check(EventQueue::KernelDeleteEqueue(queue) == KERNEL_ERROR_EBADF,
|
||||||
"second queue delete rejected");
|
"second queue delete rejected");
|
||||||
@@ -376,8 +330,7 @@ void TestPinnedClose() {
|
|||||||
|
|
||||||
void TestStaleHandleNeverAliasesNewQueue() {
|
void TestStaleHandleNeverAliasesNewQueue() {
|
||||||
EventQueue::KernelEqueue stale = EventQueue::KERNEL_EQUEUE_INVALID;
|
EventQueue::KernelEqueue stale = EventQueue::KERNEL_EQUEUE_INVALID;
|
||||||
Check(EventQueue::KernelCreateEqueue(&stale, "stale-handle") == OK,
|
Check(EventQueue::KernelCreateEqueue(&stale, "stale-handle") == OK, "create stale queue");
|
||||||
"create stale queue");
|
|
||||||
Check(EventQueue::KernelDeleteEqueue(stale) == OK, "delete stale queue");
|
Check(EventQueue::KernelDeleteEqueue(stale) == OK, "delete stale queue");
|
||||||
|
|
||||||
EventQueue::KernelEqueue replacement = EventQueue::KERNEL_EQUEUE_INVALID;
|
EventQueue::KernelEqueue replacement = EventQueue::KERNEL_EQUEUE_INVALID;
|
||||||
@@ -389,13 +342,11 @@ void TestStaleHandleNeverAliasesNewQueue() {
|
|||||||
"stale handle cannot mutate replacement");
|
"stale handle cannot mutate replacement");
|
||||||
Check(EventQueue::KernelAddUserEvent(replacement, 11) == OK,
|
Check(EventQueue::KernelAddUserEvent(replacement, 11) == OK,
|
||||||
"replacement handle remains valid");
|
"replacement handle remains valid");
|
||||||
Check(EventQueue::KernelTriggerUserEvent(stale, 11, nullptr) ==
|
Check(EventQueue::KernelTriggerUserEvent(stale, 11, nullptr) == KERNEL_ERROR_EBADF,
|
||||||
KERNEL_ERROR_EBADF,
|
|
||||||
"stale handle cannot trigger replacement");
|
"stale handle cannot trigger replacement");
|
||||||
Check(EventQueue::KernelTriggerUserEvent(replacement, 11, nullptr) == OK,
|
Check(EventQueue::KernelTriggerUserEvent(replacement, 11, nullptr) == OK,
|
||||||
"replacement event triggers");
|
"replacement event triggers");
|
||||||
Check(EventQueue::KernelDeleteEqueue(replacement) == OK,
|
Check(EventQueue::KernelDeleteEqueue(replacement) == OK, "delete replacement queue");
|
||||||
"delete replacement queue");
|
|
||||||
}
|
}
|
||||||
|
|
||||||
void TestConcurrentCloseCallback() {
|
void TestConcurrentCloseCallback() {
|
||||||
@@ -410,8 +361,7 @@ void TestConcurrentCloseCallback() {
|
|||||||
callback_event.event.filter = EventQueue::KERNEL_EVFILT_GRAPHICS;
|
callback_event.event.filter = EventQueue::KERNEL_EVFILT_GRAPHICS;
|
||||||
callback_event.filter.data = &delete_count;
|
callback_event.filter.data = &delete_count;
|
||||||
callback_event.filter.delete_event_func = CountDeletedEvent;
|
callback_event.filter.delete_event_func = CountDeletedEvent;
|
||||||
Check(EventQueue::KernelAddEvent(queue, callback_event) == OK,
|
Check(EventQueue::KernelAddEvent(queue, callback_event) == OK, "add callback race event");
|
||||||
"add callback race event");
|
|
||||||
|
|
||||||
std::atomic_bool start {false};
|
std::atomic_bool start {false};
|
||||||
std::jthread trigger([&] {
|
std::jthread trigger([&] {
|
||||||
@@ -419,16 +369,14 @@ void TestConcurrentCloseCallback() {
|
|||||||
std::this_thread::yield();
|
std::this_thread::yield();
|
||||||
}
|
}
|
||||||
for (uint32_t i = 0; i < 256; i++) {
|
for (uint32_t i = 0; i < 256; i++) {
|
||||||
CheckConcurrentResult(
|
CheckConcurrentResult(EventQueue::KernelTriggerEvent(
|
||||||
EventQueue::KernelTriggerEvent(
|
|
||||||
queue, 9, EventQueue::KERNEL_EVFILT_GRAPHICS, nullptr),
|
queue, 9, EventQueue::KERNEL_EVFILT_GRAPHICS, nullptr),
|
||||||
"callback race trigger result");
|
"callback race trigger result");
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
|
|
||||||
start.store(true, std::memory_order_release);
|
start.store(true, std::memory_order_release);
|
||||||
Check(EventQueue::KernelDeleteEqueue(queue) == OK,
|
Check(EventQueue::KernelDeleteEqueue(queue) == OK, "callback race queue delete");
|
||||||
"callback race queue delete");
|
|
||||||
trigger.join();
|
trigger.join();
|
||||||
Check(delete_count.load(std::memory_order_relaxed) == 1,
|
Check(delete_count.load(std::memory_order_relaxed) == 1,
|
||||||
"concurrent close invokes callback exactly once");
|
"concurrent close invokes callback exactly once");
|
||||||
@@ -443,8 +391,7 @@ void TestConcurrentDelete() {
|
|||||||
EventQueue::KernelEqueueEvent event {};
|
EventQueue::KernelEqueueEvent event {};
|
||||||
event.event.ident = 7;
|
event.event.ident = 7;
|
||||||
event.event.filter = EventQueue::KERNEL_EVFILT_USER;
|
event.event.filter = EventQueue::KERNEL_EVFILT_USER;
|
||||||
Check(EventQueue::KernelAddEvent(queue, event) == OK,
|
Check(EventQueue::KernelAddEvent(queue, event) == OK, "add concurrent event");
|
||||||
"add concurrent event");
|
|
||||||
|
|
||||||
std::atomic_bool start {false};
|
std::atomic_bool start {false};
|
||||||
std::jthread mutate([&] {
|
std::jthread mutate([&] {
|
||||||
@@ -454,12 +401,11 @@ void TestConcurrentDelete() {
|
|||||||
for (uint32_t i = 0; i < 64; i++) {
|
for (uint32_t i = 0; i < 64; i++) {
|
||||||
CheckConcurrentResult(EventQueue::KernelAddEvent(queue, event),
|
CheckConcurrentResult(EventQueue::KernelAddEvent(queue, event),
|
||||||
"concurrent add result");
|
"concurrent add result");
|
||||||
CheckConcurrentResult(
|
CheckConcurrentResult(EventQueue::KernelTriggerEvent(
|
||||||
EventQueue::KernelTriggerEvent(
|
|
||||||
queue, 7, EventQueue::KERNEL_EVFILT_USER, nullptr),
|
queue, 7, EventQueue::KERNEL_EVFILT_USER, nullptr),
|
||||||
"concurrent trigger result");
|
"concurrent trigger result");
|
||||||
CheckConcurrentResult(EventQueue::KernelDeleteEvent(
|
CheckConcurrentResult(
|
||||||
queue, 7, EventQueue::KERNEL_EVFILT_USER),
|
EventQueue::KernelDeleteEvent(queue, 7, EventQueue::KERNEL_EVFILT_USER),
|
||||||
"concurrent event delete result");
|
"concurrent event delete result");
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
@@ -468,20 +414,17 @@ void TestConcurrentDelete() {
|
|||||||
std::this_thread::yield();
|
std::this_thread::yield();
|
||||||
}
|
}
|
||||||
for (uint32_t i = 0; i < 128; i++) {
|
for (uint32_t i = 0; i < 128; i++) {
|
||||||
CheckConcurrentResult(
|
CheckConcurrentResult(EventQueue::KernelTriggerEvent(
|
||||||
EventQueue::KernelTriggerEvent(
|
|
||||||
queue, 7, EventQueue::KERNEL_EVFILT_USER, nullptr),
|
queue, 7, EventQueue::KERNEL_EVFILT_USER, nullptr),
|
||||||
"parallel trigger result");
|
"parallel trigger result");
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
|
|
||||||
start.store(true, std::memory_order_release);
|
start.store(true, std::memory_order_release);
|
||||||
Check(EventQueue::KernelDeleteEqueue(queue) == OK,
|
Check(EventQueue::KernelDeleteEqueue(queue) == OK, "concurrent queue delete");
|
||||||
"concurrent queue delete");
|
|
||||||
mutate.join();
|
mutate.join();
|
||||||
trigger.join();
|
trigger.join();
|
||||||
Check(!EventQueue::KernelPinEqueue(queue),
|
Check(!EventQueue::KernelPinEqueue(queue), "concurrent queue removed from registry");
|
||||||
"concurrent queue removed from registry");
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -24,7 +24,8 @@ void TestMultiOwnerAndExactErase() {
|
|||||||
owners.push_back(11);
|
owners.push_back(11);
|
||||||
owners.push_back(22);
|
owners.push_back(22);
|
||||||
|
|
||||||
Check(table.Find(17) != nullptr && table.Find(17)->size() == 2, "both page owners are retained");
|
Check(table.Find(17) != nullptr && table.Find(17)->size() == 2,
|
||||||
|
"both page owners are retained");
|
||||||
Check(Libs::Graphics::EraseExact(owners, 11U), "registered owner is erased");
|
Check(Libs::Graphics::EraseExact(owners, 11U), "registered owner is erased");
|
||||||
Check(owners.size() == 1 && owners.front() == 22, "erasing one owner preserves its neighbor");
|
Check(owners.size() == 1 && owners.front() == 22, "erasing one owner preserves its neighbor");
|
||||||
Check(!Libs::Graphics::EraseExact(owners, 33U), "missing owner is reported without mutation");
|
Check(!Libs::Graphics::EraseExact(owners, 33U), "missing owner is reported without mutation");
|
||||||
@@ -34,13 +35,15 @@ void TestCrossBucketRange() {
|
|||||||
Table::PageRange range {};
|
Table::PageRange range {};
|
||||||
constexpr uint64_t bucket_boundary = uint64_t {Table::kBucketEntries} << Table::kPageBits;
|
constexpr uint64_t bucket_boundary = uint64_t {Table::kBucketEntries} << Table::kPageBits;
|
||||||
Check(Table::TryGetPageRange(bucket_boundary - 1, 2, range), "cross-bucket range is valid");
|
Check(Table::TryGetPageRange(bucket_boundary - 1, 2, range), "cross-bucket range is valid");
|
||||||
Check(range.first == Table::kBucketEntries - 1 && range.last_exclusive == Table::kBucketEntries + 1,
|
Check(range.first == Table::kBucketEntries - 1 &&
|
||||||
|
range.last_exclusive == Table::kBucketEntries + 1,
|
||||||
"cross-bucket range covers both pages");
|
"cross-bucket range covers both pages");
|
||||||
|
|
||||||
Table table;
|
Table table;
|
||||||
table[range.first].push_back(1);
|
table[range.first].push_back(1);
|
||||||
table[range.last_exclusive - 1].push_back(2);
|
table[range.last_exclusive - 1].push_back(2);
|
||||||
Check(table.AllocatedBucketCount() == 2, "pages across the L1 boundary use distinct sparse buckets");
|
Check(table.AllocatedBucketCount() == 2,
|
||||||
|
"pages across the L1 boundary use distinct sparse buckets");
|
||||||
}
|
}
|
||||||
|
|
||||||
void TestQueriesDoNotAllocate() {
|
void TestQueriesDoNotAllocate() {
|
||||||
@@ -56,26 +59,33 @@ void TestQueriesDoNotAllocate() {
|
|||||||
|
|
||||||
void TestAddressSpaceBoundaries() {
|
void TestAddressSpaceBoundaries() {
|
||||||
Table::PageRange range {};
|
Table::PageRange range {};
|
||||||
Check(Table::TryGetPageRange(Table::kAddressSpaceSize - 1, 1, range), "last guest byte is valid");
|
Check(Table::TryGetPageRange(Table::kAddressSpaceSize - 1, 1, range),
|
||||||
|
"last guest byte is valid");
|
||||||
Check(range.first == Table::kPageCount - 1 && range.last_exclusive == Table::kPageCount,
|
Check(range.first == Table::kPageCount - 1 && range.last_exclusive == Table::kPageCount,
|
||||||
"last guest byte maps to the final page");
|
"last guest byte maps to the final page");
|
||||||
Check(!Table::TryGetPageRange(0, 0, range), "empty ranges are rejected");
|
Check(!Table::TryGetPageRange(0, 0, range), "empty ranges are rejected");
|
||||||
Check(!Table::TryGetPageRange(Table::kAddressSpaceSize, 1, range), "first out-of-range byte is rejected");
|
Check(!Table::TryGetPageRange(Table::kAddressSpaceSize, 1, range),
|
||||||
Check(!Table::TryGetPageRange(Table::kAddressSpaceSize - 1, 2, range), "crossing the address-space end is rejected");
|
"first out-of-range byte is rejected");
|
||||||
|
Check(!Table::TryGetPageRange(Table::kAddressSpaceSize - 1, 2, range),
|
||||||
|
"crossing the address-space end is rejected");
|
||||||
Check(!Table::TryGetPageRange(UINT64_MAX - 1, 4, range), "wrapping input is rejected");
|
Check(!Table::TryGetPageRange(UINT64_MAX - 1, 4, range), "wrapping input is rejected");
|
||||||
|
|
||||||
Table table;
|
Table table;
|
||||||
table.GetOrCreate(Table::kPageCount - 1).push_back(99);
|
table.GetOrCreate(Table::kPageCount - 1).push_back(99);
|
||||||
Check(table.Find(Table::kPageCount - 1) != nullptr && table.Find(Table::kPageCount - 1)->front() == 99,
|
Check(table.Find(Table::kPageCount - 1) != nullptr &&
|
||||||
|
table.Find(Table::kPageCount - 1)->front() == 99,
|
||||||
"final page supports allocating and nonallocating access");
|
"final page supports allocating and nonallocating access");
|
||||||
}
|
}
|
||||||
|
|
||||||
void TestMultiRangeRegistrationDeduplicatesPages() {
|
void TestMultiRangeRegistrationDeduplicatesPages() {
|
||||||
OwnerIndex index;
|
OwnerIndex index;
|
||||||
// Depth and stencil-like planes overlap tracking pages and share one 1 MiB bucket.
|
// Depth and stencil-like planes overlap tracking pages and share one 1 MiB bucket.
|
||||||
Check(index.Register(7, {{0x101000, 0x2800}, {0x102000, 0x3000}}), "multi-range owner registers");
|
Check(index.Register(7, {{0x101000, 0x2800}, {0x102000, 0x3000}}),
|
||||||
Check(index.CoarseMembershipCount(1) == 1, "one owner is inserted once in a shared 1 MiB bucket");
|
"multi-range owner registers");
|
||||||
Check(index.TrackingMembershipCount(0x102) == 1, "overlapping planes insert one 4 KiB membership");
|
Check(index.CoarseMembershipCount(1) == 1,
|
||||||
|
"one owner is inserted once in a shared 1 MiB bucket");
|
||||||
|
Check(index.TrackingMembershipCount(0x102) == 1,
|
||||||
|
"overlapping planes insert one 4 KiB membership");
|
||||||
Check(!index.Register(7, {{0x101000, 0x1000}}), "duplicate owner registration hard-fails");
|
Check(!index.Register(7, {{0x101000, 0x1000}}), "duplicate owner registration hard-fails");
|
||||||
|
|
||||||
const auto owners = index.Query(0x100000, 0x10000);
|
const auto owners = index.Query(0x100000, 0x10000);
|
||||||
@@ -85,7 +95,8 @@ void TestMultiRangeRegistrationDeduplicatesPages() {
|
|||||||
void TestSharedPageUnregisterLifecycle() {
|
void TestSharedPageUnregisterLifecycle() {
|
||||||
OwnerIndex index;
|
OwnerIndex index;
|
||||||
const std::vector<OwnerIndex::ByteRange> ranges {{0x202000, 0x2000}};
|
const std::vector<OwnerIndex::ByteRange> ranges {{0x202000, 0x2000}};
|
||||||
Check(index.Register(11, ranges) && index.Register(22, ranges), "two owners register on identical pages");
|
Check(index.Register(11, ranges) && index.Register(22, ranges),
|
||||||
|
"two owners register on identical pages");
|
||||||
Check(index.CoarseMembershipCount(2) == 2 && index.TrackingMembershipCount(0x202) == 2,
|
Check(index.CoarseMembershipCount(2) == 2 && index.TrackingMembershipCount(0x202) == 2,
|
||||||
"coarse and tracking pages retain both owners");
|
"coarse and tracking pages retain both owners");
|
||||||
|
|
||||||
@@ -97,7 +108,8 @@ void TestSharedPageUnregisterLifecycle() {
|
|||||||
Check(!index.Unregister(11, releases), "missing membership hard-fails without mutation");
|
Check(!index.Unregister(11, releases), "missing membership hard-fails without mutation");
|
||||||
|
|
||||||
Check(index.Unregister(22, releases), "final owner unregisters");
|
Check(index.Unregister(22, releases), "final owner unregisters");
|
||||||
Check(releases.size() == 1 && releases.front().address == 0x202000 && releases.front().size == 0x2000,
|
Check(releases.size() == 1 && releases.front().address == 0x202000 &&
|
||||||
|
releases.front().size == 0x2000,
|
||||||
"adjacent final-owner tracking pages return one contiguous release");
|
"adjacent final-owner tracking pages return one contiguous release");
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -105,14 +117,19 @@ void TestStrictByteFilteringAndPredicate() {
|
|||||||
OwnerIndex index;
|
OwnerIndex index;
|
||||||
Check(index.Register(31, {{0x300100, 0x100}}), "first byte-disjoint owner registers");
|
Check(index.Register(31, {{0x300100, 0x100}}), "first byte-disjoint owner registers");
|
||||||
Check(index.Register(32, {{0x300800, 0x100}}), "second byte-disjoint owner registers");
|
Check(index.Register(32, {{0x300800, 0x100}}), "second byte-disjoint owner registers");
|
||||||
Check(index.TrackingMembershipCount(0x300) == 2, "byte-disjoint owners share one tracking page");
|
Check(index.TrackingMembershipCount(0x300) == 2,
|
||||||
|
"byte-disjoint owners share one tracking page");
|
||||||
Check(index.Query(0x300400, 0x40).empty(), "page hit without byte overlap is filtered out");
|
Check(index.Query(0x300400, 0x40).empty(), "page hit without byte overlap is filtered out");
|
||||||
const auto page_candidates = index.QueryCandidates(0x300400, 0x40);
|
const auto page_candidates = index.QueryCandidates(0x300400, 0x40);
|
||||||
Check(page_candidates.size() == 2, "fault candidate query retains byte-disjoint owners on the touched page");
|
Check(page_candidates.size() == 2,
|
||||||
|
"fault candidate query retains byte-disjoint owners on the touched page");
|
||||||
const auto first = index.Query(0x300180, 0x10);
|
const auto first = index.Query(0x300180, 0x10);
|
||||||
Check(first.size() == 1 && first.front() == 31, "strict byte overlap selects only the matching owner");
|
Check(first.size() == 1 && first.front() == 31,
|
||||||
const auto predicate_filtered = index.Query(0x300000, 0x1000, [](uint32_t owner) { return owner == 32; });
|
"strict byte overlap selects only the matching owner");
|
||||||
Check(predicate_filtered.size() == 1 && predicate_filtered.front() == 32, "supplied predicate filters query owners");
|
const auto predicate_filtered =
|
||||||
|
index.Query(0x300000, 0x1000, [](uint32_t owner) { return owner == 32; });
|
||||||
|
Check(predicate_filtered.size() == 1 && predicate_filtered.front() == 32,
|
||||||
|
"supplied predicate filters query owners");
|
||||||
}
|
}
|
||||||
|
|
||||||
} // namespace
|
} // namespace
|
||||||
|
|||||||
+135
-221
@@ -1,7 +1,7 @@
|
|||||||
#include "graphics/host_gpu/memoryTracker.h"
|
|
||||||
#include "graphics/host_gpu/rangeSet.h"
|
|
||||||
#include "common/assert.h"
|
#include "common/assert.h"
|
||||||
#include "common/virtualMemory.h"
|
#include "common/virtualMemory.h"
|
||||||
|
#include "graphics/host_gpu/memoryTracker.h"
|
||||||
|
#include "graphics/host_gpu/rangeSet.h"
|
||||||
|
|
||||||
#include <atomic>
|
#include <atomic>
|
||||||
#include <cstdint>
|
#include <cstdint>
|
||||||
@@ -30,15 +30,15 @@
|
|||||||
|
|
||||||
namespace {
|
namespace {
|
||||||
|
|
||||||
using Libs::Graphics::DirtySource;
|
|
||||||
using Libs::Graphics::CpuFaultAction;
|
using Libs::Graphics::CpuFaultAction;
|
||||||
|
using Libs::Graphics::DirtySource;
|
||||||
using Libs::Graphics::MemoryTracker;
|
using Libs::Graphics::MemoryTracker;
|
||||||
using Libs::Graphics::PageFaultAccess;
|
using Libs::Graphics::PageFaultAccess;
|
||||||
using Libs::Graphics::PageFaultPhase;
|
using Libs::Graphics::PageFaultPhase;
|
||||||
using Libs::Graphics::PageManager;
|
using Libs::Graphics::PageManager;
|
||||||
using Libs::Graphics::PageWatchMode;
|
using Libs::Graphics::PageWatchMode;
|
||||||
using Libs::Graphics::RegionManager;
|
|
||||||
using Libs::Graphics::RangeSet;
|
using Libs::Graphics::RangeSet;
|
||||||
|
using Libs::Graphics::RegionManager;
|
||||||
|
|
||||||
void Check(bool value, const char* text) {
|
void Check(bool value, const char* text) {
|
||||||
if (!value) {
|
if (!value) {
|
||||||
@@ -59,12 +59,9 @@ constexpr uint32_t MEM_RELEASE = 0;
|
|||||||
|
|
||||||
int ToHostProt(uint32_t protection) {
|
int ToHostProt(uint32_t protection) {
|
||||||
switch (protection) {
|
switch (protection) {
|
||||||
case PAGE_NOACCESS:
|
case PAGE_NOACCESS: return PROT_NONE;
|
||||||
return PROT_NONE;
|
case PAGE_READONLY: return PROT_READ;
|
||||||
case PAGE_READONLY:
|
default: return PROT_READ | PROT_WRITE;
|
||||||
return PROT_READ;
|
|
||||||
default:
|
|
||||||
return PROT_READ | PROT_WRITE;
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -92,7 +89,9 @@ uint32_t Protection(const void *address) {
|
|||||||
return result;
|
return result;
|
||||||
}
|
}
|
||||||
|
|
||||||
bool IsWritable(const void *address) { return Protection(address) == PAGE_READWRITE; }
|
bool IsWritable(const void* address) {
|
||||||
|
return Protection(address) == PAGE_READWRITE;
|
||||||
|
}
|
||||||
|
|
||||||
// munmap needs a length where VirtualFree's callers pass 0, so sizes are remembered here.
|
// munmap needs a length where VirtualFree's callers pass 0, so sizes are remembered here.
|
||||||
std::map<void*, size_t>& AllocationSizes() {
|
std::map<void*, size_t>& AllocationSizes() {
|
||||||
@@ -102,8 +101,8 @@ std::map<void *, size_t> &AllocationSizes() {
|
|||||||
|
|
||||||
void* VirtualAlloc(void* address, size_t size, DWORD /*type*/, uint32_t protection) {
|
void* VirtualAlloc(void* address, size_t size, DWORD /*type*/, uint32_t protection) {
|
||||||
const int extra = address != nullptr ? MAP_FIXED_NOREPLACE : 0;
|
const int extra = address != nullptr ? MAP_FIXED_NOREPLACE : 0;
|
||||||
void *raw = ::mmap(address, size, ToHostProt(protection),
|
void* raw =
|
||||||
MAP_PRIVATE | MAP_ANONYMOUS | extra, -1, 0);
|
::mmap(address, size, ToHostProt(protection), MAP_PRIVATE | MAP_ANONYMOUS | extra, -1, 0);
|
||||||
if (raw == MAP_FAILED) {
|
if (raw == MAP_FAILED) {
|
||||||
return nullptr;
|
return nullptr;
|
||||||
}
|
}
|
||||||
@@ -180,17 +179,14 @@ uint32_t Protection(const void *address) {
|
|||||||
class SharedPage final {
|
class SharedPage final {
|
||||||
public:
|
public:
|
||||||
SharedPage(uintptr_t address, uint64_t size) {
|
SharedPage(uintptr_t address, uint64_t size) {
|
||||||
mapping_ = CreateFileMappingA(INVALID_HANDLE_VALUE, nullptr, PAGE_READWRITE,
|
mapping_ =
|
||||||
static_cast<DWORD>(size >> 32u),
|
CreateFileMappingA(INVALID_HANDLE_VALUE, nullptr, PAGE_READWRITE,
|
||||||
static_cast<DWORD>(size), nullptr);
|
static_cast<DWORD>(size >> 32u), static_cast<DWORD>(size), nullptr);
|
||||||
Check(mapping_ != nullptr, "CreateFileMapping failed");
|
Check(mapping_ != nullptr, "CreateFileMapping failed");
|
||||||
guest = static_cast<uint8_t *>(MapViewOfFileEx(
|
guest = static_cast<uint8_t*>(MapViewOfFileEx(mapping_, FILE_MAP_ALL_ACCESS, 0, 0, size,
|
||||||
mapping_, FILE_MAP_ALL_ACCESS, 0, 0, size,
|
|
||||||
reinterpret_cast<void*>(address)));
|
reinterpret_cast<void*>(address)));
|
||||||
Check(guest == reinterpret_cast<void *>(address),
|
Check(guest == reinterpret_cast<void*>(address), "fixed shared view failed");
|
||||||
"fixed shared view failed");
|
backing = static_cast<uint8_t*>(MapViewOfFile(mapping_, FILE_MAP_ALL_ACCESS, 0, 0, size));
|
||||||
backing = static_cast<uint8_t *>(
|
|
||||||
MapViewOfFile(mapping_, FILE_MAP_ALL_ACCESS, 0, 0, size));
|
|
||||||
Check(backing != nullptr, "shared backing view failed");
|
Check(backing != nullptr, "shared backing view failed");
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -211,8 +207,7 @@ class SharedPage final {
|
|||||||
};
|
};
|
||||||
#endif
|
#endif
|
||||||
|
|
||||||
bool ProtectAddressSpace(uint64_t vaddr, uint64_t size,
|
bool ProtectAddressSpace(uint64_t vaddr, uint64_t size, Common::VirtualMemory::Mode mode) {
|
||||||
Common::VirtualMemory::Mode mode) {
|
|
||||||
uint32_t protection = PAGE_NOACCESS;
|
uint32_t protection = PAGE_NOACCESS;
|
||||||
if (mode == Common::VirtualMemory::Mode::Read) {
|
if (mode == Common::VirtualMemory::Mode::Read) {
|
||||||
protection = PAGE_READONLY;
|
protection = PAGE_READONLY;
|
||||||
@@ -220,8 +215,7 @@ bool ProtectAddressSpace(uint64_t vaddr, uint64_t size,
|
|||||||
protection = PAGE_READWRITE;
|
protection = PAGE_READWRITE;
|
||||||
}
|
}
|
||||||
DWORD old_protection = 0;
|
DWORD old_protection = 0;
|
||||||
return VirtualProtect(reinterpret_cast<void *>(vaddr), size, protection,
|
return VirtualProtect(reinterpret_cast<void*>(vaddr), size, protection, &old_protection) != 0;
|
||||||
&old_protection) != 0;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
#if 1
|
#if 1
|
||||||
@@ -242,8 +236,7 @@ struct TrackerHarness {
|
|||||||
: self->target->InvalidateRegion(vaddr, size, phase);
|
: self->target->InvalidateRegion(vaddr, size, phase);
|
||||||
}
|
}
|
||||||
|
|
||||||
explicit TrackerHarness(
|
explicit TrackerHarness(PageWatchMode gpu_watch_mode = PageWatchMode::ReadWrite)
|
||||||
PageWatchMode gpu_watch_mode = PageWatchMode::ReadWrite)
|
|
||||||
: page_manager(Fault, this), tracker(page_manager, gpu_watch_mode) {
|
: page_manager(Fault, this), tracker(page_manager, gpu_watch_mode) {
|
||||||
target = &tracker;
|
target = &tracker;
|
||||||
}
|
}
|
||||||
@@ -266,8 +259,7 @@ struct SharedTrackerHarness {
|
|||||||
return first || second;
|
return first || second;
|
||||||
}
|
}
|
||||||
|
|
||||||
SharedTrackerHarness()
|
SharedTrackerHarness(): page_manager(Fault, this), first(page_manager), second(page_manager) {}
|
||||||
: page_manager(Fault, this), first(page_manager), second(page_manager) {}
|
|
||||||
|
|
||||||
PageManager page_manager;
|
PageManager page_manager;
|
||||||
MemoryTracker first;
|
MemoryTracker first;
|
||||||
@@ -275,16 +267,15 @@ struct SharedTrackerHarness {
|
|||||||
};
|
};
|
||||||
|
|
||||||
struct SharedMetadataImageHarness {
|
struct SharedMetadataImageHarness {
|
||||||
static bool Fault(void *context, PageFaultAccess access, uint64_t vaddr,
|
static bool Fault(void* context, PageFaultAccess access, uint64_t vaddr, uint64_t size,
|
||||||
uint64_t size, PageFaultPhase phase) noexcept {
|
PageFaultPhase phase) noexcept {
|
||||||
auto* self = static_cast<SharedMetadataImageHarness*>(context);
|
auto* self = static_cast<SharedMetadataImageHarness*>(context);
|
||||||
if (self == nullptr ||
|
if (self == nullptr ||
|
||||||
(access != PageFaultAccess::Read && access != PageFaultAccess::Write)) {
|
(access != PageFaultAccess::Read && access != PageFaultAccess::Write)) {
|
||||||
EXIT("shared metadata/image test received an invalid fault\n");
|
EXIT("shared metadata/image test received an invalid fault\n");
|
||||||
}
|
}
|
||||||
const bool metadata = access == PageFaultAccess::Write &&
|
const bool metadata = access == PageFaultAccess::Write &&
|
||||||
self->metadata.InvalidateVirtualGpuWrite(
|
self->metadata.InvalidateVirtualGpuWrite(access, vaddr, size, phase);
|
||||||
access, vaddr, size, phase);
|
|
||||||
const bool image = self->image.InvalidateRegion(vaddr, size, phase);
|
const bool image = self->image.InvalidateRegion(vaddr, size, phase);
|
||||||
return metadata || image;
|
return metadata || image;
|
||||||
}
|
}
|
||||||
@@ -299,8 +290,8 @@ struct SharedMetadataImageHarness {
|
|||||||
};
|
};
|
||||||
|
|
||||||
struct SplitTrackerHarness {
|
struct SplitTrackerHarness {
|
||||||
static bool Fault(void *context, PageFaultAccess access, uint64_t vaddr,
|
static bool Fault(void* context, PageFaultAccess access, uint64_t vaddr, uint64_t size,
|
||||||
uint64_t size, PageFaultPhase phase) noexcept {
|
PageFaultPhase phase) noexcept {
|
||||||
auto* self = static_cast<SplitTrackerHarness*>(context);
|
auto* self = static_cast<SplitTrackerHarness*>(context);
|
||||||
if (self == nullptr) {
|
if (self == nullptr) {
|
||||||
EXIT("split memory-tracker test fault has no harness\n");
|
EXIT("split memory-tracker test fault has no harness\n");
|
||||||
@@ -309,8 +300,7 @@ struct SplitTrackerHarness {
|
|||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
const bool buffer = self->buffer.InvalidateRegion(vaddr, size, phase);
|
const bool buffer = self->buffer.InvalidateRegion(vaddr, size, phase);
|
||||||
const bool metadata = self->metadata.InvalidateVirtualGpuWrite(
|
const bool metadata = self->metadata.InvalidateVirtualGpuWrite(access, vaddr, size, phase);
|
||||||
access, vaddr, size, phase);
|
|
||||||
const bool image = self->image.InvalidateRegion(vaddr, size, phase);
|
const bool image = self->image.InvalidateRegion(vaddr, size, phase);
|
||||||
if (static_cast<unsigned>(buffer) + static_cast<unsigned>(metadata) +
|
if (static_cast<unsigned>(buffer) + static_cast<unsigned>(metadata) +
|
||||||
static_cast<unsigned>(image) !=
|
static_cast<unsigned>(image) !=
|
||||||
@@ -331,8 +321,8 @@ struct SplitTrackerHarness {
|
|||||||
};
|
};
|
||||||
|
|
||||||
struct DownloadTrackerHarness {
|
struct DownloadTrackerHarness {
|
||||||
static bool Fault(void *context, PageFaultAccess access, uint64_t vaddr,
|
static bool Fault(void* context, PageFaultAccess access, uint64_t vaddr, uint64_t size,
|
||||||
uint64_t size, PageFaultPhase phase) noexcept {
|
PageFaultPhase phase) noexcept {
|
||||||
auto* self = static_cast<DownloadTrackerHarness*>(context);
|
auto* self = static_cast<DownloadTrackerHarness*>(context);
|
||||||
if (self == nullptr) {
|
if (self == nullptr) {
|
||||||
EXIT("download memory-tracker test fault has no harness\n");
|
EXIT("download memory-tracker test fault has no harness\n");
|
||||||
@@ -355,21 +345,18 @@ struct DownloadTrackerHarness {
|
|||||||
if (self->pending_access != access || self->download_data.empty()) {
|
if (self->pending_access != access || self->download_data.empty()) {
|
||||||
EXIT("download memory-tracker test has invalid completion state\n");
|
EXIT("download memory-tracker test has invalid completion state\n");
|
||||||
}
|
}
|
||||||
if (self->backing == nullptr ||
|
if (self->backing == nullptr || self->download_address < self->guest_address) {
|
||||||
self->download_address < self->guest_address) {
|
|
||||||
EXIT("download memory-tracker test has no backing alias\n");
|
EXIT("download memory-tracker test has no backing alias\n");
|
||||||
}
|
}
|
||||||
std::memcpy(self->backing + self->download_address - self->guest_address,
|
std::memcpy(self->backing + self->download_address - self->guest_address,
|
||||||
self->download_data.data(), self->download_data.size());
|
self->download_data.data(), self->download_data.size());
|
||||||
}
|
}
|
||||||
const bool completed =
|
const bool completed = self->tracker.CompleteCpuFault(vaddr, size, access, downloaded);
|
||||||
self->tracker.CompleteCpuFault(vaddr, size, access, downloaded);
|
|
||||||
self->pending_access = PageFaultAccess::Unknown;
|
self->pending_access = PageFaultAccess::Unknown;
|
||||||
return completed;
|
return completed;
|
||||||
}
|
}
|
||||||
|
|
||||||
DownloadTrackerHarness()
|
DownloadTrackerHarness(): page_manager(Fault, this), tracker(page_manager) {}
|
||||||
: page_manager(Fault, this), tracker(page_manager) {}
|
|
||||||
|
|
||||||
PageFaultAccess pending_access = PageFaultAccess::Unknown;
|
PageFaultAccess pending_access = PageFaultAccess::Unknown;
|
||||||
uint64_t download_address = 0;
|
uint64_t download_address = 0;
|
||||||
@@ -399,8 +386,7 @@ LONG CALLBACK NativeTrackerFaultHandler(EXCEPTION_POINTERS *exception) {
|
|||||||
return EXCEPTION_CONTINUE_SEARCH;
|
return EXCEPTION_CONTINUE_SEARCH;
|
||||||
}
|
}
|
||||||
g_native_fault_entered.store(true, std::memory_order_release);
|
g_native_fault_entered.store(true, std::memory_order_release);
|
||||||
return page_manager->HandleFault(
|
return page_manager->HandleFault(access, exception->ExceptionRecord->ExceptionInformation[1])
|
||||||
access, exception->ExceptionRecord->ExceptionInformation[1])
|
|
||||||
? EXCEPTION_CONTINUE_EXECUTION
|
? EXCEPTION_CONTINUE_EXECUTION
|
||||||
: EXCEPTION_CONTINUE_SEARCH;
|
: EXCEPTION_CONTINUE_SEARCH;
|
||||||
}
|
}
|
||||||
@@ -449,8 +435,7 @@ void TestPendingFaultBlocksUploadConsumption() {
|
|||||||
constexpr uint64_t region_size = 4ull * 1024ull * 1024ull;
|
constexpr uint64_t region_size = 4ull * 1024ull * 1024ull;
|
||||||
PageManager page_manager(DummyFault, nullptr);
|
PageManager page_manager(DummyFault, nullptr);
|
||||||
const auto page_size = page_manager.GetPageSize();
|
const auto page_size = page_manager.GetPageSize();
|
||||||
auto *memory = static_cast<uint8_t *>(
|
auto* memory = static_cast<uint8_t*>(VirtualAlloc(reinterpret_cast<void*>(base), page_size,
|
||||||
VirtualAlloc(reinterpret_cast<void *>(base), page_size,
|
|
||||||
MEM_RESERVE | MEM_COMMIT, PAGE_READWRITE));
|
MEM_RESERVE | MEM_COMMIT, PAGE_READWRITE));
|
||||||
Check(memory == reinterpret_cast<void*>(base), "fixed VirtualAlloc failed");
|
Check(memory == reinterpret_cast<void*>(base), "fixed VirtualAlloc failed");
|
||||||
const auto address = reinterpret_cast<uint64_t>(memory);
|
const auto address = reinterpret_cast<uint64_t>(memory);
|
||||||
@@ -472,12 +457,10 @@ void TestPendingFaultBlocksUploadConsumption() {
|
|||||||
uint32_t ranges = 0;
|
uint32_t ranges = 0;
|
||||||
{
|
{
|
||||||
std::scoped_lock lock(region.lock);
|
std::scoped_lock lock(region.lock);
|
||||||
const auto pending_change =
|
const auto pending_change = region.ForEachModifiedRange<DirtySource::Cpu, true>(
|
||||||
region.ForEachModifiedRange<DirtySource::Cpu, true>(
|
|
||||||
address, page_size, [&](uint64_t, uint64_t) noexcept { ranges++; });
|
address, page_size, [&](uint64_t, uint64_t) noexcept { ranges++; });
|
||||||
Check(ranges == 0 && pending_change.none() &&
|
Check(ranges == 0 && pending_change.none() &&
|
||||||
region.IsModified<DirtySource::Cpu>(address - region.GetCpuAddr(),
|
region.IsModified<DirtySource::Cpu>(address - region.GetCpuAddr(), page_size),
|
||||||
page_size),
|
|
||||||
"pending fault page was consumed by upload");
|
"pending fault page was consumed by upload");
|
||||||
Check(region.CompleteCpuFault(address, 1, PageFaultAccess::Write, false),
|
Check(region.CompleteCpuFault(address, 1, PageFaultAccess::Write, false),
|
||||||
"fault completion was not recorded");
|
"fault completion was not recorded");
|
||||||
@@ -499,20 +482,17 @@ void TestCleanReadFaultPreservesCpuState() {
|
|||||||
PageManager page_manager(DummyFault, nullptr);
|
PageManager page_manager(DummyFault, nullptr);
|
||||||
MemoryTracker tracker(page_manager);
|
MemoryTracker tracker(page_manager);
|
||||||
const auto page_size = page_manager.GetPageSize();
|
const auto page_size = page_manager.GetPageSize();
|
||||||
auto *memory = static_cast<uint8_t *>(VirtualAlloc(
|
auto* memory = static_cast<uint8_t*>(VirtualAlloc(reinterpret_cast<void*>(base), page_size,
|
||||||
reinterpret_cast<void *>(base), page_size, MEM_RESERVE | MEM_COMMIT,
|
MEM_RESERVE | MEM_COMMIT, PAGE_READWRITE));
|
||||||
PAGE_READWRITE));
|
|
||||||
Check(memory == reinterpret_cast<void*>(base), "fixed VirtualAlloc failed");
|
Check(memory == reinterpret_cast<void*>(base), "fixed VirtualAlloc failed");
|
||||||
const auto address = reinterpret_cast<uint64_t>(memory);
|
const auto address = reinterpret_cast<uint64_t>(memory);
|
||||||
page_manager.OnGpuMap(address, page_size);
|
page_manager.OnGpuMap(address, page_size);
|
||||||
tracker.ForEachUploadRange(address, page_size, false,
|
tracker.ForEachUploadRange(
|
||||||
[](uint64_t, uint64_t) noexcept {},
|
address, page_size, false, [](uint64_t, uint64_t) noexcept {}, []() noexcept {});
|
||||||
[]() noexcept {});
|
|
||||||
Check(!tracker.IsRegionCpuModified(address, page_size) &&
|
Check(!tracker.IsRegionCpuModified(address, page_size) &&
|
||||||
!tracker.IsRegionGpuModified(address, page_size),
|
!tracker.IsRegionGpuModified(address, page_size),
|
||||||
"clean read-fault setup retained dirty ownership");
|
"clean read-fault setup retained dirty ownership");
|
||||||
Check(tracker.BeginCpuFault(address, 1, PageFaultAccess::Read) ==
|
Check(tracker.BeginCpuFault(address, 1, PageFaultAccess::Read) == CpuFaultAction::Continue,
|
||||||
CpuFaultAction::Continue,
|
|
||||||
"clean tracked read fault was not accepted");
|
"clean tracked read fault was not accepted");
|
||||||
Check(tracker.CompleteCpuFault(address, 1, PageFaultAccess::Read, false),
|
Check(tracker.CompleteCpuFault(address, 1, PageFaultAccess::Read, false),
|
||||||
"clean tracked read fault did not complete");
|
"clean tracked read fault did not complete");
|
||||||
@@ -535,8 +515,7 @@ void TestGpuDownloadFaultOwnership() {
|
|||||||
harness.backing = shared.backing;
|
harness.backing = shared.backing;
|
||||||
harness.page_manager.OnGpuMap(address, page_size);
|
harness.page_manager.OnGpuMap(address, page_size);
|
||||||
harness.tracker.ForEachUploadRange(
|
harness.tracker.ForEachUploadRange(
|
||||||
address, page_size, true, [](uint64_t, uint64_t) noexcept {},
|
address, page_size, true, [](uint64_t, uint64_t) noexcept {}, []() noexcept {});
|
||||||
[]() noexcept {});
|
|
||||||
harness.download_address = address + 32;
|
harness.download_address = address + 32;
|
||||||
harness.download_data = {0x11, 0x22, 0x33, 0x44};
|
harness.download_data = {0x11, 0x22, 0x33, 0x44};
|
||||||
Check(harness.page_manager.HandleFault(PageFaultAccess::Read, address + 32),
|
Check(harness.page_manager.HandleFault(PageFaultAccess::Read, address + 32),
|
||||||
@@ -544,8 +523,7 @@ void TestGpuDownloadFaultOwnership() {
|
|||||||
Check(std::memcmp(memory + 32, harness.download_data.data(), 4) == 0 &&
|
Check(std::memcmp(memory + 32, harness.download_data.data(), 4) == 0 &&
|
||||||
!harness.tracker.IsRegionGpuModified(address, page_size) &&
|
!harness.tracker.IsRegionGpuModified(address, page_size) &&
|
||||||
!harness.tracker.IsRegionCpuModified(address, page_size) &&
|
!harness.tracker.IsRegionCpuModified(address, page_size) &&
|
||||||
Protection(memory) == PAGE_READONLY &&
|
Protection(memory) == PAGE_READONLY && harness.page_manager.IsTracked(address),
|
||||||
harness.page_manager.IsTracked(address),
|
|
||||||
"GPU readback did not leave a clean write-watched page");
|
"GPU readback did not leave a clean write-watched page");
|
||||||
|
|
||||||
Check(harness.page_manager.HandleFault(PageFaultAccess::Write, address + 32),
|
Check(harness.page_manager.HandleFault(PageFaultAccess::Write, address + 32),
|
||||||
@@ -554,8 +532,7 @@ void TestGpuDownloadFaultOwnership() {
|
|||||||
"post-read CPU write did not claim CPU ownership");
|
"post-read CPU write did not claim CPU ownership");
|
||||||
|
|
||||||
harness.tracker.ForEachUploadRange(
|
harness.tracker.ForEachUploadRange(
|
||||||
address, page_size, true, [](uint64_t, uint64_t) noexcept {},
|
address, page_size, true, [](uint64_t, uint64_t) noexcept {}, []() noexcept {});
|
||||||
[]() noexcept {});
|
|
||||||
harness.download_data = {0xaa, 0xbb, 0xcc, 0xdd};
|
harness.download_data = {0xaa, 0xbb, 0xcc, 0xdd};
|
||||||
Check(harness.page_manager.HandleFault(PageFaultAccess::Write, address + 33),
|
Check(harness.page_manager.HandleFault(PageFaultAccess::Write, address + 33),
|
||||||
"GPU-dirty write fault was not handled");
|
"GPU-dirty write fault was not handled");
|
||||||
@@ -571,16 +548,14 @@ void TestVirtualGpuWriteDiscard() {
|
|||||||
TrackerHarness harness(PageWatchMode::Write);
|
TrackerHarness harness(PageWatchMode::Write);
|
||||||
harness.discard_virtual = true;
|
harness.discard_virtual = true;
|
||||||
const auto page_size = harness.page_manager.GetPageSize();
|
const auto page_size = harness.page_manager.GetPageSize();
|
||||||
auto *memory = static_cast<uint8_t *>(
|
auto* memory = static_cast<uint8_t*>(VirtualAlloc(reinterpret_cast<void*>(base), page_size,
|
||||||
VirtualAlloc(reinterpret_cast<void *>(base), page_size,
|
|
||||||
MEM_RESERVE | MEM_COMMIT, PAGE_READWRITE));
|
MEM_RESERVE | MEM_COMMIT, PAGE_READWRITE));
|
||||||
Check(memory == reinterpret_cast<void*>(base), "fixed VirtualAlloc failed");
|
Check(memory == reinterpret_cast<void*>(base), "fixed VirtualAlloc failed");
|
||||||
const auto address = reinterpret_cast<uint64_t>(memory);
|
const auto address = reinterpret_cast<uint64_t>(memory);
|
||||||
memory[8] = 0x5a;
|
memory[8] = 0x5a;
|
||||||
harness.page_manager.OnGpuMap(address, page_size);
|
harness.page_manager.OnGpuMap(address, page_size);
|
||||||
harness.tracker.ForEachUploadRange(
|
harness.tracker.ForEachUploadRange(
|
||||||
address, page_size, true, [](uint64_t, uint64_t) noexcept {},
|
address, page_size, true, [](uint64_t, uint64_t) noexcept {}, []() noexcept {});
|
||||||
[]() noexcept {});
|
|
||||||
Check(Protection(memory) == PAGE_READONLY && memory[8] == 0x5a &&
|
Check(Protection(memory) == PAGE_READONLY && memory[8] == 0x5a &&
|
||||||
harness.tracker.IsRegionGpuModified(address, page_size),
|
harness.tracker.IsRegionGpuModified(address, page_size),
|
||||||
"virtual GPU ownership did not preserve authoritative backing reads");
|
"virtual GPU ownership did not preserve authoritative backing reads");
|
||||||
@@ -597,40 +572,33 @@ void TestSameSlabTrackerArbitration() {
|
|||||||
constexpr uintptr_t base = 0x0000000200010000ull;
|
constexpr uintptr_t base = 0x0000000200010000ull;
|
||||||
SplitTrackerHarness harness;
|
SplitTrackerHarness harness;
|
||||||
const auto page_size = harness.page_manager.GetPageSize();
|
const auto page_size = harness.page_manager.GetPageSize();
|
||||||
auto *memory = static_cast<uint8_t *>(VirtualAlloc(
|
auto* memory = static_cast<uint8_t*>(VirtualAlloc(reinterpret_cast<void*>(base), page_size * 3,
|
||||||
reinterpret_cast<void *>(base), page_size * 3,
|
|
||||||
MEM_RESERVE | MEM_COMMIT, PAGE_READWRITE));
|
MEM_RESERVE | MEM_COMMIT, PAGE_READWRITE));
|
||||||
Check(memory == reinterpret_cast<void*>(base), "fixed VirtualAlloc failed");
|
Check(memory == reinterpret_cast<void*>(base), "fixed VirtualAlloc failed");
|
||||||
const auto address = reinterpret_cast<uint64_t>(memory);
|
const auto address = reinterpret_cast<uint64_t>(memory);
|
||||||
harness.page_manager.OnGpuMap(address, page_size * 3);
|
harness.page_manager.OnGpuMap(address, page_size * 3);
|
||||||
|
|
||||||
harness.buffer.ForEachUploadRange(
|
harness.buffer.ForEachUploadRange(
|
||||||
address, page_size, false, [](uint64_t, uint64_t) noexcept {},
|
address, page_size, false, [](uint64_t, uint64_t) noexcept {}, []() noexcept {});
|
||||||
[]() noexcept {});
|
|
||||||
harness.image.ForEachUploadRange(
|
harness.image.ForEachUploadRange(
|
||||||
address + page_size, page_size, false,
|
address + page_size, page_size, false, [](uint64_t, uint64_t) noexcept {},
|
||||||
[](uint64_t, uint64_t) noexcept {}, []() noexcept {});
|
[]() noexcept {});
|
||||||
harness.metadata.ForEachUploadRange(
|
harness.metadata.ForEachUploadRange(
|
||||||
address + page_size * 2, page_size, true,
|
address + page_size * 2, page_size, true, [](uint64_t, uint64_t) noexcept {},
|
||||||
[](uint64_t, uint64_t) noexcept {}, []() noexcept {});
|
[]() noexcept {});
|
||||||
|
|
||||||
Check(harness.buffer.BeginCpuFault(address + page_size * 2, 1) ==
|
Check(harness.buffer.BeginCpuFault(address + page_size * 2, 1) == CpuFaultAction::Untracked &&
|
||||||
CpuFaultAction::Untracked &&
|
harness.image.BeginCpuFault(address + page_size * 2, 1) == CpuFaultAction::Untracked,
|
||||||
harness.image.BeginCpuFault(address + page_size * 2, 1) ==
|
|
||||||
CpuFaultAction::Untracked,
|
|
||||||
"unrelated same-slab trackers claimed the metadata page");
|
"unrelated same-slab trackers claimed the metadata page");
|
||||||
Check(harness.page_manager.HandleFault(PageFaultAccess::Write,
|
Check(harness.page_manager.HandleFault(PageFaultAccess::Write, address + page_size * 2 + 8),
|
||||||
address + page_size * 2 + 8),
|
|
||||||
"metadata write fault was not exclusively handled");
|
"metadata write fault was not exclusively handled");
|
||||||
Check(harness.page_manager.HandleFault(PageFaultAccess::Write, address + 8),
|
Check(harness.page_manager.HandleFault(PageFaultAccess::Write, address + 8),
|
||||||
"buffer write fault was not exclusively handled");
|
"buffer write fault was not exclusively handled");
|
||||||
Check(harness.page_manager.HandleFault(PageFaultAccess::Write,
|
Check(harness.page_manager.HandleFault(PageFaultAccess::Write, address + page_size + 8),
|
||||||
address + page_size + 8),
|
|
||||||
"image write fault was not exclusively handled");
|
"image write fault was not exclusively handled");
|
||||||
Check(harness.buffer.IsRegionCpuModified(address, page_size) &&
|
Check(harness.buffer.IsRegionCpuModified(address, page_size) &&
|
||||||
harness.image.IsRegionCpuModified(address + page_size, page_size) &&
|
harness.image.IsRegionCpuModified(address + page_size, page_size) &&
|
||||||
harness.metadata.IsRegionCpuModified(address + page_size * 2,
|
harness.metadata.IsRegionCpuModified(address + page_size * 2, page_size) &&
|
||||||
page_size) &&
|
|
||||||
IsWritable(memory) && IsWritable(memory + page_size) &&
|
IsWritable(memory) && IsWritable(memory + page_size) &&
|
||||||
IsWritable(memory + page_size * 2),
|
IsWritable(memory + page_size * 2),
|
||||||
"exclusive same-slab faults did not transfer exact CPU ownership");
|
"exclusive same-slab faults did not transfer exact CPU ownership");
|
||||||
@@ -646,18 +614,15 @@ void TestSharedMetadataAndImagePageFault() {
|
|||||||
constexpr uintptr_t base = 0x0000000200010000ull;
|
constexpr uintptr_t base = 0x0000000200010000ull;
|
||||||
SharedMetadataImageHarness harness;
|
SharedMetadataImageHarness harness;
|
||||||
const auto page_size = harness.page_manager.GetPageSize();
|
const auto page_size = harness.page_manager.GetPageSize();
|
||||||
auto *memory = static_cast<uint8_t *>(VirtualAlloc(
|
auto* memory = static_cast<uint8_t*>(VirtualAlloc(reinterpret_cast<void*>(base), page_size,
|
||||||
reinterpret_cast<void *>(base), page_size, MEM_RESERVE | MEM_COMMIT,
|
MEM_RESERVE | MEM_COMMIT, PAGE_READWRITE));
|
||||||
PAGE_READWRITE));
|
|
||||||
Check(memory == reinterpret_cast<void*>(base), "fixed VirtualAlloc failed");
|
Check(memory == reinterpret_cast<void*>(base), "fixed VirtualAlloc failed");
|
||||||
const auto address = reinterpret_cast<uint64_t>(memory);
|
const auto address = reinterpret_cast<uint64_t>(memory);
|
||||||
harness.page_manager.OnGpuMap(address, page_size);
|
harness.page_manager.OnGpuMap(address, page_size);
|
||||||
harness.metadata.ForEachUploadRange(
|
harness.metadata.ForEachUploadRange(
|
||||||
address, page_size, true, [](uint64_t, uint64_t) noexcept {},
|
address, page_size, true, [](uint64_t, uint64_t) noexcept {}, []() noexcept {});
|
||||||
[]() noexcept {});
|
|
||||||
harness.image.ForEachUploadRange(
|
harness.image.ForEachUploadRange(
|
||||||
address, page_size, false, [](uint64_t, uint64_t) noexcept {},
|
address, page_size, false, [](uint64_t, uint64_t) noexcept {}, []() noexcept {});
|
||||||
[]() noexcept {});
|
|
||||||
Check(Protection(memory) == PAGE_READONLY &&
|
Check(Protection(memory) == PAGE_READONLY &&
|
||||||
harness.metadata.IsRegionGpuModified(address, page_size) &&
|
harness.metadata.IsRegionGpuModified(address, page_size) &&
|
||||||
!harness.image.IsRegionCpuModified(address, page_size),
|
!harness.image.IsRegionCpuModified(address, page_size),
|
||||||
@@ -666,8 +631,7 @@ void TestSharedMetadataAndImagePageFault() {
|
|||||||
"shared metadata/image write fault was not handled");
|
"shared metadata/image write fault was not handled");
|
||||||
Check(!harness.metadata.IsRegionGpuModified(address, page_size) &&
|
Check(!harness.metadata.IsRegionGpuModified(address, page_size) &&
|
||||||
harness.metadata.IsRegionCpuModified(address, page_size) &&
|
harness.metadata.IsRegionCpuModified(address, page_size) &&
|
||||||
harness.image.IsRegionCpuModified(address, page_size) &&
|
harness.image.IsRegionCpuModified(address, page_size) && IsWritable(memory),
|
||||||
IsWritable(memory),
|
|
||||||
"shared write fault did not invalidate both native trackers");
|
"shared write fault did not invalidate both native trackers");
|
||||||
harness.metadata.UntrackMemory(address, page_size);
|
harness.metadata.UntrackMemory(address, page_size);
|
||||||
harness.image.UntrackMemory(address, page_size);
|
harness.image.UntrackMemory(address, page_size);
|
||||||
@@ -710,25 +674,22 @@ void TestRangeInvalidation() {
|
|||||||
auto& tracker = harness.tracker;
|
auto& tracker = harness.tracker;
|
||||||
auto& page_manager = harness.page_manager;
|
auto& page_manager = harness.page_manager;
|
||||||
constexpr uint64_t size = Libs::Graphics::TRACKER_REGION_SIZE * 2;
|
constexpr uint64_t size = Libs::Graphics::TRACKER_REGION_SIZE * 2;
|
||||||
auto *memory = static_cast<uint8_t *>(
|
auto* memory = static_cast<uint8_t*>(VirtualAlloc(reinterpret_cast<void*>(base), size,
|
||||||
VirtualAlloc(reinterpret_cast<void *>(base), size, MEM_RESERVE | MEM_COMMIT,
|
MEM_RESERVE | MEM_COMMIT, PAGE_READWRITE));
|
||||||
PAGE_READWRITE));
|
Check(memory == reinterpret_cast<void*>(base), "range invalidation allocation failed");
|
||||||
Check(memory == reinterpret_cast<void *>(base),
|
|
||||||
"range invalidation allocation failed");
|
|
||||||
const auto address = reinterpret_cast<uint64_t>(memory);
|
const auto address = reinterpret_cast<uint64_t>(memory);
|
||||||
page_manager.OnGpuMap(address, size);
|
page_manager.OnGpuMap(address, size);
|
||||||
|
|
||||||
tracker.ForEachUploadRange(
|
tracker.ForEachUploadRange(
|
||||||
address, size, true, [](uint64_t, uint64_t) noexcept {},
|
address, size, true, [](uint64_t, uint64_t) noexcept {}, []() noexcept {});
|
||||||
[]() noexcept {});
|
|
||||||
Check(tracker.IsRegionGpuModified(address, size) && !IsWritable(memory),
|
Check(tracker.IsRegionGpuModified(address, size) && !IsWritable(memory),
|
||||||
"range invalidation setup did not establish GPU ownership");
|
"range invalidation setup did not establish GPU ownership");
|
||||||
|
|
||||||
uint32_t flushes = 0;
|
uint32_t flushes = 0;
|
||||||
tracker.InvalidateRegion(address + 16, size - 32, [&] {
|
tracker.InvalidateRegion(address + 16, size - 32, [&] {
|
||||||
flushes++;
|
flushes++;
|
||||||
tracker.ForEachDownloadRange<true>(
|
tracker.ForEachDownloadRange<true>(address + 16, size - 32,
|
||||||
address + 16, size - 32, [](uint64_t, uint64_t) noexcept {});
|
[](uint64_t, uint64_t) noexcept {});
|
||||||
});
|
});
|
||||||
Check(flushes == 1 && !tracker.IsRegionGpuModified(address, size) &&
|
Check(flushes == 1 && !tracker.IsRegionGpuModified(address, size) &&
|
||||||
tracker.IsRegionCpuModified(address, size) && IsWritable(memory) &&
|
tracker.IsRegionCpuModified(address, size) && IsWritable(memory) &&
|
||||||
@@ -736,12 +697,10 @@ void TestRangeInvalidation() {
|
|||||||
"range invalidation did not batch ownership transfer across regions");
|
"range invalidation did not batch ownership transfer across regions");
|
||||||
|
|
||||||
tracker.InvalidateRegion(address + 16, size - 32, [&] { flushes++; });
|
tracker.InvalidateRegion(address + 16, size - 32, [&] { flushes++; });
|
||||||
Check(flushes == 1,
|
Check(flushes == 1, "clean range invalidation unnecessarily requested a GPU flush");
|
||||||
"clean range invalidation unnecessarily requested a GPU flush");
|
|
||||||
tracker.UntrackMemory(address, size);
|
tracker.UntrackMemory(address, size);
|
||||||
page_manager.OnGpuUnmap(address, size);
|
page_manager.OnGpuUnmap(address, size);
|
||||||
Check(VirtualFree(memory, 0, MEM_RELEASE) != 0,
|
Check(VirtualFree(memory, 0, MEM_RELEASE) != 0, "range invalidation VirtualFree failed");
|
||||||
"range invalidation VirtualFree failed");
|
|
||||||
}
|
}
|
||||||
|
|
||||||
void TestCpuDirtyUploadAndFault() {
|
void TestCpuDirtyUploadAndFault() {
|
||||||
@@ -750,14 +709,12 @@ void TestCpuDirtyUploadAndFault() {
|
|||||||
auto& tracker = harness.tracker;
|
auto& tracker = harness.tracker;
|
||||||
auto& page_manager = harness.page_manager;
|
auto& page_manager = harness.page_manager;
|
||||||
const auto page_size = page_manager.GetPageSize();
|
const auto page_size = page_manager.GetPageSize();
|
||||||
auto *memory = static_cast<uint8_t *>(
|
auto* memory = static_cast<uint8_t*>(VirtualAlloc(reinterpret_cast<void*>(base), page_size * 2,
|
||||||
VirtualAlloc(reinterpret_cast<void *>(base), page_size * 2,
|
|
||||||
MEM_RESERVE | MEM_COMMIT, PAGE_READWRITE));
|
MEM_RESERVE | MEM_COMMIT, PAGE_READWRITE));
|
||||||
Check(memory == reinterpret_cast<void*>(base), "fixed VirtualAlloc failed");
|
Check(memory == reinterpret_cast<void*>(base), "fixed VirtualAlloc failed");
|
||||||
const auto address = reinterpret_cast<uint64_t>(memory);
|
const auto address = reinterpret_cast<uint64_t>(memory);
|
||||||
page_manager.OnGpuMap(address, page_size * 2);
|
page_manager.OnGpuMap(address, page_size * 2);
|
||||||
Check(tracker.IsRegionCpuModified(address + 16, 32),
|
Check(tracker.IsRegionCpuModified(address + 16, 32), "new region was not CPU dirty");
|
||||||
"new region was not CPU dirty");
|
|
||||||
|
|
||||||
uint32_t ranges = 0;
|
uint32_t ranges = 0;
|
||||||
bool uploaded = false;
|
bool uploaded = false;
|
||||||
@@ -769,8 +726,7 @@ void TestCpuDirtyUploadAndFault() {
|
|||||||
ranges++;
|
ranges++;
|
||||||
},
|
},
|
||||||
[&]() noexcept { uploaded = true; });
|
[&]() noexcept { uploaded = true; });
|
||||||
Check(ranges == 1 && uploaded &&
|
Check(ranges == 1 && uploaded && !tracker.IsRegionCpuModified(address + 16, 32) &&
|
||||||
!tracker.IsRegionCpuModified(address + 16, 32) &&
|
|
||||||
!IsWritable(memory),
|
!IsWritable(memory),
|
||||||
"upload did not clear CPU dirty state and arm protection");
|
"upload did not clear CPU dirty state and arm protection");
|
||||||
|
|
||||||
@@ -779,13 +735,11 @@ void TestCpuDirtyUploadAndFault() {
|
|||||||
Check(tracker.IsRegionCpuModified(address + 16, 32) && IsWritable(memory),
|
Check(tracker.IsRegionCpuModified(address + 16, 32) && IsWritable(memory),
|
||||||
"fault did not restore CPU dirty state and write access");
|
"fault did not restore CPU dirty state and write access");
|
||||||
tracker.ForEachUploadRange(
|
tracker.ForEachUploadRange(
|
||||||
address + 16, 32, false, [](uint64_t, uint64_t) noexcept {},
|
address + 16, 32, false, [](uint64_t, uint64_t) noexcept {}, []() noexcept {});
|
||||||
[]() noexcept {});
|
|
||||||
Check(!tracker.IsRegionCpuModified(address + 16, 32) && !IsWritable(memory),
|
Check(!tracker.IsRegionCpuModified(address + 16, 32) && !IsWritable(memory),
|
||||||
"fault owner state did not support a balanced rearm");
|
"fault owner state did not support a balanced rearm");
|
||||||
tracker.MarkRegionAsCpuModified(address + 16, 32);
|
tracker.MarkRegionAsCpuModified(address + 16, 32);
|
||||||
Check(IsWritable(memory),
|
Check(IsWritable(memory), "explicit CPU dirty transition did not release the rearmed watch");
|
||||||
"explicit CPU dirty transition did not release the rearmed watch");
|
|
||||||
|
|
||||||
tracker.UntrackMemory(address, page_size * 2);
|
tracker.UntrackMemory(address, page_size * 2);
|
||||||
Check(VirtualFree(memory, 0, MEM_RELEASE) != 0, "VirtualFree failed");
|
Check(VirtualFree(memory, 0, MEM_RELEASE) != 0, "VirtualFree failed");
|
||||||
@@ -797,8 +751,7 @@ void TestFaultDuringUploadRemainsDirty() {
|
|||||||
auto& tracker = harness.tracker;
|
auto& tracker = harness.tracker;
|
||||||
auto& page_manager = harness.page_manager;
|
auto& page_manager = harness.page_manager;
|
||||||
const auto page_size = page_manager.GetPageSize();
|
const auto page_size = page_manager.GetPageSize();
|
||||||
auto *memory = static_cast<uint8_t *>(
|
auto* memory = static_cast<uint8_t*>(VirtualAlloc(reinterpret_cast<void*>(base), page_size,
|
||||||
VirtualAlloc(reinterpret_cast<void *>(base), page_size,
|
|
||||||
MEM_RESERVE | MEM_COMMIT, PAGE_READWRITE));
|
MEM_RESERVE | MEM_COMMIT, PAGE_READWRITE));
|
||||||
Check(memory == reinterpret_cast<void*>(base), "fixed VirtualAlloc failed");
|
Check(memory == reinterpret_cast<void*>(base), "fixed VirtualAlloc failed");
|
||||||
const auto address = reinterpret_cast<uint64_t>(memory);
|
const auto address = reinterpret_cast<uint64_t>(memory);
|
||||||
@@ -807,9 +760,8 @@ void TestFaultDuringUploadRemainsDirty() {
|
|||||||
address, page_size, false, [](uint64_t, uint64_t) noexcept {},
|
address, page_size, false, [](uint64_t, uint64_t) noexcept {},
|
||||||
[&]() noexcept {
|
[&]() noexcept {
|
||||||
bool handled = false;
|
bool handled = false;
|
||||||
std::thread fault([&] {
|
std::thread fault(
|
||||||
handled = page_manager.HandleFault(PageFaultAccess::Write, address);
|
[&] { handled = page_manager.HandleFault(PageFaultAccess::Write, address); });
|
||||||
});
|
|
||||||
fault.join();
|
fault.join();
|
||||||
Check(handled, "concurrent write racing upload was not handled");
|
Check(handled, "concurrent write racing upload was not handled");
|
||||||
});
|
});
|
||||||
@@ -825,8 +777,7 @@ void TestNativeStoreDuringRangeEnumeration() {
|
|||||||
auto& tracker = harness.tracker;
|
auto& tracker = harness.tracker;
|
||||||
auto& page_manager = harness.page_manager;
|
auto& page_manager = harness.page_manager;
|
||||||
const auto page_size = page_manager.GetPageSize();
|
const auto page_size = page_manager.GetPageSize();
|
||||||
auto *memory = static_cast<uint8_t *>(
|
auto* memory = static_cast<uint8_t*>(VirtualAlloc(reinterpret_cast<void*>(base), page_size,
|
||||||
VirtualAlloc(reinterpret_cast<void *>(base), page_size,
|
|
||||||
MEM_RESERVE | MEM_COMMIT, PAGE_READWRITE));
|
MEM_RESERVE | MEM_COMMIT, PAGE_READWRITE));
|
||||||
Check(memory == reinterpret_cast<void*>(base), "fixed VirtualAlloc failed");
|
Check(memory == reinterpret_cast<void*>(base), "fixed VirtualAlloc failed");
|
||||||
const auto address = reinterpret_cast<uint64_t>(memory);
|
const auto address = reinterpret_cast<uint64_t>(memory);
|
||||||
@@ -834,8 +785,7 @@ void TestNativeStoreDuringRangeEnumeration() {
|
|||||||
|
|
||||||
void* handler = AddVectoredExceptionHandler(1, NativeTrackerFaultHandler);
|
void* handler = AddVectoredExceptionHandler(1, NativeTrackerFaultHandler);
|
||||||
Check(handler != nullptr, "AddVectoredExceptionHandler failed");
|
Check(handler != nullptr, "AddVectoredExceptionHandler failed");
|
||||||
Check(g_native_page_manager.exchange(&page_manager, std::memory_order_acq_rel) ==
|
Check(g_native_page_manager.exchange(&page_manager, std::memory_order_acq_rel) == nullptr,
|
||||||
nullptr,
|
|
||||||
"native page manager already installed");
|
"native page manager already installed");
|
||||||
g_native_fault_entered.store(false, std::memory_order_release);
|
g_native_fault_entered.store(false, std::memory_order_release);
|
||||||
|
|
||||||
@@ -843,19 +793,16 @@ void TestNativeStoreDuringRangeEnumeration() {
|
|||||||
tracker.ForEachUploadRange(
|
tracker.ForEachUploadRange(
|
||||||
address, page_size, false,
|
address, page_size, false,
|
||||||
[&](uint64_t, uint64_t) noexcept {
|
[&](uint64_t, uint64_t) noexcept {
|
||||||
writer = std::thread(
|
writer = std::thread([&] { *static_cast<volatile uint8_t*>(memory) = 0x6b; });
|
||||||
[&] { *static_cast<volatile uint8_t *>(memory) = 0x6b; });
|
|
||||||
while (!g_native_fault_entered.load(std::memory_order_acquire)) {
|
while (!g_native_fault_entered.load(std::memory_order_acquire)) {
|
||||||
std::this_thread::yield();
|
std::this_thread::yield();
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
[&]() noexcept { writer.join(); });
|
[&]() noexcept { writer.join(); });
|
||||||
|
|
||||||
Check(g_native_page_manager.exchange(nullptr, std::memory_order_acq_rel) ==
|
Check(g_native_page_manager.exchange(nullptr, std::memory_order_acq_rel) == &page_manager,
|
||||||
&page_manager,
|
|
||||||
"native page manager publication changed");
|
"native page manager publication changed");
|
||||||
Check(RemoveVectoredExceptionHandler(handler) != 0,
|
Check(RemoveVectoredExceptionHandler(handler) != 0, "RemoveVectoredExceptionHandler failed");
|
||||||
"RemoveVectoredExceptionHandler failed");
|
|
||||||
Check(memory[0] == 0x6b && tracker.IsRegionCpuModified(address, page_size) &&
|
Check(memory[0] == 0x6b && tracker.IsRegionCpuModified(address, page_size) &&
|
||||||
IsWritable(memory),
|
IsWritable(memory),
|
||||||
"native store during range enumeration was lost");
|
"native store during range enumeration was lost");
|
||||||
@@ -875,17 +822,15 @@ void TestFaultDuringDownloadSynchronization() {
|
|||||||
const auto address = reinterpret_cast<uint64_t>(memory);
|
const auto address = reinterpret_cast<uint64_t>(memory);
|
||||||
page_manager.OnGpuMap(address, page_size * 3);
|
page_manager.OnGpuMap(address, page_size * 3);
|
||||||
tracker.ForEachUploadRange(
|
tracker.ForEachUploadRange(
|
||||||
address, page_size, true, [](uint64_t, uint64_t) noexcept {},
|
address, page_size, true, [](uint64_t, uint64_t) noexcept {}, []() noexcept {});
|
||||||
[]() noexcept {});
|
|
||||||
tracker.ForEachUploadRange(
|
tracker.ForEachUploadRange(
|
||||||
address + page_size * 2, page_size, true,
|
address + page_size * 2, page_size, true, [](uint64_t, uint64_t) noexcept {},
|
||||||
[](uint64_t, uint64_t) noexcept {}, []() noexcept {});
|
[]() noexcept {});
|
||||||
memory[page_size] = 0x31;
|
memory[page_size] = 0x31;
|
||||||
|
|
||||||
void* handler = AddVectoredExceptionHandler(1, NativeTrackerFaultHandler);
|
void* handler = AddVectoredExceptionHandler(1, NativeTrackerFaultHandler);
|
||||||
Check(handler != nullptr, "AddVectoredExceptionHandler failed");
|
Check(handler != nullptr, "AddVectoredExceptionHandler failed");
|
||||||
Check(g_native_page_manager.exchange(&page_manager, std::memory_order_acq_rel) ==
|
Check(g_native_page_manager.exchange(&page_manager, std::memory_order_acq_rel) == nullptr,
|
||||||
nullptr,
|
|
||||||
"native page manager already installed");
|
"native page manager already installed");
|
||||||
g_native_fault_entered.store(false, std::memory_order_release);
|
g_native_fault_entered.store(false, std::memory_order_release);
|
||||||
|
|
||||||
@@ -894,27 +839,24 @@ void TestFaultDuringDownloadSynchronization() {
|
|||||||
std::thread writer;
|
std::thread writer;
|
||||||
{
|
{
|
||||||
PageManager::BackingWrite first(page_manager, address, page_size);
|
PageManager::BackingWrite first(page_manager, address, page_size);
|
||||||
PageManager::BackingWrite third(page_manager, address + page_size * 2,
|
PageManager::BackingWrite third(page_manager, address + page_size * 2, page_size);
|
||||||
page_size);
|
|
||||||
tracker.ForEachDownloadRange<true>(
|
tracker.ForEachDownloadRange<true>(
|
||||||
address, page_size * 3,
|
address, page_size * 3,
|
||||||
[&](uint64_t download_address, uint64_t download_size) noexcept {
|
[&](uint64_t download_address, uint64_t download_size) noexcept {
|
||||||
Check((download_address == address ||
|
Check(
|
||||||
download_address == address + page_size * 2) &&
|
(download_address == address || download_address == address + page_size * 2) &&
|
||||||
download_size == page_size,
|
download_size == page_size,
|
||||||
"download transaction reported the wrong range");
|
"download transaction reported the wrong range");
|
||||||
ranges++;
|
ranges++;
|
||||||
if (download_address == address) {
|
if (download_address == address) {
|
||||||
writer = std::thread(
|
writer = std::thread([&] { *static_cast<volatile uint8_t*>(memory) = 0x7c; });
|
||||||
[&] { *static_cast<volatile uint8_t *>(memory) = 0x7c; });
|
|
||||||
while (!g_native_fault_entered.load(std::memory_order_acquire)) {
|
while (!g_native_fault_entered.load(std::memory_order_acquire)) {
|
||||||
std::this_thread::yield();
|
std::this_thread::yield();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
std::memcpy(shared.backing + download_address - address,
|
std::memcpy(shared.backing + download_address - address, download.data(),
|
||||||
download.data(), download_size);
|
download_size);
|
||||||
Check(Protection(reinterpret_cast<void *>(download_address)) ==
|
Check(Protection(reinterpret_cast<void*>(download_address)) == PAGE_NOACCESS,
|
||||||
PAGE_NOACCESS,
|
|
||||||
"backing alias exposed the protected guest page");
|
"backing alias exposed the protected guest page");
|
||||||
Check(Protection(memory + page_size) == PAGE_READWRITE,
|
Check(Protection(memory + page_size) == PAGE_READWRITE,
|
||||||
"clean middle page was reserved or protected");
|
"clean middle page was reserved or protected");
|
||||||
@@ -926,14 +868,12 @@ void TestFaultDuringDownloadSynchronization() {
|
|||||||
}
|
}
|
||||||
writer.join();
|
writer.join();
|
||||||
|
|
||||||
Check(g_native_page_manager.exchange(nullptr, std::memory_order_acq_rel) ==
|
Check(g_native_page_manager.exchange(nullptr, std::memory_order_acq_rel) == &page_manager,
|
||||||
&page_manager,
|
|
||||||
"native page manager publication changed");
|
"native page manager publication changed");
|
||||||
Check(RemoveVectoredExceptionHandler(handler) != 0,
|
Check(RemoveVectoredExceptionHandler(handler) != 0, "RemoveVectoredExceptionHandler failed");
|
||||||
"RemoveVectoredExceptionHandler failed");
|
|
||||||
Check(ranges == 2, "partial download did not enumerate both dirty ranges");
|
Check(ranges == 2, "partial download did not enumerate both dirty ranges");
|
||||||
Check(memory[0] == 0x7c && memory[1] == 0x5a &&
|
Check(memory[0] == 0x7c && memory[1] == 0x5a && memory[page_size] == 0x31 &&
|
||||||
memory[page_size] == 0x31 && memory[page_size * 2] == 0x5a,
|
memory[page_size * 2] == 0x5a,
|
||||||
"reserved backing write raced or lost downloaded data");
|
"reserved backing write raced or lost downloaded data");
|
||||||
Check(!tracker.IsRegionGpuModified(address, page_size * 3) &&
|
Check(!tracker.IsRegionGpuModified(address, page_size * 3) &&
|
||||||
tracker.IsRegionCpuModified(address, page_size * 3),
|
tracker.IsRegionCpuModified(address, page_size * 3),
|
||||||
@@ -953,16 +893,14 @@ void TestFaultAndExplicitDirtyRace() {
|
|||||||
auto& tracker = harness.tracker;
|
auto& tracker = harness.tracker;
|
||||||
auto& page_manager = harness.page_manager;
|
auto& page_manager = harness.page_manager;
|
||||||
const auto page_size = page_manager.GetPageSize();
|
const auto page_size = page_manager.GetPageSize();
|
||||||
auto *memory = static_cast<uint8_t *>(
|
auto* memory = static_cast<uint8_t*>(VirtualAlloc(reinterpret_cast<void*>(base), page_size,
|
||||||
VirtualAlloc(reinterpret_cast<void *>(base), page_size,
|
|
||||||
MEM_RESERVE | MEM_COMMIT, PAGE_READWRITE));
|
MEM_RESERVE | MEM_COMMIT, PAGE_READWRITE));
|
||||||
Check(memory == reinterpret_cast<void*>(base), "fixed VirtualAlloc failed");
|
Check(memory == reinterpret_cast<void*>(base), "fixed VirtualAlloc failed");
|
||||||
const auto address = reinterpret_cast<uint64_t>(memory);
|
const auto address = reinterpret_cast<uint64_t>(memory);
|
||||||
page_manager.OnGpuMap(address, page_size);
|
page_manager.OnGpuMap(address, page_size);
|
||||||
for (uint32_t iteration = 0; iteration < 64; iteration++) {
|
for (uint32_t iteration = 0; iteration < 64; iteration++) {
|
||||||
tracker.ForEachUploadRange(
|
tracker.ForEachUploadRange(
|
||||||
address, page_size, false, [](uint64_t, uint64_t) noexcept {},
|
address, page_size, false, [](uint64_t, uint64_t) noexcept {}, []() noexcept {});
|
||||||
[]() noexcept {});
|
|
||||||
std::atomic_bool start {false};
|
std::atomic_bool start {false};
|
||||||
bool handled = false;
|
bool handled = false;
|
||||||
std::thread fault([&] {
|
std::thread fault([&] {
|
||||||
@@ -980,8 +918,7 @@ void TestFaultAndExplicitDirtyRace() {
|
|||||||
start.store(true, std::memory_order_release);
|
start.store(true, std::memory_order_release);
|
||||||
fault.join();
|
fault.join();
|
||||||
dirty.join();
|
dirty.join();
|
||||||
Check(handled && tracker.IsRegionCpuModified(address, page_size) &&
|
Check(handled && tracker.IsRegionCpuModified(address, page_size) && IsWritable(memory),
|
||||||
IsWritable(memory),
|
|
||||||
"fault/explicit-dirty race lost dirty state or write access");
|
"fault/explicit-dirty race lost dirty state or write access");
|
||||||
}
|
}
|
||||||
tracker.UntrackMemory(address, page_size);
|
tracker.UntrackMemory(address, page_size);
|
||||||
@@ -992,8 +929,7 @@ void TestSharedTrackersAndConcurrentPageFaults() {
|
|||||||
constexpr uintptr_t base = 0x0000000200010000ull;
|
constexpr uintptr_t base = 0x0000000200010000ull;
|
||||||
SharedTrackerHarness harness;
|
SharedTrackerHarness harness;
|
||||||
const auto page_size = harness.page_manager.GetPageSize();
|
const auto page_size = harness.page_manager.GetPageSize();
|
||||||
auto *memory = static_cast<uint8_t *>(
|
auto* memory = static_cast<uint8_t*>(VirtualAlloc(reinterpret_cast<void*>(base), page_size * 2,
|
||||||
VirtualAlloc(reinterpret_cast<void *>(base), page_size * 2,
|
|
||||||
MEM_RESERVE | MEM_COMMIT, PAGE_READWRITE));
|
MEM_RESERVE | MEM_COMMIT, PAGE_READWRITE));
|
||||||
Check(memory == reinterpret_cast<void*>(base), "fixed VirtualAlloc failed");
|
Check(memory == reinterpret_cast<void*>(base), "fixed VirtualAlloc failed");
|
||||||
const auto address = reinterpret_cast<uint64_t>(memory);
|
const auto address = reinterpret_cast<uint64_t>(memory);
|
||||||
@@ -1001,8 +937,7 @@ void TestSharedTrackersAndConcurrentPageFaults() {
|
|||||||
|
|
||||||
for (auto* tracker: {&harness.first, &harness.second}) {
|
for (auto* tracker: {&harness.first, &harness.second}) {
|
||||||
tracker->ForEachUploadRange(
|
tracker->ForEachUploadRange(
|
||||||
address, page_size * 2, false, [](uint64_t, uint64_t) noexcept {},
|
address, page_size * 2, false, [](uint64_t, uint64_t) noexcept {}, []() noexcept {});
|
||||||
[]() noexcept {});
|
|
||||||
}
|
}
|
||||||
Check(!IsWritable(memory) && !IsWritable(memory + page_size),
|
Check(!IsWritable(memory) && !IsWritable(memory + page_size),
|
||||||
"shared trackers did not arm both pages");
|
"shared trackers did not arm both pages");
|
||||||
@@ -1014,22 +949,20 @@ void TestSharedTrackersAndConcurrentPageFaults() {
|
|||||||
while (!start.load(std::memory_order_acquire)) {
|
while (!start.load(std::memory_order_acquire)) {
|
||||||
std::this_thread::yield();
|
std::this_thread::yield();
|
||||||
}
|
}
|
||||||
first_handled = harness.page_manager.HandleFault(PageFaultAccess::Write,
|
first_handled = harness.page_manager.HandleFault(PageFaultAccess::Write, address + 8);
|
||||||
address + 8);
|
|
||||||
});
|
});
|
||||||
std::thread second_fault([&] {
|
std::thread second_fault([&] {
|
||||||
while (!start.load(std::memory_order_acquire)) {
|
while (!start.load(std::memory_order_acquire)) {
|
||||||
std::this_thread::yield();
|
std::this_thread::yield();
|
||||||
}
|
}
|
||||||
second_handled = harness.page_manager.HandleFault(
|
second_handled =
|
||||||
PageFaultAccess::Write, address + page_size + 8);
|
harness.page_manager.HandleFault(PageFaultAccess::Write, address + page_size + 8);
|
||||||
});
|
});
|
||||||
start.store(true, std::memory_order_release);
|
start.store(true, std::memory_order_release);
|
||||||
first_fault.join();
|
first_fault.join();
|
||||||
second_fault.join();
|
second_fault.join();
|
||||||
|
|
||||||
Check(first_handled && second_handled,
|
Check(first_handled && second_handled, "concurrent faults were not handled by shared trackers");
|
||||||
"concurrent faults were not handled by shared trackers");
|
|
||||||
for (auto* tracker: {&harness.first, &harness.second}) {
|
for (auto* tracker: {&harness.first, &harness.second}) {
|
||||||
Check(tracker->IsRegionCpuModified(address, page_size * 2),
|
Check(tracker->IsRegionCpuModified(address, page_size * 2),
|
||||||
"a shared tracker lost concurrent CPU dirtiness");
|
"a shared tracker lost concurrent CPU dirtiness");
|
||||||
@@ -1046,26 +979,22 @@ void TestGpuDirtyBits() {
|
|||||||
auto& tracker = harness.tracker;
|
auto& tracker = harness.tracker;
|
||||||
auto& page_manager = harness.page_manager;
|
auto& page_manager = harness.page_manager;
|
||||||
const auto page_size = page_manager.GetPageSize();
|
const auto page_size = page_manager.GetPageSize();
|
||||||
auto *memory = static_cast<uint8_t *>(
|
auto* memory = static_cast<uint8_t*>(VirtualAlloc(reinterpret_cast<void*>(base), page_size * 2,
|
||||||
VirtualAlloc(reinterpret_cast<void *>(base), page_size * 2,
|
|
||||||
MEM_RESERVE | MEM_COMMIT, PAGE_READWRITE));
|
MEM_RESERVE | MEM_COMMIT, PAGE_READWRITE));
|
||||||
Check(memory == reinterpret_cast<void*>(base), "fixed VirtualAlloc failed");
|
Check(memory == reinterpret_cast<void*>(base), "fixed VirtualAlloc failed");
|
||||||
const auto address = reinterpret_cast<uint64_t>(memory);
|
const auto address = reinterpret_cast<uint64_t>(memory);
|
||||||
page_manager.OnGpuMap(address, page_size * 2);
|
page_manager.OnGpuMap(address, page_size * 2);
|
||||||
tracker.ForEachUploadRange(
|
tracker.ForEachUploadRange(
|
||||||
address, page_size, true, [](uint64_t, uint64_t) noexcept {},
|
address, page_size, true, [](uint64_t, uint64_t) noexcept {}, []() noexcept {});
|
||||||
[]() noexcept {});
|
|
||||||
Check(tracker.IsRegionGpuModified(address, page_size) &&
|
Check(tracker.IsRegionGpuModified(address, page_size) &&
|
||||||
!tracker.IsRegionGpuModified(address + page_size, page_size) &&
|
!tracker.IsRegionGpuModified(address + page_size, page_size) &&
|
||||||
Protection(memory) == PAGE_NOACCESS,
|
Protection(memory) == PAGE_NOACCESS,
|
||||||
"GPU dirty state escaped the requested range");
|
"GPU dirty state escaped the requested range");
|
||||||
tracker.UnmarkRegionAsGpuModified(address, page_size);
|
tracker.UnmarkRegionAsGpuModified(address, page_size);
|
||||||
Check(!tracker.IsRegionGpuModified(address, page_size) &&
|
Check(!tracker.IsRegionGpuModified(address, page_size) && Protection(memory) == PAGE_READONLY,
|
||||||
Protection(memory) == PAGE_READONLY,
|
|
||||||
"GPU dirty state did not restore write-only tracking");
|
"GPU dirty state did not restore write-only tracking");
|
||||||
tracker.MarkRegionAsGpuModified(address, page_size);
|
tracker.MarkRegionAsGpuModified(address, page_size);
|
||||||
Check(tracker.IsRegionGpuModified(address, page_size) &&
|
Check(tracker.IsRegionGpuModified(address, page_size) && Protection(memory) == PAGE_NOACCESS,
|
||||||
Protection(memory) == PAGE_NOACCESS,
|
|
||||||
"explicit GPU dirty transition did not trap CPU access");
|
"explicit GPU dirty transition did not trap CPU access");
|
||||||
tracker.UnmarkRegionAsGpuModified(address, page_size);
|
tracker.UnmarkRegionAsGpuModified(address, page_size);
|
||||||
tracker.MarkRegionAsCpuModified(address, page_size);
|
tracker.MarkRegionAsCpuModified(address, page_size);
|
||||||
@@ -1080,26 +1009,24 @@ void TestCrossRegionUpload() {
|
|||||||
auto& tracker = harness.tracker;
|
auto& tracker = harness.tracker;
|
||||||
auto& page_manager = harness.page_manager;
|
auto& page_manager = harness.page_manager;
|
||||||
const auto page_size = page_manager.GetPageSize();
|
const auto page_size = page_manager.GetPageSize();
|
||||||
auto *memory = static_cast<uint8_t *>(
|
auto* memory = static_cast<uint8_t*>(VirtualAlloc(
|
||||||
VirtualAlloc(reinterpret_cast<void *>(base), region_size * 2,
|
reinterpret_cast<void*>(base), region_size * 2, MEM_RESERVE | MEM_COMMIT, PAGE_READWRITE));
|
||||||
MEM_RESERVE | MEM_COMMIT, PAGE_READWRITE));
|
|
||||||
Check(memory == reinterpret_cast<void*>(base), "fixed VirtualAlloc failed");
|
Check(memory == reinterpret_cast<void*>(base), "fixed VirtualAlloc failed");
|
||||||
const auto address = reinterpret_cast<uint64_t>(memory);
|
const auto address = reinterpret_cast<uint64_t>(memory);
|
||||||
const auto boundary = (address + region_size - 1) & ~(region_size - 1);
|
const auto boundary = (address + region_size - 1) & ~(region_size - 1);
|
||||||
page_manager.OnGpuMap(address, region_size * 2);
|
page_manager.OnGpuMap(address, region_size * 2);
|
||||||
uint32_t ranges = 0;
|
uint32_t ranges = 0;
|
||||||
tracker.ForEachUploadRange(
|
tracker.ForEachUploadRange(
|
||||||
boundary - page_size, page_size * 2, false,
|
boundary - page_size, page_size * 2, false, [&](uint64_t, uint64_t) noexcept { ranges++; },
|
||||||
[&](uint64_t, uint64_t) noexcept { ranges++; }, []() noexcept {});
|
[]() noexcept {});
|
||||||
Check(ranges == 2 &&
|
Check(ranges == 2 && !tracker.IsRegionCpuModified(boundary - page_size, page_size * 2) &&
|
||||||
!tracker.IsRegionCpuModified(boundary - page_size, page_size * 2) &&
|
|
||||||
!IsWritable(reinterpret_cast<void*>(boundary - page_size)) &&
|
!IsWritable(reinterpret_cast<void*>(boundary - page_size)) &&
|
||||||
!IsWritable(reinterpret_cast<void*>(boundary)),
|
!IsWritable(reinterpret_cast<void*>(boundary)),
|
||||||
"cross-region upload did not clear and protect both regions");
|
"cross-region upload did not clear and protect both regions");
|
||||||
tracker.MarkRegionAsCpuModified(boundary - page_size, page_size * 2);
|
tracker.MarkRegionAsCpuModified(boundary - page_size, page_size * 2);
|
||||||
tracker.ForEachUploadRange(
|
tracker.ForEachUploadRange(
|
||||||
boundary - page_size, page_size * 2, true,
|
boundary - page_size, page_size * 2, true, [](uint64_t, uint64_t) noexcept {},
|
||||||
[](uint64_t, uint64_t) noexcept {}, []() noexcept {});
|
[]() noexcept {});
|
||||||
Check(tracker.IsRegionGpuModified(boundary - page_size, page_size * 2),
|
Check(tracker.IsRegionGpuModified(boundary - page_size, page_size * 2),
|
||||||
"cross-region written upload did not mark GPU dirty state");
|
"cross-region written upload did not mark GPU dirty state");
|
||||||
tracker.UnmarkRegionAsGpuModified(boundary - page_size, page_size * 2);
|
tracker.UnmarkRegionAsGpuModified(boundary - page_size, page_size * 2);
|
||||||
@@ -1115,27 +1042,22 @@ void TestCrossRegionUpload() {
|
|||||||
auto& page_manager = harness.page_manager;
|
auto& page_manager = harness.page_manager;
|
||||||
const auto page_size = page_manager.GetPageSize();
|
const auto page_size = page_manager.GetPageSize();
|
||||||
const auto allocation_size =
|
const auto allocation_size =
|
||||||
std::strcmp(name, "missing-download-bytes") == 0 ? page_size * 2
|
std::strcmp(name, "missing-download-bytes") == 0 ? page_size * 2 : page_size;
|
||||||
: page_size;
|
auto* memory = static_cast<uint8_t*>(VirtualAlloc(
|
||||||
auto *memory = static_cast<uint8_t *>(
|
reinterpret_cast<void*>(base), allocation_size, MEM_RESERVE | MEM_COMMIT, PAGE_READWRITE));
|
||||||
VirtualAlloc(reinterpret_cast<void *>(base), allocation_size,
|
|
||||||
MEM_RESERVE | MEM_COMMIT, PAGE_READWRITE));
|
|
||||||
Check(memory == reinterpret_cast<void*>(base), "fixed VirtualAlloc failed");
|
Check(memory == reinterpret_cast<void*>(base), "fixed VirtualAlloc failed");
|
||||||
const auto address = reinterpret_cast<uint64_t>(memory);
|
const auto address = reinterpret_cast<uint64_t>(memory);
|
||||||
page_manager.OnGpuMap(address, allocation_size);
|
page_manager.OnGpuMap(address, allocation_size);
|
||||||
if (std::strcmp(name, "gpu-dirty-fault") == 0 ||
|
if (std::strcmp(name, "gpu-dirty-fault") == 0 || std::strcmp(name, "gpu-dirty-read") == 0 ||
|
||||||
std::strcmp(name, "gpu-dirty-read") == 0 ||
|
|
||||||
std::strcmp(name, "gpu-dirty-explicit-cpu") == 0 ||
|
std::strcmp(name, "gpu-dirty-explicit-cpu") == 0 ||
|
||||||
std::strcmp(name, "virtual-gpu-read") == 0) {
|
std::strcmp(name, "virtual-gpu-read") == 0) {
|
||||||
harness.discard_virtual = std::strcmp(name, "virtual-gpu-read") == 0;
|
harness.discard_virtual = std::strcmp(name, "virtual-gpu-read") == 0;
|
||||||
tracker.ForEachUploadRange(
|
tracker.ForEachUploadRange(
|
||||||
address, page_size, true, [](uint64_t, uint64_t) noexcept {},
|
address, page_size, true, [](uint64_t, uint64_t) noexcept {}, []() noexcept {});
|
||||||
[]() noexcept {});
|
|
||||||
if (std::strcmp(name, "gpu-dirty-explicit-cpu") == 0) {
|
if (std::strcmp(name, "gpu-dirty-explicit-cpu") == 0) {
|
||||||
tracker.MarkRegionAsCpuModified(address, page_size);
|
tracker.MarkRegionAsCpuModified(address, page_size);
|
||||||
} else {
|
} else {
|
||||||
(void)page_manager.HandleFault(
|
(void)page_manager.HandleFault((std::strcmp(name, "gpu-dirty-read") == 0 ||
|
||||||
(std::strcmp(name, "gpu-dirty-read") == 0 ||
|
|
||||||
std::strcmp(name, "virtual-gpu-read") == 0)
|
std::strcmp(name, "virtual-gpu-read") == 0)
|
||||||
? PageFaultAccess::Read
|
? PageFaultAccess::Read
|
||||||
: PageFaultAccess::Write,
|
: PageFaultAccess::Write,
|
||||||
@@ -1144,9 +1066,7 @@ void TestCrossRegionUpload() {
|
|||||||
} else if (std::strcmp(name, "reentrant-upload") == 0) {
|
} else if (std::strcmp(name, "reentrant-upload") == 0) {
|
||||||
tracker.ForEachUploadRange(
|
tracker.ForEachUploadRange(
|
||||||
address, page_size, true, [](uint64_t, uint64_t) noexcept {},
|
address, page_size, true, [](uint64_t, uint64_t) noexcept {},
|
||||||
[&]() noexcept {
|
[&]() noexcept { (void)tracker.IsRegionCpuModified(address, page_size); });
|
||||||
(void)tracker.IsRegionCpuModified(address, page_size);
|
|
||||||
});
|
|
||||||
} else if (std::strcmp(name, "writable-upload-race") == 0) {
|
} else if (std::strcmp(name, "writable-upload-race") == 0) {
|
||||||
std::atomic_bool start {false};
|
std::atomic_bool start {false};
|
||||||
std::atomic_bool entered {false};
|
std::atomic_bool entered {false};
|
||||||
@@ -1168,8 +1088,7 @@ void TestCrossRegionUpload() {
|
|||||||
fault.join();
|
fault.join();
|
||||||
} else if (std::strcmp(name, "missing-download-bytes") == 0) {
|
} else if (std::strcmp(name, "missing-download-bytes") == 0) {
|
||||||
tracker.ForEachUploadRange(
|
tracker.ForEachUploadRange(
|
||||||
address, allocation_size, true, [](uint64_t, uint64_t) noexcept {},
|
address, allocation_size, true, [](uint64_t, uint64_t) noexcept {}, []() noexcept {});
|
||||||
[]() noexcept {});
|
|
||||||
RangeSet dirty_bytes;
|
RangeSet dirty_bytes;
|
||||||
dirty_bytes.Add(address, 1);
|
dirty_bytes.Add(address, 1);
|
||||||
PageManager::BackingWrite backing(page_manager, address, page_size);
|
PageManager::BackingWrite backing(page_manager, address, page_size);
|
||||||
@@ -1191,29 +1110,25 @@ void TestCrossRegionUpload() {
|
|||||||
void TestFatalPaths() {
|
void TestFatalPaths() {
|
||||||
#if KYTY_PLATFORM == KYTY_PLATFORM_WINDOWS
|
#if KYTY_PLATFORM == KYTY_PLATFORM_WINDOWS
|
||||||
char path[MAX_PATH] {};
|
char path[MAX_PATH] {};
|
||||||
Check(GetModuleFileNameA(nullptr, path, MAX_PATH) != 0,
|
Check(GetModuleFileNameA(nullptr, path, MAX_PATH) != 0, "GetModuleFileName failed");
|
||||||
"GetModuleFileName failed");
|
|
||||||
#endif
|
#endif
|
||||||
for (const char *name : {"gpu-dirty-fault", "gpu-dirty-read", "virtual-gpu-read",
|
for (const char* name:
|
||||||
"gpu-dirty-explicit-cpu",
|
{"gpu-dirty-fault", "gpu-dirty-read", "virtual-gpu-read", "gpu-dirty-explicit-cpu",
|
||||||
"reentrant-upload", "writable-upload-race",
|
"reentrant-upload", "writable-upload-race", "missing-download-bytes"}) {
|
||||||
"missing-download-bytes"}) {
|
|
||||||
#if KYTY_PLATFORM == KYTY_PLATFORM_WINDOWS
|
#if KYTY_PLATFORM == KYTY_PLATFORM_WINDOWS
|
||||||
std::string command = std::string("\"") + path + "\" --death " + name;
|
std::string command = std::string("\"") + path + "\" --death " + name;
|
||||||
std::vector<char> mutable_command(command.begin(), command.end());
|
std::vector<char> mutable_command(command.begin(), command.end());
|
||||||
mutable_command.push_back('\0');
|
mutable_command.push_back('\0');
|
||||||
STARTUPINFOA startup {sizeof(startup)};
|
STARTUPINFOA startup {sizeof(startup)};
|
||||||
PROCESS_INFORMATION process {};
|
PROCESS_INFORMATION process {};
|
||||||
Check(CreateProcessA(nullptr, mutable_command.data(), nullptr, nullptr,
|
Check(CreateProcessA(nullptr, mutable_command.data(), nullptr, nullptr, FALSE,
|
||||||
FALSE, CREATE_NO_WINDOW, nullptr, nullptr, &startup,
|
CREATE_NO_WINDOW, nullptr, nullptr, &startup, &process) != 0,
|
||||||
&process) != 0,
|
|
||||||
"CreateProcess failed");
|
"CreateProcess failed");
|
||||||
Check(WaitForSingleObject(process.hProcess, 10000) == WAIT_OBJECT_0,
|
Check(WaitForSingleObject(process.hProcess, 10000) == WAIT_OBJECT_0,
|
||||||
"MemoryTracker death test timed out");
|
"MemoryTracker death test timed out");
|
||||||
DWORD exit_code = 0;
|
DWORD exit_code = 0;
|
||||||
Check(GetExitCodeProcess(process.hProcess, &exit_code) != 0 &&
|
Check(GetExitCodeProcess(process.hProcess, &exit_code) != 0 &&
|
||||||
(exit_code == 321 ||
|
(exit_code == 321 || exit_code == EXCEPTION_NONCONTINUABLE_EXCEPTION),
|
||||||
exit_code == EXCEPTION_NONCONTINUABLE_EXCEPTION),
|
|
||||||
"MemoryTracker death path used the wrong exit");
|
"MemoryTracker death path used the wrong exit");
|
||||||
CloseHandle(process.hThread);
|
CloseHandle(process.hThread);
|
||||||
CloseHandle(process.hProcess);
|
CloseHandle(process.hProcess);
|
||||||
@@ -1229,8 +1144,7 @@ void TestFatalPaths() {
|
|||||||
// Exit status carries only the low 8 bits.
|
// Exit status carries only the low 8 bits.
|
||||||
const bool fatal_exit = WIFEXITED(status) && (WEXITSTATUS(status) == (321 & 0xff) ||
|
const bool fatal_exit = WIFEXITED(status) && (WEXITSTATUS(status) == (321 & 0xff) ||
|
||||||
WEXITSTATUS(status) == (322 & 0xff));
|
WEXITSTATUS(status) == (322 & 0xff));
|
||||||
Check(fatal_exit || WIFSIGNALED(status),
|
Check(fatal_exit || WIFSIGNALED(status), "MemoryTracker death path used the wrong exit");
|
||||||
"MemoryTracker death path used the wrong exit");
|
|
||||||
#endif
|
#endif
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
+57
-105
@@ -1,5 +1,5 @@
|
|||||||
#include "graphics/host_gpu/pageManager.h"
|
|
||||||
#include "common/virtualMemory.h"
|
#include "common/virtualMemory.h"
|
||||||
|
#include "graphics/host_gpu/pageManager.h"
|
||||||
|
|
||||||
#include <atomic>
|
#include <atomic>
|
||||||
#include <cstdint>
|
#include <cstdint>
|
||||||
@@ -49,12 +49,9 @@ constexpr uint32_t MEM_RELEASE = 0;
|
|||||||
|
|
||||||
int ToHostProt(uint32_t protection) {
|
int ToHostProt(uint32_t protection) {
|
||||||
switch (protection) {
|
switch (protection) {
|
||||||
case PAGE_NOACCESS:
|
case PAGE_NOACCESS: return PROT_NONE;
|
||||||
return PROT_NONE;
|
case PAGE_READONLY: return PROT_READ;
|
||||||
case PAGE_READONLY:
|
default: return PROT_READ | PROT_WRITE;
|
||||||
return PROT_READ;
|
|
||||||
default:
|
|
||||||
return PROT_READ | PROT_WRITE;
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -82,7 +79,9 @@ uint32_t Protection(const void *address) {
|
|||||||
return result;
|
return result;
|
||||||
}
|
}
|
||||||
|
|
||||||
bool IsWritable(const void *address) { return Protection(address) == PAGE_READWRITE; }
|
bool IsWritable(const void* address) {
|
||||||
|
return Protection(address) == PAGE_READWRITE;
|
||||||
|
}
|
||||||
|
|
||||||
// munmap needs the length that VirtualFree's callers pass as 0, so sizes are remembered here.
|
// munmap needs the length that VirtualFree's callers pass as 0, so sizes are remembered here.
|
||||||
std::map<void*, size_t>& AllocationSizes() {
|
std::map<void*, size_t>& AllocationSizes() {
|
||||||
@@ -125,8 +124,7 @@ uint32_t Protection(const void *address) {
|
|||||||
|
|
||||||
std::atomic_uint64_t g_protection_calls {0};
|
std::atomic_uint64_t g_protection_calls {0};
|
||||||
|
|
||||||
bool ProtectAddressSpace(uint64_t vaddr, uint64_t size,
|
bool ProtectAddressSpace(uint64_t vaddr, uint64_t size, Common::VirtualMemory::Mode mode) {
|
||||||
Common::VirtualMemory::Mode mode) {
|
|
||||||
uint32_t protection = PAGE_NOACCESS;
|
uint32_t protection = PAGE_NOACCESS;
|
||||||
if (mode == Common::VirtualMemory::Mode::Read) {
|
if (mode == Common::VirtualMemory::Mode::Read) {
|
||||||
protection = PAGE_READONLY;
|
protection = PAGE_READONLY;
|
||||||
@@ -135,8 +133,7 @@ bool ProtectAddressSpace(uint64_t vaddr, uint64_t size,
|
|||||||
}
|
}
|
||||||
DWORD old_protection = 0;
|
DWORD old_protection = 0;
|
||||||
g_protection_calls.fetch_add(1, std::memory_order_relaxed);
|
g_protection_calls.fetch_add(1, std::memory_order_relaxed);
|
||||||
return VirtualProtect(reinterpret_cast<void *>(vaddr), size, protection,
|
return VirtualProtect(reinterpret_cast<void*>(vaddr), size, protection, &old_protection) != 0;
|
||||||
&old_protection) != 0;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
#if 1
|
#if 1
|
||||||
@@ -178,8 +175,7 @@ LONG CALLBACK NativeFaultHandler(EXCEPTION_POINTERS *exception) {
|
|||||||
std::this_thread::yield();
|
std::this_thread::yield();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
return manager->HandleFault(
|
return manager->HandleFault(access, exception->ExceptionRecord->ExceptionInformation[1])
|
||||||
access, exception->ExceptionRecord->ExceptionInformation[1])
|
|
||||||
? EXCEPTION_CONTINUE_EXECUTION
|
? EXCEPTION_CONTINUE_EXECUTION
|
||||||
: EXCEPTION_CONTINUE_SEARCH;
|
: EXCEPTION_CONTINUE_SEARCH;
|
||||||
}
|
}
|
||||||
@@ -237,8 +233,7 @@ bool InvalidateFault(void *context, Libs::Graphics::PageFaultAccess, uint64_t va
|
|||||||
}
|
}
|
||||||
fault->calls.fetch_add(1, std::memory_order_relaxed);
|
fault->calls.fetch_add(1, std::memory_order_relaxed);
|
||||||
if (fault->reenter) {
|
if (fault->reenter) {
|
||||||
const auto address =
|
const auto address = fault->reenter_address != 0 ? fault->reenter_address : vaddr;
|
||||||
fault->reenter_address != 0 ? fault->reenter_address : vaddr;
|
|
||||||
(void)fault->manager->HandleFault(PageFaultAccess::Write, address);
|
(void)fault->manager->HandleFault(PageFaultAccess::Write, address);
|
||||||
}
|
}
|
||||||
if (fault->block) {
|
if (fault->block) {
|
||||||
@@ -254,11 +249,9 @@ bool InvalidateFault(void *context, Libs::Graphics::PageFaultAccess, uint64_t va
|
|||||||
uint8_t* Allocate(uint64_t size, uint32_t protection = PAGE_READWRITE) {
|
uint8_t* Allocate(uint64_t size, uint32_t protection = PAGE_READWRITE) {
|
||||||
constexpr uintptr_t test_address = 0x0000000200010000ull;
|
constexpr uintptr_t test_address = 0x0000000200010000ull;
|
||||||
#if KYTY_PLATFORM == KYTY_PLATFORM_WINDOWS
|
#if KYTY_PLATFORM == KYTY_PLATFORM_WINDOWS
|
||||||
auto *memory = static_cast<uint8_t *>(
|
auto* memory = static_cast<uint8_t*>(VirtualAlloc(reinterpret_cast<void*>(test_address), size,
|
||||||
VirtualAlloc(reinterpret_cast<void *>(test_address), size,
|
|
||||||
MEM_RESERVE | MEM_COMMIT, protection));
|
MEM_RESERVE | MEM_COMMIT, protection));
|
||||||
Check(memory == reinterpret_cast<void *>(test_address),
|
Check(memory == reinterpret_cast<void*>(test_address), "fixed low VirtualAlloc failed");
|
||||||
"fixed low VirtualAlloc failed");
|
|
||||||
#else
|
#else
|
||||||
// Do not overwrite a leaked mapping from an earlier case.
|
// Do not overwrite a leaked mapping from an earlier case.
|
||||||
void* raw = ::mmap(reinterpret_cast<void*>(test_address), size, ToHostProt(protection),
|
void* raw = ::mmap(reinterpret_cast<void*>(test_address), size, ToHostProt(protection),
|
||||||
@@ -279,24 +272,18 @@ void TestWatchFaultAndUnwatch() {
|
|||||||
auto* memory = Allocate(page_size * 2);
|
auto* memory = Allocate(page_size * 2);
|
||||||
|
|
||||||
manager.OnGpuMap(reinterpret_cast<uint64_t>(memory), page_size * 2);
|
manager.OnGpuMap(reinterpret_cast<uint64_t>(memory), page_size * 2);
|
||||||
manager.UpdatePageWatchers(true, reinterpret_cast<uint64_t>(memory),
|
manager.UpdatePageWatchers(true, reinterpret_cast<uint64_t>(memory), page_size);
|
||||||
page_size);
|
Check(manager.IsTracked(reinterpret_cast<uint64_t>(memory)) && !IsWritable(memory),
|
||||||
Check(manager.IsTracked(reinterpret_cast<uint64_t>(memory)) &&
|
|
||||||
!IsWritable(memory),
|
|
||||||
"watch did not protect the page");
|
"watch did not protect the page");
|
||||||
Check(g_protection_calls.load(std::memory_order_relaxed) != 0,
|
Check(g_protection_calls.load(std::memory_order_relaxed) != 0,
|
||||||
"watch protection bypassed the address-space owner callback");
|
"watch protection bypassed the address-space owner callback");
|
||||||
Check(manager.HandleFault(PageFaultAccess::Write,
|
Check(manager.HandleFault(PageFaultAccess::Write, reinterpret_cast<uint64_t>(memory + 32)),
|
||||||
reinterpret_cast<uint64_t>(memory + 32)),
|
|
||||||
"tracked write fault was not handled");
|
"tracked write fault was not handled");
|
||||||
Check(!manager.IsTracked(reinterpret_cast<uint64_t>(memory)) &&
|
Check(!manager.IsTracked(reinterpret_cast<uint64_t>(memory)) && IsWritable(memory),
|
||||||
IsWritable(memory),
|
|
||||||
"fault invalidation did not remove the watcher");
|
"fault invalidation did not remove the watcher");
|
||||||
Check(manager.HandleFault(PageFaultAccess::Write,
|
Check(manager.HandleFault(PageFaultAccess::Write, reinterpret_cast<uint64_t>(memory)),
|
||||||
reinterpret_cast<uint64_t>(memory)),
|
|
||||||
"single delayed write fault was not coalesced");
|
"single delayed write fault was not coalesced");
|
||||||
Check(manager.HandleFault(PageFaultAccess::Write,
|
Check(manager.HandleFault(PageFaultAccess::Write, reinterpret_cast<uint64_t>(memory)),
|
||||||
reinterpret_cast<uint64_t>(memory)),
|
|
||||||
"second delayed write fault was not coalesced");
|
"second delayed write fault was not coalesced");
|
||||||
manager.OnGpuUnmap(reinterpret_cast<uint64_t>(memory), page_size * 2);
|
manager.OnGpuUnmap(reinterpret_cast<uint64_t>(memory), page_size * 2);
|
||||||
Check(VirtualFree(memory, 0, MEM_RELEASE) != 0, "VirtualFree failed");
|
Check(VirtualFree(memory, 0, MEM_RELEASE) != 0, "VirtualFree failed");
|
||||||
@@ -330,12 +317,9 @@ void TestReadWriteWatcherFault() {
|
|||||||
const auto address = reinterpret_cast<uint64_t>(memory);
|
const auto address = reinterpret_cast<uint64_t>(memory);
|
||||||
|
|
||||||
manager.OnGpuMap(address, page_size);
|
manager.OnGpuMap(address, page_size);
|
||||||
manager.UpdatePageWatchers(true, address, page_size,
|
manager.UpdatePageWatchers(true, address, page_size, Libs::Graphics::PageWatchMode::Write);
|
||||||
Libs::Graphics::PageWatchMode::Write);
|
manager.UpdatePageWatchers(true, address, page_size, Libs::Graphics::PageWatchMode::ReadWrite);
|
||||||
manager.UpdatePageWatchers(true, address, page_size,
|
manager.UpdatePageWatchers(false, address, page_size, Libs::Graphics::PageWatchMode::Write);
|
||||||
Libs::Graphics::PageWatchMode::ReadWrite);
|
|
||||||
manager.UpdatePageWatchers(false, address, page_size,
|
|
||||||
Libs::Graphics::PageWatchMode::Write);
|
|
||||||
Check(Protection(memory) == PAGE_NOACCESS,
|
Check(Protection(memory) == PAGE_NOACCESS,
|
||||||
"read/write watcher did not install no-access protection");
|
"read/write watcher did not install no-access protection");
|
||||||
Check(manager.HandleFault(PageFaultAccess::Read, address + 8),
|
Check(manager.HandleFault(PageFaultAccess::Read, address + 8),
|
||||||
@@ -377,10 +361,8 @@ void TestPartialMappingUnmapPreservesTokens() {
|
|||||||
|
|
||||||
manager.OnGpuMap(address, page_size);
|
manager.OnGpuMap(address, page_size);
|
||||||
manager.OnGpuMap(address + 8, 16);
|
manager.OnGpuMap(address + 8, 16);
|
||||||
manager.UpdatePageWatchers(true, address, page_size,
|
manager.UpdatePageWatchers(true, address, page_size, Libs::Graphics::PageWatchMode::ReadWrite);
|
||||||
Libs::Graphics::PageWatchMode::ReadWrite);
|
manager.UpdatePageWatchers(false, address, page_size, Libs::Graphics::PageWatchMode::ReadWrite);
|
||||||
manager.UpdatePageWatchers(false, address, page_size,
|
|
||||||
Libs::Graphics::PageWatchMode::ReadWrite);
|
|
||||||
manager.OnGpuUnmap(address + 8, 16);
|
manager.OnGpuUnmap(address + 8, 16);
|
||||||
Check(manager.HandleFault(PageFaultAccess::Read, address),
|
Check(manager.HandleFault(PageFaultAccess::Read, address),
|
||||||
"partial mapping unmap erased delayed read ownership");
|
"partial mapping unmap erased delayed read ownership");
|
||||||
@@ -401,39 +383,34 @@ void TestNativeDelayedReadAfterModeDowngrade() {
|
|||||||
|
|
||||||
manager.OnGpuMap(address, page_size);
|
manager.OnGpuMap(address, page_size);
|
||||||
manager.UpdatePageWatchers(true, address, page_size);
|
manager.UpdatePageWatchers(true, address, page_size);
|
||||||
manager.UpdatePageWatchers(true, address, page_size,
|
manager.UpdatePageWatchers(true, address, page_size, Libs::Graphics::PageWatchMode::ReadWrite);
|
||||||
Libs::Graphics::PageWatchMode::ReadWrite);
|
|
||||||
manager.UpdatePageWatchers(false, address, page_size);
|
manager.UpdatePageWatchers(false, address, page_size);
|
||||||
Check(Protection(memory) == PAGE_NOACCESS,
|
Check(Protection(memory) == PAGE_NOACCESS,
|
||||||
"read/write ownership did not install no-access protection");
|
"read/write ownership did not install no-access protection");
|
||||||
|
|
||||||
void* handler = AddVectoredExceptionHandler(1, NativeFaultHandler);
|
void* handler = AddVectoredExceptionHandler(1, NativeFaultHandler);
|
||||||
Check(handler != nullptr, "AddVectoredExceptionHandler failed");
|
Check(handler != nullptr, "AddVectoredExceptionHandler failed");
|
||||||
Check(g_native_fault_manager.exchange(&manager, std::memory_order_acq_rel) ==
|
Check(g_native_fault_manager.exchange(&manager, std::memory_order_acq_rel) == nullptr,
|
||||||
nullptr,
|
|
||||||
"native fault manager already installed");
|
"native fault manager already installed");
|
||||||
g_native_fault_entered.store(false, std::memory_order_release);
|
g_native_fault_entered.store(false, std::memory_order_release);
|
||||||
g_release_native_fault.store(false, std::memory_order_release);
|
g_release_native_fault.store(false, std::memory_order_release);
|
||||||
g_delay_native_fault.store(true, std::memory_order_release);
|
g_delay_native_fault.store(true, std::memory_order_release);
|
||||||
|
|
||||||
uint8_t value = 0;
|
uint8_t value = 0;
|
||||||
std::thread reader(
|
std::thread reader([&] { value = *static_cast<volatile uint8_t*>(memory); });
|
||||||
[&] { value = *static_cast<volatile uint8_t *>(memory); });
|
|
||||||
while (!g_native_fault_entered.load(std::memory_order_acquire)) {
|
while (!g_native_fault_entered.load(std::memory_order_acquire)) {
|
||||||
std::this_thread::yield();
|
std::this_thread::yield();
|
||||||
}
|
}
|
||||||
manager.UpdatePageWatchers(true, address, page_size);
|
manager.UpdatePageWatchers(true, address, page_size);
|
||||||
manager.UpdatePageWatchers(false, address, page_size,
|
manager.UpdatePageWatchers(false, address, page_size, Libs::Graphics::PageWatchMode::ReadWrite);
|
||||||
Libs::Graphics::PageWatchMode::ReadWrite);
|
|
||||||
Check(Protection(memory) == PAGE_READONLY,
|
Check(Protection(memory) == PAGE_READONLY,
|
||||||
"mode downgrade did not restore readable protection");
|
"mode downgrade did not restore readable protection");
|
||||||
manager.UpdatePageWatchers(true, address, page_size);
|
manager.UpdatePageWatchers(true, address, page_size);
|
||||||
|
|
||||||
context.block = true;
|
context.block = true;
|
||||||
bool write_handled = false;
|
bool write_handled = false;
|
||||||
std::thread writer([&] {
|
std::thread writer(
|
||||||
write_handled = manager.HandleFault(PageFaultAccess::Write, address);
|
[&] { write_handled = manager.HandleFault(PageFaultAccess::Write, address); });
|
||||||
});
|
|
||||||
while (!context.entered.load(std::memory_order_acquire)) {
|
while (!context.entered.load(std::memory_order_acquire)) {
|
||||||
std::this_thread::yield();
|
std::this_thread::yield();
|
||||||
}
|
}
|
||||||
@@ -443,13 +420,10 @@ void TestNativeDelayedReadAfterModeDowngrade() {
|
|||||||
writer.join();
|
writer.join();
|
||||||
|
|
||||||
g_delay_native_fault.store(false, std::memory_order_release);
|
g_delay_native_fault.store(false, std::memory_order_release);
|
||||||
Check(g_native_fault_manager.exchange(nullptr, std::memory_order_acq_rel) ==
|
Check(g_native_fault_manager.exchange(nullptr, std::memory_order_acq_rel) == &manager,
|
||||||
&manager,
|
|
||||||
"native fault manager publication changed");
|
"native fault manager publication changed");
|
||||||
Check(RemoveVectoredExceptionHandler(handler) != 0,
|
Check(RemoveVectoredExceptionHandler(handler) != 0, "RemoveVectoredExceptionHandler failed");
|
||||||
"RemoveVectoredExceptionHandler failed");
|
Check(value == 0x6d && write_handled && context.calls.load(std::memory_order_relaxed) == 1,
|
||||||
Check(value == 0x6d && write_handled &&
|
|
||||||
context.calls.load(std::memory_order_relaxed) == 1,
|
|
||||||
"delayed read was not coalesced across write ownership/resolution");
|
"delayed read was not coalesced across write ownership/resolution");
|
||||||
|
|
||||||
manager.OnGpuUnmap(address, page_size);
|
manager.OnGpuUnmap(address, page_size);
|
||||||
@@ -491,15 +465,12 @@ void TestNativeAccessViolation() {
|
|||||||
|
|
||||||
void* handler = AddVectoredExceptionHandler(1, NativeFaultHandler);
|
void* handler = AddVectoredExceptionHandler(1, NativeFaultHandler);
|
||||||
Check(handler != nullptr, "AddVectoredExceptionHandler failed");
|
Check(handler != nullptr, "AddVectoredExceptionHandler failed");
|
||||||
Check(g_native_fault_manager.exchange(&manager, std::memory_order_acq_rel) ==
|
Check(g_native_fault_manager.exchange(&manager, std::memory_order_acq_rel) == nullptr,
|
||||||
nullptr,
|
|
||||||
"native fault manager already installed");
|
"native fault manager already installed");
|
||||||
*static_cast<volatile uint8_t*>(memory) = 0x5a;
|
*static_cast<volatile uint8_t*>(memory) = 0x5a;
|
||||||
Check(g_native_fault_manager.exchange(nullptr, std::memory_order_acq_rel) ==
|
Check(g_native_fault_manager.exchange(nullptr, std::memory_order_acq_rel) == &manager,
|
||||||
&manager,
|
|
||||||
"native fault manager publication changed");
|
"native fault manager publication changed");
|
||||||
Check(RemoveVectoredExceptionHandler(handler) != 0,
|
Check(RemoveVectoredExceptionHandler(handler) != 0, "RemoveVectoredExceptionHandler failed");
|
||||||
"RemoveVectoredExceptionHandler failed");
|
|
||||||
|
|
||||||
Check(memory[0] == 0x5a && !manager.IsTracked(address) &&
|
Check(memory[0] == 0x5a && !manager.IsTracked(address) &&
|
||||||
context.calls.load(std::memory_order_relaxed) == 1,
|
context.calls.load(std::memory_order_relaxed) == 1,
|
||||||
@@ -517,8 +488,7 @@ void TestCrossRegionRange() {
|
|||||||
auto* memory = Allocate(region_size * 2);
|
auto* memory = Allocate(region_size * 2);
|
||||||
const auto base = reinterpret_cast<uint64_t>(memory);
|
const auto base = reinterpret_cast<uint64_t>(memory);
|
||||||
const auto boundary = (base + region_size - 1) & ~(region_size - 1);
|
const auto boundary = (base + region_size - 1) & ~(region_size - 1);
|
||||||
Check(boundary >= base + page_size &&
|
Check(boundary >= base + page_size && boundary + page_size <= base + region_size * 2,
|
||||||
boundary + page_size <= base + region_size * 2,
|
|
||||||
"test allocation does not contain a region boundary");
|
"test allocation does not contain a region boundary");
|
||||||
|
|
||||||
manager.OnGpuMap(base, region_size * 2);
|
manager.OnGpuMap(base, region_size * 2);
|
||||||
@@ -554,8 +524,7 @@ void TestBatchedWatcherRanges() {
|
|||||||
"fragmented watch did not coalesce to read-only");
|
"fragmented watch did not coalesce to read-only");
|
||||||
}
|
}
|
||||||
manager.UpdatePageWatchers(false, address, page_size * 5);
|
manager.UpdatePageWatchers(false, address, page_size * 5);
|
||||||
Check(IsWritable(memory) &&
|
Check(IsWritable(memory) && Protection(memory + page_size) == PAGE_READONLY &&
|
||||||
Protection(memory + page_size) == PAGE_READONLY &&
|
|
||||||
IsWritable(memory + page_size * 2) &&
|
IsWritable(memory + page_size * 2) &&
|
||||||
Protection(memory + page_size * 3) == PAGE_READONLY &&
|
Protection(memory + page_size * 3) == PAGE_READONLY &&
|
||||||
IsWritable(memory + page_size * 4),
|
IsWritable(memory + page_size * 4),
|
||||||
@@ -564,14 +533,12 @@ void TestBatchedWatcherRanges() {
|
|||||||
manager.UpdatePageWatchers(false, address + page_size * 3, page_size);
|
manager.UpdatePageWatchers(false, address + page_size * 3, page_size);
|
||||||
|
|
||||||
manager.UpdatePageWatchers(true, address, allocation_size);
|
manager.UpdatePageWatchers(true, address, allocation_size);
|
||||||
Check(!IsWritable(memory) &&
|
Check(!IsWritable(memory) && !IsWritable(memory + region_size) &&
|
||||||
!IsWritable(memory + region_size) &&
|
|
||||||
!IsWritable(memory + region_size * 2) &&
|
!IsWritable(memory + region_size * 2) &&
|
||||||
!IsWritable(memory + allocation_size - page_size),
|
!IsWritable(memory + allocation_size - page_size),
|
||||||
"large cross-region watch did not protect the full range");
|
"large cross-region watch did not protect the full range");
|
||||||
manager.UpdatePageWatchers(false, address, allocation_size);
|
manager.UpdatePageWatchers(false, address, allocation_size);
|
||||||
Check(IsWritable(memory) &&
|
Check(IsWritable(memory) && IsWritable(memory + region_size) &&
|
||||||
IsWritable(memory + region_size) &&
|
|
||||||
IsWritable(memory + region_size * 2) &&
|
IsWritable(memory + region_size * 2) &&
|
||||||
IsWritable(memory + allocation_size - page_size),
|
IsWritable(memory + allocation_size - page_size),
|
||||||
"large cross-region unwatch did not restore the full range");
|
"large cross-region unwatch did not restore the full range");
|
||||||
@@ -579,23 +546,20 @@ void TestBatchedWatcherRanges() {
|
|||||||
manager.UpdatePageWatchers(true, address, page_size * 5);
|
manager.UpdatePageWatchers(true, address, page_size * 5);
|
||||||
manager.UpdatePageWatchers(true, address + page_size, page_size * 3,
|
manager.UpdatePageWatchers(true, address + page_size, page_size * 3,
|
||||||
Libs::Graphics::PageWatchMode::ReadWrite);
|
Libs::Graphics::PageWatchMode::ReadWrite);
|
||||||
Check(Protection(memory) == PAGE_READONLY &&
|
Check(Protection(memory) == PAGE_READONLY && Protection(memory + page_size) == PAGE_NOACCESS &&
|
||||||
Protection(memory + page_size) == PAGE_NOACCESS &&
|
|
||||||
Protection(memory + page_size * 2) == PAGE_NOACCESS &&
|
Protection(memory + page_size * 2) == PAGE_NOACCESS &&
|
||||||
Protection(memory + page_size * 3) == PAGE_NOACCESS &&
|
Protection(memory + page_size * 3) == PAGE_NOACCESS &&
|
||||||
Protection(memory + page_size * 4) == PAGE_READONLY,
|
Protection(memory + page_size * 4) == PAGE_READONLY,
|
||||||
"mixed watcher modes installed incorrect protections");
|
"mixed watcher modes installed incorrect protections");
|
||||||
manager.UpdatePageWatchers(false, address, page_size * 5);
|
manager.UpdatePageWatchers(false, address, page_size * 5);
|
||||||
Check(IsWritable(memory) &&
|
Check(IsWritable(memory) && Protection(memory + page_size) == PAGE_NOACCESS &&
|
||||||
Protection(memory + page_size) == PAGE_NOACCESS &&
|
|
||||||
Protection(memory + page_size * 2) == PAGE_NOACCESS &&
|
Protection(memory + page_size * 2) == PAGE_NOACCESS &&
|
||||||
Protection(memory + page_size * 3) == PAGE_NOACCESS &&
|
Protection(memory + page_size * 3) == PAGE_NOACCESS &&
|
||||||
IsWritable(memory + page_size * 4),
|
IsWritable(memory + page_size * 4),
|
||||||
"write unwatch incorrectly released read/write watchers");
|
"write unwatch incorrectly released read/write watchers");
|
||||||
manager.UpdatePageWatchers(false, address + page_size, page_size * 3,
|
manager.UpdatePageWatchers(false, address + page_size, page_size * 3,
|
||||||
Libs::Graphics::PageWatchMode::ReadWrite);
|
Libs::Graphics::PageWatchMode::ReadWrite);
|
||||||
Check(IsWritable(memory + page_size) &&
|
Check(IsWritable(memory + page_size) && IsWritable(memory + page_size * 2) &&
|
||||||
IsWritable(memory + page_size * 2) &&
|
|
||||||
IsWritable(memory + page_size * 3),
|
IsWritable(memory + page_size * 3),
|
||||||
"read/write unwatch did not restore writable protection");
|
"read/write unwatch did not restore writable protection");
|
||||||
|
|
||||||
@@ -637,8 +601,7 @@ void TestBatchedWatcherRanges() {
|
|||||||
(void)manager->HandleFault(PageFaultAccess::Write, address);
|
(void)manager->HandleFault(PageFaultAccess::Write, address);
|
||||||
} else if (std::strcmp(name, "concurrent-non-write") == 0) {
|
} else if (std::strcmp(name, "concurrent-non-write") == 0) {
|
||||||
context.block = true;
|
context.block = true;
|
||||||
std::thread first(
|
std::thread first([&] { (void)manager->HandleFault(PageFaultAccess::Write, address); });
|
||||||
[&] { (void)manager->HandleFault(PageFaultAccess::Write, address); });
|
|
||||||
while (!context.entered.load(std::memory_order_acquire)) {
|
while (!context.entered.load(std::memory_order_acquire)) {
|
||||||
std::this_thread::yield();
|
std::this_thread::yield();
|
||||||
}
|
}
|
||||||
@@ -654,22 +617,18 @@ void TestBatchedWatcherRanges() {
|
|||||||
void CheckDeathCase(const char* name) {
|
void CheckDeathCase(const char* name) {
|
||||||
#if KYTY_PLATFORM == KYTY_PLATFORM_WINDOWS
|
#if KYTY_PLATFORM == KYTY_PLATFORM_WINDOWS
|
||||||
char path[MAX_PATH] {};
|
char path[MAX_PATH] {};
|
||||||
Check(GetModuleFileNameA(nullptr, path, MAX_PATH) != 0,
|
Check(GetModuleFileNameA(nullptr, path, MAX_PATH) != 0, "GetModuleFileName failed");
|
||||||
"GetModuleFileName failed");
|
|
||||||
std::string command = std::string("\"") + path + "\" --death " + name;
|
std::string command = std::string("\"") + path + "\" --death " + name;
|
||||||
std::vector<char> mutable_command(command.begin(), command.end());
|
std::vector<char> mutable_command(command.begin(), command.end());
|
||||||
mutable_command.push_back('\0');
|
mutable_command.push_back('\0');
|
||||||
STARTUPINFOA startup {sizeof(startup)};
|
STARTUPINFOA startup {sizeof(startup)};
|
||||||
PROCESS_INFORMATION process {};
|
PROCESS_INFORMATION process {};
|
||||||
Check(CreateProcessA(nullptr, mutable_command.data(), nullptr, nullptr, FALSE,
|
Check(CreateProcessA(nullptr, mutable_command.data(), nullptr, nullptr, FALSE, CREATE_NO_WINDOW,
|
||||||
CREATE_NO_WINDOW, nullptr, nullptr, &startup,
|
nullptr, nullptr, &startup, &process) != 0,
|
||||||
&process) != 0,
|
|
||||||
"CreateProcess failed");
|
"CreateProcess failed");
|
||||||
Check(WaitForSingleObject(process.hProcess, 10000) == WAIT_OBJECT_0,
|
Check(WaitForSingleObject(process.hProcess, 10000) == WAIT_OBJECT_0, "death test timed out");
|
||||||
"death test timed out");
|
|
||||||
DWORD exit_code = 0;
|
DWORD exit_code = 0;
|
||||||
Check(
|
Check(GetExitCodeProcess(process.hProcess, &exit_code) != 0 &&
|
||||||
GetExitCodeProcess(process.hProcess, &exit_code) != 0 &&
|
|
||||||
(exit_code == 322 || exit_code == EXCEPTION_NONCONTINUABLE_EXCEPTION),
|
(exit_code == 322 || exit_code == EXCEPTION_NONCONTINUABLE_EXCEPTION),
|
||||||
"death case did not use the PageManager fatal exit");
|
"death case did not use the PageManager fatal exit");
|
||||||
CloseHandle(process.hThread);
|
CloseHandle(process.hThread);
|
||||||
@@ -686,16 +645,14 @@ void CheckDeathCase(const char *name) {
|
|||||||
// Exit status carries only the low 8 bits.
|
// Exit status carries only the low 8 bits.
|
||||||
const bool fatal_exit = WIFEXITED(status) && WEXITSTATUS(status) == (322 & 0xff);
|
const bool fatal_exit = WIFEXITED(status) && WEXITSTATUS(status) == (322 & 0xff);
|
||||||
const bool fatal_signal = WIFSIGNALED(status);
|
const bool fatal_signal = WIFSIGNALED(status);
|
||||||
Check(fatal_exit || fatal_signal,
|
Check(fatal_exit || fatal_signal, "death case did not use the PageManager fatal exit");
|
||||||
"death case did not use the PageManager fatal exit");
|
|
||||||
#endif
|
#endif
|
||||||
}
|
}
|
||||||
|
|
||||||
void TestFatalPaths() {
|
void TestFatalPaths() {
|
||||||
for (const char* name:
|
for (const char* name:
|
||||||
{"invalid-range", "unknown-untrack", "destructor-watch", "non-write",
|
{"invalid-range", "unknown-untrack", "destructor-watch", "non-write", "callback-false",
|
||||||
"callback-false", "reentrant", "cross-reentrant",
|
"reentrant", "cross-reentrant", "concurrent-non-write"}) {
|
||||||
"concurrent-non-write"}) {
|
|
||||||
CheckDeathCase(name);
|
CheckDeathCase(name);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -712,20 +669,16 @@ void TestConcurrentFault() {
|
|||||||
manager.UpdatePageWatchers(true, address, page_size);
|
manager.UpdatePageWatchers(true, address, page_size);
|
||||||
bool first_result = false;
|
bool first_result = false;
|
||||||
bool second_result = false;
|
bool second_result = false;
|
||||||
std::thread first([&] {
|
std::thread first([&] { first_result = manager.HandleFault(PageFaultAccess::Write, address); });
|
||||||
first_result = manager.HandleFault(PageFaultAccess::Write, address);
|
|
||||||
});
|
|
||||||
while (!context.entered.load(std::memory_order_acquire)) {
|
while (!context.entered.load(std::memory_order_acquire)) {
|
||||||
std::this_thread::yield();
|
std::this_thread::yield();
|
||||||
}
|
}
|
||||||
std::thread second([&] {
|
std::thread second(
|
||||||
second_result = manager.HandleFault(PageFaultAccess::Write, address);
|
[&] { second_result = manager.HandleFault(PageFaultAccess::Write, address); });
|
||||||
});
|
|
||||||
context.release.store(true, std::memory_order_release);
|
context.release.store(true, std::memory_order_release);
|
||||||
first.join();
|
first.join();
|
||||||
second.join();
|
second.join();
|
||||||
Check(first_result && second_result &&
|
Check(first_result && second_result && context.calls.load(std::memory_order_relaxed) == 1,
|
||||||
context.calls.load(std::memory_order_relaxed) == 1,
|
|
||||||
"concurrent faults dispatched invalidation more than once");
|
"concurrent faults dispatched invalidation more than once");
|
||||||
manager.OnGpuUnmap(address, page_size);
|
manager.OnGpuUnmap(address, page_size);
|
||||||
Check(VirtualFree(memory, 0, MEM_RELEASE) != 0, "VirtualFree failed");
|
Check(VirtualFree(memory, 0, MEM_RELEASE) != 0, "VirtualFree failed");
|
||||||
@@ -742,8 +695,7 @@ void TestExternalDirtyTransferDuringResolution() {
|
|||||||
manager.OnGpuMap(address, page_size);
|
manager.OnGpuMap(address, page_size);
|
||||||
manager.UpdatePageWatchers(true, address, page_size);
|
manager.UpdatePageWatchers(true, address, page_size);
|
||||||
bool handled = false;
|
bool handled = false;
|
||||||
std::thread fault(
|
std::thread fault([&] { handled = manager.HandleFault(PageFaultAccess::Write, address); });
|
||||||
[&] { handled = manager.HandleFault(PageFaultAccess::Write, address); });
|
|
||||||
while (!context.entered.load(std::memory_order_acquire)) {
|
while (!context.entered.load(std::memory_order_acquire)) {
|
||||||
std::this_thread::yield();
|
std::this_thread::yield();
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -1,9 +1,9 @@
|
|||||||
#include "graphics/host_gpu/renderer/cache/resourceMutex.h"
|
#include "graphics/host_gpu/renderer/cache/resourceMutex.h"
|
||||||
|
|
||||||
#include <atomic>
|
#include <atomic>
|
||||||
#include <cstring>
|
|
||||||
#include <cstdio>
|
#include <cstdio>
|
||||||
#include <cstdlib>
|
#include <cstdlib>
|
||||||
|
#include <cstring>
|
||||||
#include <mutex>
|
#include <mutex>
|
||||||
#include <string>
|
#include <string>
|
||||||
#include <thread>
|
#include <thread>
|
||||||
|
|||||||
+253
-425
File diff suppressed because it is too large
Load Diff
@@ -482,8 +482,8 @@ void TestReadLaneVectorOverwriteInvalidatesSpill() {
|
|||||||
overwrite.dst = Vgpr(11);
|
overwrite.dst = Vgpr(11);
|
||||||
overwrite.src[0] = Imm(0);
|
overwrite.src[0] = Imm(0);
|
||||||
overwrite.src_count = 1;
|
overwrite.src_count = 1;
|
||||||
program.blocks[0].instructions = {WriteLane(0, 11, 4, 0), overwrite,
|
program.blocks[0].instructions = {WriteLane(0, 11, 4, 0), overwrite, ReadLane(8, 0, 11, 0),
|
||||||
ReadLane(8, 0, 11, 0), BufferUse(12, 0)};
|
BufferUse(12, 0)};
|
||||||
|
|
||||||
std::string error;
|
std::string error;
|
||||||
Check(BuildScalarProvenance(program, &error), error.c_str());
|
Check(BuildScalarProvenance(program, &error), error.c_str());
|
||||||
@@ -550,8 +550,8 @@ void TestReadLaneWideAndRelativeWritesInvalidateSpill() {
|
|||||||
overwrite.dst = Vgpr(11);
|
overwrite.dst = Vgpr(11);
|
||||||
overwrite.src[0] = Imm(0);
|
overwrite.src[0] = Imm(0);
|
||||||
overwrite.src_count = 1;
|
overwrite.src_count = 1;
|
||||||
program.blocks[0].instructions = {WriteLane(0, 12, 4, 0), overwrite,
|
program.blocks[0].instructions = {WriteLane(0, 12, 4, 0), overwrite, ReadLane(8, 0, 12, 0),
|
||||||
ReadLane(8, 0, 12, 0), BufferUse(12, 0)};
|
BufferUse(12, 0)};
|
||||||
|
|
||||||
std::string error;
|
std::string error;
|
||||||
Check(BuildScalarProvenance(program, &error), error.c_str());
|
Check(BuildScalarProvenance(program, &error), error.c_str());
|
||||||
@@ -589,8 +589,8 @@ void TestReadLaneModuloAndDynamicLane() {
|
|||||||
write.src[1] = Sgpr(7);
|
write.src[1] = Sgpr(7);
|
||||||
dynamic.blocks[0].instructions = {write, ReadLane(4, 0, 11, 0), BufferUse(8, 0)};
|
dynamic.blocks[0].instructions = {write, ReadLane(4, 0, 11, 0), BufferUse(8, 0)};
|
||||||
Check(BuildScalarProvenance(dynamic, &error), error.c_str());
|
Check(BuildScalarProvenance(dynamic, &error), error.c_str());
|
||||||
Check(!DescriptorSourceResolved(
|
Check(!DescriptorSourceResolved(dynamic,
|
||||||
dynamic, dynamic.blocks[0].instructions.back().memory.resource_source),
|
dynamic.blocks[0].instructions.back().memory.resource_source),
|
||||||
"dynamic writelane selector retained unsafe lane provenance");
|
"dynamic writelane selector retained unsafe lane provenance");
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -599,8 +599,7 @@ void TestReadLaneEliminationSnapshotsWriteValue() {
|
|||||||
program.wave_size = 64;
|
program.wave_size = 64;
|
||||||
program.user_data_count = 8;
|
program.user_data_count = 8;
|
||||||
program.blocks.resize(1);
|
program.blocks.resize(1);
|
||||||
program.blocks[0].instructions = {
|
program.blocks[0].instructions = {MoveImmediate(0, 4, 0x12345678u), WriteLane(4, 11, 4, 4),
|
||||||
MoveImmediate(0, 4, 0x12345678u), WriteLane(4, 11, 4, 4),
|
|
||||||
MoveImmediate(8, 4, 0xdeadbeefu), ReadLane(12, 0, 11, 4)};
|
MoveImmediate(8, 4, 0xdeadbeefu), ReadLane(12, 0, 11, 4)};
|
||||||
|
|
||||||
std::string error;
|
std::string error;
|
||||||
@@ -680,8 +679,7 @@ void TestReadLaneEliminationHonorsVectorInvalidation() {
|
|||||||
overwrite.dst = Vgpr(11);
|
overwrite.dst = Vgpr(11);
|
||||||
overwrite.src[0] = Imm(0);
|
overwrite.src[0] = Imm(0);
|
||||||
overwrite.src_count = 1;
|
overwrite.src_count = 1;
|
||||||
program.blocks[0].instructions = {WriteLane(0, 11, 4, 4), overwrite,
|
program.blocks[0].instructions = {WriteLane(0, 11, 4, 4), overwrite, ReadLane(8, 0, 11, 4)};
|
||||||
ReadLane(8, 0, 11, 4)};
|
|
||||||
|
|
||||||
std::string error;
|
std::string error;
|
||||||
Check(BuildScalarProvenance(program, &error), error.c_str());
|
Check(BuildScalarProvenance(program, &error), error.c_str());
|
||||||
@@ -840,8 +838,7 @@ void TestDynamicReadIsNotFlattened() {
|
|||||||
DescriptorValue descriptor;
|
DescriptorValue descriptor;
|
||||||
const auto source = program.blocks[0].instructions[1].memory.resource_source;
|
const auto source = program.blocks[0].instructions[1].memory.resource_source;
|
||||||
const SrtRuntime runtime {user_data, 0, ReadHostMemory, nullptr};
|
const SrtRuntime runtime {user_data, 0, ReadHostMemory, nullptr};
|
||||||
Check(EvaluateDescriptorSource(program, source, 4, runtime, descriptor, &error),
|
Check(EvaluateDescriptorSource(program, source, 4, runtime, descriptor, &error), error.c_str());
|
||||||
error.c_str());
|
|
||||||
Check(descriptor.dwords[0] == table[1], "dynamic ReadConst evaluated the wrong dword");
|
Check(descriptor.dwords[0] == table[1], "dynamic ReadConst evaluated the wrong dword");
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -1151,39 +1148,36 @@ void TestBitFieldMaskDescriptor() {
|
|||||||
mask.src_count = 2;
|
mask.src_count = 2;
|
||||||
auto high = MoveImmediate(8, 30, 0x05500000u);
|
auto high = MoveImmediate(8, 30, 0x05500000u);
|
||||||
high.op = Opcode::MoveU64;
|
high.op = Opcode::MoveU64;
|
||||||
program.blocks[0].instructions = {MoveImmediate(0, 28, 0x92u), mask, high,
|
program.blocks[0].instructions = {MoveImmediate(0, 28, 0x92u), mask, high, BufferUse(12, 28)};
|
||||||
BufferUse(12, 28)};
|
|
||||||
|
|
||||||
std::string error;
|
std::string error;
|
||||||
Check(BuildScalarProvenance(program, &error) && BuildSrtPlan(program, &error), error.c_str());
|
Check(BuildScalarProvenance(program, &error) && BuildSrtPlan(program, &error), error.c_str());
|
||||||
DescriptorValue descriptor;
|
DescriptorValue descriptor;
|
||||||
const SrtRuntime runtime {{}, 0, nullptr, nullptr};
|
const SrtRuntime runtime {{}, 0, nullptr, nullptr};
|
||||||
Check(EvaluateDescriptorSource(program,
|
Check(EvaluateDescriptorSource(program,
|
||||||
program.blocks[0].instructions.back().memory.resource_source,
|
program.blocks[0].instructions.back().memory.resource_source, 16,
|
||||||
16, runtime, descriptor, &error),
|
runtime, descriptor, &error),
|
||||||
error.c_str());
|
error.c_str());
|
||||||
Check(descriptor.dwords[0] == 0x92u && descriptor.dwords[1] == 0x00fff000u &&
|
Check(descriptor.dwords[0] == 0x92u && descriptor.dwords[1] == 0x00fff000u &&
|
||||||
descriptor.dwords[2] == 0x05500000u && descriptor.dwords[3] == 0,
|
descriptor.dwords[2] == 0x05500000u && descriptor.dwords[3] == 0,
|
||||||
"production sampler bit-field mask evaluated incorrectly");
|
"production sampler bit-field mask evaluated incorrectly");
|
||||||
|
|
||||||
mask.src[0] = Imm(0);
|
mask.src[0] = Imm(0);
|
||||||
program.blocks[0].instructions = {MoveImmediate(0, 28, 0x92u), mask, high,
|
program.blocks[0].instructions = {MoveImmediate(0, 28, 0x92u), mask, high, BufferUse(12, 28)};
|
||||||
BufferUse(12, 28)};
|
|
||||||
Check(BuildScalarProvenance(program, &error) && BuildSrtPlan(program, &error), error.c_str());
|
Check(BuildScalarProvenance(program, &error) && BuildSrtPlan(program, &error), error.c_str());
|
||||||
Check(EvaluateDescriptorSource(program,
|
Check(EvaluateDescriptorSource(program,
|
||||||
program.blocks[0].instructions.back().memory.resource_source,
|
program.blocks[0].instructions.back().memory.resource_source, 12,
|
||||||
12, runtime, descriptor, &error),
|
runtime, descriptor, &error),
|
||||||
error.c_str());
|
error.c_str());
|
||||||
Check(descriptor.dwords[1] == 0, "zero-width bit-field mask was not zero");
|
Check(descriptor.dwords[1] == 0, "zero-width bit-field mask was not zero");
|
||||||
|
|
||||||
mask.src[0] = Imm(31);
|
mask.src[0] = Imm(31);
|
||||||
mask.src[1] = Imm(31);
|
mask.src[1] = Imm(31);
|
||||||
program.blocks[0].instructions = {MoveImmediate(0, 28, 0x92u), mask, high,
|
program.blocks[0].instructions = {MoveImmediate(0, 28, 0x92u), mask, high, BufferUse(12, 28)};
|
||||||
BufferUse(12, 28)};
|
|
||||||
Check(BuildScalarProvenance(program, &error) && BuildSrtPlan(program, &error), error.c_str());
|
Check(BuildScalarProvenance(program, &error) && BuildSrtPlan(program, &error), error.c_str());
|
||||||
Check(EvaluateDescriptorSource(program,
|
Check(EvaluateDescriptorSource(program,
|
||||||
program.blocks[0].instructions.back().memory.resource_source,
|
program.blocks[0].instructions.back().memory.resource_source, 12,
|
||||||
12, runtime, descriptor, &error),
|
runtime, descriptor, &error),
|
||||||
error.c_str());
|
error.c_str());
|
||||||
Check(descriptor.dwords[1] == 0x80000000u,
|
Check(descriptor.dwords[1] == 0x80000000u,
|
||||||
"maximum bit-field mask count/offset evaluated incorrectly");
|
"maximum bit-field mask count/offset evaluated incorrectly");
|
||||||
|
|||||||
+2457
-3836
File diff suppressed because it is too large
Load Diff
@@ -1,7 +1,6 @@
|
|||||||
#include "graphics/shader/shader.h"
|
|
||||||
|
|
||||||
#include "graphics/shader/recompiler/ir/ResourceMaterialization.h"
|
#include "graphics/shader/recompiler/ir/ResourceMaterialization.h"
|
||||||
#include "graphics/shader/recompiler/ir/ShaderIR.h"
|
#include "graphics/shader/recompiler/ir/ShaderIR.h"
|
||||||
|
#include "graphics/shader/shader.h"
|
||||||
|
|
||||||
#include <cstdio>
|
#include <cstdio>
|
||||||
#include <cstdlib>
|
#include <cstdlib>
|
||||||
@@ -61,8 +60,7 @@ void TestMappedSrtUsesDirectReaderByDefault() {
|
|||||||
Check(ShaderMaterializeStageRuntime(cached_program, {}, 0, stage, &error), error.c_str());
|
Check(ShaderMaterializeStageRuntime(cached_program, {}, 0, stage, &error), error.c_str());
|
||||||
Check(stage.program == cached_program && stage.resources != nullptr,
|
Check(stage.program == cached_program && stage.resources != nullptr,
|
||||||
"cache rematerialization did not publish the mapped stage");
|
"cache rematerialization did not publish the mapped stage");
|
||||||
Check(stage.resources->flattened_srt.size() == 1 &&
|
Check(stage.resources->flattened_srt.size() == 1 && stage.resources->flattened_srt[0] == dword,
|
||||||
stage.resources->flattened_srt[0] == dword,
|
|
||||||
"cache rematerialization did not use the direct reader by default");
|
"cache rematerialization did not use the direct reader by default");
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -75,8 +75,8 @@ void TestValidAndInvalidMetadata() {
|
|||||||
CheckRejected(excessive_semantics.mapped, "excessive vertex semantic count was accepted");
|
CheckRejected(excessive_semantics.mapped, "excessive vertex semantic count was accepted");
|
||||||
|
|
||||||
Fixture excessive_register;
|
Fixture excessive_register;
|
||||||
excessive_register.offsets[
|
excessive_register.offsets[static_cast<size_t>(AgcDirectResourceType::PtrVertexBufferTable)] =
|
||||||
static_cast<size_t>(AgcDirectResourceType::PtrVertexBufferTable)] = 63;
|
63;
|
||||||
CheckRejected(excessive_register.mapped, "out-of-domain vertex table SGPR was accepted");
|
CheckRejected(excessive_register.mapped, "out-of-domain vertex table SGPR was accepted");
|
||||||
|
|
||||||
Fixture missing_semantics;
|
Fixture missing_semantics;
|
||||||
|
|||||||
@@ -107,8 +107,7 @@ void InitSubsystems() {
|
|||||||
slist->Add(log, {core, config});
|
slist->Add(log, {core, config});
|
||||||
Check("InitSubsystems", slist->InitAll(false), "failed to initialize logging subsystem");
|
Check("InitSubsystems", slist->InitAll(false), "failed to initialize logging subsystem");
|
||||||
|
|
||||||
const auto param_json =
|
const auto param_json = std::filesystem::temp_directory_path() /
|
||||||
std::filesystem::temp_directory_path() /
|
|
||||||
("kyty_virtual_memory_" +
|
("kyty_virtual_memory_" +
|
||||||
std::to_string(reinterpret_cast<uintptr_t>(&initialized)) + ".json");
|
std::to_string(reinterpret_cast<uintptr_t>(&initialized)) + ".json");
|
||||||
constexpr char json[] = R"({"kernel":{"flexibleMemorySize":3221225472}})";
|
constexpr char json[] = R"({"kernel":{"flexibleMemorySize":3221225472}})";
|
||||||
@@ -247,8 +246,8 @@ void TestGuestAddressSpaceOwnsReservationsBeforeBacking() {
|
|||||||
Check(test, Libs::LibKernel::Memory::TestPlaceholderRangeIsFree(base, SceKernelPageSize),
|
Check(test, Libs::LibKernel::Memory::TestPlaceholderRangeIsFree(base, SceKernelPageSize),
|
||||||
"semantic reservation replaced the owner's placeholder");
|
"semantic reservation replaced the owner's placeholder");
|
||||||
Check(test,
|
Check(test,
|
||||||
Libs::LibKernel::Memory::ProtectGuestHostMemory(
|
Libs::LibKernel::Memory::ProtectGuestHostMemory(base, SceKernelPageSize,
|
||||||
base, SceKernelPageSize, Common::VirtualMemory::Mode::NoAccess),
|
Common::VirtualMemory::Mode::NoAccess),
|
||||||
"owner rejected a sparse placeholder protection no-op");
|
"owner rejected a sparse placeholder protection no-op");
|
||||||
CheckOk(test, Libs::LibKernel::Memory::KernelMunmap(base, SceKernelPageSize), "KernelMunmap");
|
CheckOk(test, Libs::LibKernel::Memory::KernelMunmap(base, SceKernelPageSize), "KernelMunmap");
|
||||||
Check(test, Libs::LibKernel::Memory::TestPlaceholderRangeIsFree(base, SceKernelPageSize),
|
Check(test, Libs::LibKernel::Memory::TestPlaceholderRangeIsFree(base, SceKernelPageSize),
|
||||||
@@ -402,8 +401,7 @@ void TestFlexibleDmemCompatAndAlignmentFlags() {
|
|||||||
Check(test, stack_start == nullptr && stack_end == nullptr,
|
Check(test, stack_start == nullptr && stack_end == nullptr,
|
||||||
"DMEM_COMPAT flexible mapping was reported as a stack");
|
"DMEM_COMPAT flexible mapping was reported as a stack");
|
||||||
|
|
||||||
CheckOk(test, Libs::LibKernel::Memory::KernelMunmap(base, SceKernelPageSize),
|
CheckOk(test, Libs::LibKernel::Memory::KernelMunmap(base, SceKernelPageSize), "KernelMunmap");
|
||||||
"KernelMunmap");
|
|
||||||
Check(test, AvailableFlexibleMemory(test) == baseline,
|
Check(test, AvailableFlexibleMemory(test) == baseline,
|
||||||
"DMEM_COMPAT cleanup did not restore flexible capacity");
|
"DMEM_COMPAT cleanup did not restore flexible capacity");
|
||||||
|
|
||||||
@@ -443,8 +441,8 @@ void TestFlexibleNoCoalescePreservesBoundaries() {
|
|||||||
const auto baseline = AvailableFlexibleMemory(test);
|
const auto baseline = AvailableFlexibleMemory(test);
|
||||||
void* reserve = nullptr;
|
void* reserve = nullptr;
|
||||||
CheckOk(test,
|
CheckOk(test,
|
||||||
Libs::LibKernel::Memory::KernelReserveVirtualRange(
|
Libs::LibKernel::Memory::KernelReserveVirtualRange(&reserve, SceKernelPageSize * 2, 0,
|
||||||
&reserve, SceKernelPageSize * 2, 0, SceKernelPageSize),
|
SceKernelPageSize),
|
||||||
"KernelReserveVirtualRange");
|
"KernelReserveVirtualRange");
|
||||||
const auto base = reinterpret_cast<uint64_t>(reserve);
|
const auto base = reinterpret_cast<uint64_t>(reserve);
|
||||||
|
|
||||||
@@ -601,12 +599,11 @@ void TestRuntimeMemoryOwnerLifecycle() {
|
|||||||
Common::VirtualMemory::Mode::ReadWrite, "runtime_adjacent_second", true);
|
Common::VirtualMemory::Mode::ReadWrite, "runtime_adjacent_second", true);
|
||||||
Check(test, adjacent_second == adjacent_first + SceKernelPageSize,
|
Check(test, adjacent_second == adjacent_first + SceKernelPageSize,
|
||||||
"second adjacent runtime allocation failed");
|
"second adjacent runtime allocation failed");
|
||||||
Check(test,
|
Check(test, Libs::LibKernel::Memory::FreeGuestMemory(adjacent_first, SceKernelPageSize * 2),
|
||||||
Libs::LibKernel::Memory::FreeGuestMemory(adjacent_first, SceKernelPageSize * 2),
|
|
||||||
"combined adjacent runtime free failed");
|
"combined adjacent runtime free failed");
|
||||||
Check(test,
|
Check(
|
||||||
Libs::LibKernel::Memory::TestPlaceholderRangeIsFree(adjacent_first,
|
test,
|
||||||
SceKernelPageSize * 2),
|
Libs::LibKernel::Memory::TestPlaceholderRangeIsFree(adjacent_first, SceKernelPageSize * 2),
|
||||||
"combined adjacent runtime free did not restore one owner placeholder");
|
"combined adjacent runtime free did not restore one owner placeholder");
|
||||||
|
|
||||||
std::printf("[host] %-48s ok\n", test);
|
std::printf("[host] %-48s ok\n", test);
|
||||||
@@ -877,12 +874,12 @@ void TestDirectPartialProtectUnmapPreservesNeighbors() {
|
|||||||
SceKernelPageSize, SceKernelProtCpuRead),
|
SceKernelPageSize, SceKernelProtCpuRead),
|
||||||
"KernelMprotect(middle)");
|
"KernelMprotect(middle)");
|
||||||
Check(test,
|
Check(test,
|
||||||
Libs::LibKernel::Memory::ProtectGuestHostMemory(
|
Libs::LibKernel::Memory::ProtectGuestHostMemory(base, size,
|
||||||
base, size, Common::VirtualMemory::Mode::Read),
|
Common::VirtualMemory::Mode::Read),
|
||||||
"owner could not protect fragmented backing views");
|
"owner could not protect fragmented backing views");
|
||||||
Check(test,
|
Check(test,
|
||||||
Libs::LibKernel::Memory::ProtectGuestHostMemory(
|
Libs::LibKernel::Memory::ProtectGuestHostMemory(base, size,
|
||||||
base, size, Common::VirtualMemory::Mode::ReadWrite),
|
Common::VirtualMemory::Mode::ReadWrite),
|
||||||
"owner could not restore fragmented backing views");
|
"owner could not restore fragmented backing views");
|
||||||
CheckOk(test,
|
CheckOk(test,
|
||||||
Libs::LibKernel::Memory::KernelMunmap(base + SceKernelPageSize, SceKernelPageSize),
|
Libs::LibKernel::Memory::KernelMunmap(base + SceKernelPageSize, SceKernelPageSize),
|
||||||
@@ -1091,12 +1088,12 @@ void TestMunmapAcrossAdjacentFlexibleMappings() {
|
|||||||
Libs::LibKernel::Memory::ClampRangeSize(base + SceKernelPageSize - 0x100, 0x200) == 0x200,
|
Libs::LibKernel::Memory::ClampRangeSize(base + SceKernelPageSize - 0x100, 0x200) == 0x200,
|
||||||
"ClampRangeSize did not cross adjacent committed mappings");
|
"ClampRangeSize did not cross adjacent committed mappings");
|
||||||
Check(test,
|
Check(test,
|
||||||
Libs::LibKernel::Memory::ProtectGuestHostMemory(
|
Libs::LibKernel::Memory::ProtectGuestHostMemory(base, SceKernelPageSize * 2,
|
||||||
base, SceKernelPageSize * 2, Common::VirtualMemory::Mode::Read),
|
Common::VirtualMemory::Mode::Read),
|
||||||
"owner could not protect adjacent backing mappings");
|
"owner could not protect adjacent backing mappings");
|
||||||
Check(test,
|
Check(test,
|
||||||
Libs::LibKernel::Memory::ProtectGuestHostMemory(
|
Libs::LibKernel::Memory::ProtectGuestHostMemory(base, SceKernelPageSize * 2,
|
||||||
base, SceKernelPageSize * 2, Common::VirtualMemory::Mode::ReadWrite),
|
Common::VirtualMemory::Mode::ReadWrite),
|
||||||
"owner could not restore adjacent backing mappings");
|
"owner could not restore adjacent backing mappings");
|
||||||
|
|
||||||
CheckOk(test, Libs::LibKernel::Memory::KernelMunmap(base, SceKernelPageSize * 2),
|
CheckOk(test, Libs::LibKernel::Memory::KernelMunmap(base, SceKernelPageSize * 2),
|
||||||
|
|||||||
+41
-51
@@ -4,22 +4,22 @@
|
|||||||
#include "common/threads.h"
|
#include "common/threads.h"
|
||||||
#include "graphics/guest_gpu/hardwareContext.h"
|
#include "graphics/guest_gpu/hardwareContext.h"
|
||||||
#include "graphics/guest_gpu/pm4.h"
|
#include "graphics/guest_gpu/pm4.h"
|
||||||
#include "graphics/host_gpu/renderer/image/textureCommon.h"
|
|
||||||
#include "graphics/host_gpu/renderer/image/imageView.h"
|
#include "graphics/host_gpu/renderer/image/imageView.h"
|
||||||
|
#include "graphics/host_gpu/renderer/image/textureCommon.h"
|
||||||
#include "graphics/host_gpu/renderer/pipeline/shaderResourceBarrier.h"
|
#include "graphics/host_gpu/renderer/pipeline/shaderResourceBarrier.h"
|
||||||
#include "graphics/host_gpu/renderer/pipeline/shaderSubgroup.h"
|
#include "graphics/host_gpu/renderer/pipeline/shaderSubgroup.h"
|
||||||
#include "graphics/shader/recompiler/ExecMask.h"
|
#include "graphics/shader/recompiler/ExecMask.h"
|
||||||
#include "graphics/shader/recompiler/ir/ResourceTracking.h"
|
#include "graphics/shader/recompiler/ShaderRecompiler.h"
|
||||||
#include "graphics/shader/recompiler/ir/ScalarProvenance.h"
|
|
||||||
#include "graphics/shader/recompiler/cfg/ShaderCFG.h"
|
#include "graphics/shader/recompiler/cfg/ShaderCFG.h"
|
||||||
#include "graphics/shader/recompiler/decompiler/ShaderDecoder.h"
|
#include "graphics/shader/recompiler/decompiler/ShaderDecoder.h"
|
||||||
|
#include "graphics/shader/recompiler/emitter/SpirvEmitter.h"
|
||||||
|
#include "graphics/shader/recompiler/emitter/spirvEmitterInternal.h"
|
||||||
|
#include "graphics/shader/recompiler/ir/ResourceTracking.h"
|
||||||
|
#include "graphics/shader/recompiler/ir/ScalarProvenance.h"
|
||||||
#include "graphics/shader/recompiler/ir/ShaderIR.h"
|
#include "graphics/shader/recompiler/ir/ShaderIR.h"
|
||||||
#include "graphics/shader/recompiler/ir/ShaderInfoCollection.h"
|
#include "graphics/shader/recompiler/ir/ShaderInfoCollection.h"
|
||||||
#include "graphics/shader/recompiler/ShaderRecompiler.h"
|
|
||||||
#include "graphics/shader/recompiler/emitter/SpirvEmitter.h"
|
|
||||||
#include "graphics/shader/recompiler/ir/SrtPatcher.h"
|
#include "graphics/shader/recompiler/ir/SrtPatcher.h"
|
||||||
#include "graphics/shader/recompiler/ir/SrtWalker.h"
|
#include "graphics/shader/recompiler/ir/SrtWalker.h"
|
||||||
#include "graphics/shader/recompiler/emitter/spirvEmitterInternal.h"
|
|
||||||
#include "graphics/shader/shader.h"
|
#include "graphics/shader/shader.h"
|
||||||
#include "libs/agc.h"
|
#include "libs/agc.h"
|
||||||
#include "spirv-tools/libspirv.hpp"
|
#include "spirv-tools/libspirv.hpp"
|
||||||
@@ -411,8 +411,8 @@ constexpr uint32_t EncodeSopp(uint32_t opcode, uint32_t simm = 0) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
void TestNativeShaderResourceDependencies() {
|
void TestNativeShaderResourceDependencies() {
|
||||||
const auto stages = ShaderPipelineStages(
|
const auto stages =
|
||||||
vk::ShaderStageFlagBits::eVertex | vk::ShaderStageFlagBits::eFragment |
|
ShaderPipelineStages(vk::ShaderStageFlagBits::eVertex | vk::ShaderStageFlagBits::eFragment |
|
||||||
vk::ShaderStageFlagBits::eCompute);
|
vk::ShaderStageFlagBits::eCompute);
|
||||||
Check(stages == (vk::PipelineStageFlagBits::eVertexShader |
|
Check(stages == (vk::PipelineStageFlagBits::eVertexShader |
|
||||||
vk::PipelineStageFlagBits::eFragmentShader |
|
vk::PipelineStageFlagBits::eFragmentShader |
|
||||||
@@ -463,8 +463,7 @@ void TestNormalizedImageContracts() {
|
|||||||
ImageInfo container {};
|
ImageInfo container {};
|
||||||
container.data = {0x10000, 0x15000};
|
container.data = {0x10000, 0x15000};
|
||||||
container.pixel_format = vk::Format::eR8G8B8A8Unorm;
|
container.pixel_format = vk::Format::eR8G8B8A8Unorm;
|
||||||
container.guest_format =
|
container.guest_format = Prospero::GpuEnumValue(Prospero::BufferFormat::k8_8_8_8UNorm);
|
||||||
Prospero::GpuEnumValue(Prospero::BufferFormat::k8_8_8_8UNorm);
|
|
||||||
container.type = Prospero::ImageType::kColor2D;
|
container.type = Prospero::ImageType::kColor2D;
|
||||||
container.extent = {64, 64, 1};
|
container.extent = {64, 64, 1};
|
||||||
container.resources = {3, 4};
|
container.resources = {3, 4};
|
||||||
@@ -488,8 +487,7 @@ void TestNormalizedImageContracts() {
|
|||||||
"normalized image block extent changed");
|
"normalized image block extent changed");
|
||||||
Check(subresource.IsCompatible(container), "normalized compatible image was rejected");
|
Check(subresource.IsCompatible(container), "normalized compatible image was rejected");
|
||||||
Check(subresource.MipOf(container) == 1, "normalized mip lookup missed a subresource");
|
Check(subresource.MipOf(container) == 1, "normalized mip lookup missed a subresource");
|
||||||
Check(subresource.SliceOf(container, 1) == 2,
|
Check(subresource.SliceOf(container, 1) == 2, "normalized slice lookup missed a subresource");
|
||||||
"normalized slice lookup missed a subresource");
|
|
||||||
|
|
||||||
auto incompatible = subresource;
|
auto incompatible = subresource;
|
||||||
incompatible.samples = 2;
|
incompatible.samples = 2;
|
||||||
@@ -497,17 +495,14 @@ void TestNormalizedImageContracts() {
|
|||||||
"sample-count mismatch was accepted as a compatible image");
|
"sample-count mismatch was accepted as a compatible image");
|
||||||
|
|
||||||
auto compressed = container;
|
auto compressed = container;
|
||||||
compressed.guest_format =
|
compressed.guest_format = Prospero::GpuEnumValue(Prospero::BufferFormat::kBc3UNorm);
|
||||||
Prospero::GpuEnumValue(Prospero::BufferFormat::kBc3UNorm);
|
|
||||||
compressed.pitch = 128;
|
compressed.pitch = 128;
|
||||||
compressed.extent.height = 64;
|
compressed.extent.height = 64;
|
||||||
Check(compressed.BlockExtent() == vk::Extent2D {32, 16},
|
Check(compressed.BlockExtent() == vk::Extent2D {32, 16},
|
||||||
"block-compressed extent was not expressed in blocks");
|
"block-compressed extent was not expressed in blocks");
|
||||||
|
|
||||||
Check(ImageViewOps::FormatsCompatible(vk::Format::eR8G8B8A8Unorm,
|
Check(ImageViewOps::FormatsCompatible(vk::Format::eR8G8B8A8Unorm, vk::Format::eR8G8B8A8Uint) &&
|
||||||
vk::Format::eR8G8B8A8Uint) &&
|
!ImageViewOps::FormatsCompatible(vk::Format::eD32Sfloat, vk::Format::eR32Sfloat) &&
|
||||||
!ImageViewOps::FormatsCompatible(vk::Format::eD32Sfloat,
|
|
||||||
vk::Format::eR32Sfloat) &&
|
|
||||||
ImageViewOps::FormatsCompatible(vk::Format::eBc3UnormBlock,
|
ImageViewOps::FormatsCompatible(vk::Format::eBc3UnormBlock,
|
||||||
vk::Format::eR32G32B32A32Uint),
|
vk::Format::eR32G32B32A32Uint),
|
||||||
"Vulkan image-view compatibility classes diverged from production");
|
"Vulkan image-view compatibility classes diverged from production");
|
||||||
@@ -524,20 +519,19 @@ void TestNativeSubgroupPolicy() {
|
|||||||
safe.wave_size = 32;
|
safe.wave_size = 32;
|
||||||
safe.lane_mask_mode = ShaderLaneMaskMode::NativeWave;
|
safe.lane_mask_mode = ShaderLaneMaskMode::NativeWave;
|
||||||
Check(ConfigureShaderSubgroup(ShaderSubgroupCapabilities {context},
|
Check(ConfigureShaderSubgroup(ShaderSubgroupCapabilities {context},
|
||||||
vk::ShaderStageFlagBits::eVertex, safe).mode ==
|
vk::ShaderStageFlagBits::eVertex, safe)
|
||||||
ShaderSubgroupMode::Natural,
|
.mode == ShaderSubgroupMode::Natural,
|
||||||
"native wave32 policy changed");
|
"native wave32 policy changed");
|
||||||
safe.wave_size = 64;
|
safe.wave_size = 64;
|
||||||
Check(SelectGraphicsLaneMaskMode(safe.wave_size) ==
|
Check(SelectGraphicsLaneMaskMode(safe.wave_size) == ShaderLaneMaskMode::PerInvocation &&
|
||||||
ShaderLaneMaskMode::PerInvocation &&
|
|
||||||
ConfigureShaderSubgroup(ShaderSubgroupCapabilities {context},
|
ConfigureShaderSubgroup(ShaderSubgroupCapabilities {context},
|
||||||
vk::ShaderStageFlagBits::eVertex, safe).mode ==
|
vk::ShaderStageFlagBits::eVertex, safe)
|
||||||
ShaderSubgroupMode::Unsupported,
|
.mode == ShaderSubgroupMode::Unsupported,
|
||||||
"wave64 graphics mismatch accepted native-wave mask lowering");
|
"wave64 graphics mismatch accepted native-wave mask lowering");
|
||||||
safe.lane_mask_mode = ShaderLaneMaskMode::PerInvocation;
|
safe.lane_mask_mode = ShaderLaneMaskMode::PerInvocation;
|
||||||
Check(ConfigureShaderSubgroup(ShaderSubgroupCapabilities {context},
|
Check(ConfigureShaderSubgroup(ShaderSubgroupCapabilities {context},
|
||||||
vk::ShaderStageFlagBits::eVertex, safe).mode ==
|
vk::ShaderStageFlagBits::eVertex, safe)
|
||||||
ShaderSubgroupMode::PerInvocationGraphics,
|
.mode == ShaderSubgroupMode::PerInvocationGraphics,
|
||||||
"wave64 graphics mismatch did not select per-invocation masks");
|
"wave64 graphics mismatch did not select per-invocation masks");
|
||||||
|
|
||||||
ShaderRecompiler::IR::Program cross_lane = safe;
|
ShaderRecompiler::IR::Program cross_lane = safe;
|
||||||
@@ -545,14 +539,14 @@ void TestNativeSubgroupPolicy() {
|
|||||||
ShaderRecompiler::IR::Opcode::ReadLaneU32;
|
ShaderRecompiler::IR::Opcode::ReadLaneU32;
|
||||||
Check(ShaderRecompiler::Spirv::ProgramRequiresExactSubgroupSize(cross_lane) &&
|
Check(ShaderRecompiler::Spirv::ProgramRequiresExactSubgroupSize(cross_lane) &&
|
||||||
ConfigureShaderSubgroup(ShaderSubgroupCapabilities {context},
|
ConfigureShaderSubgroup(ShaderSubgroupCapabilities {context},
|
||||||
vk::ShaderStageFlagBits::eVertex, cross_lane).mode ==
|
vk::ShaderStageFlagBits::eVertex, cross_lane)
|
||||||
ShaderSubgroupMode::PerInvocationGraphics,
|
.mode == ShaderSubgroupMode::PerInvocationGraphics,
|
||||||
"graphics mismatch did not select per-invocation masks");
|
"graphics mismatch did not select per-invocation masks");
|
||||||
auto cross_lane_compute = cross_lane;
|
auto cross_lane_compute = cross_lane;
|
||||||
cross_lane_compute.lane_mask_mode = ShaderLaneMaskMode::NativeWave;
|
cross_lane_compute.lane_mask_mode = ShaderLaneMaskMode::NativeWave;
|
||||||
Check(ConfigureShaderSubgroup(ShaderSubgroupCapabilities {context},
|
Check(ConfigureShaderSubgroup(ShaderSubgroupCapabilities {context},
|
||||||
vk::ShaderStageFlagBits::eCompute, cross_lane_compute).mode ==
|
vk::ShaderStageFlagBits::eCompute, cross_lane_compute)
|
||||||
ShaderSubgroupMode::Unsupported,
|
.mode == ShaderSubgroupMode::Unsupported,
|
||||||
"cross-lane compute mismatch bypassed the exact subgroup requirement");
|
"cross-lane compute mismatch bypassed the exact subgroup requirement");
|
||||||
|
|
||||||
ShaderRecompiler::IR::Program zero_exec = safe;
|
ShaderRecompiler::IR::Program zero_exec = safe;
|
||||||
@@ -568,8 +562,8 @@ void TestNativeSubgroupPolicy() {
|
|||||||
zero_bfm.scalar_sources[0] = 2;
|
zero_bfm.scalar_sources[0] = 2;
|
||||||
Check(!ShaderRecompiler::Spirv::ProgramRequiresExactSubgroupSize(zero_exec) &&
|
Check(!ShaderRecompiler::Spirv::ProgramRequiresExactSubgroupSize(zero_exec) &&
|
||||||
ConfigureShaderSubgroup(ShaderSubgroupCapabilities {context},
|
ConfigureShaderSubgroup(ShaderSubgroupCapabilities {context},
|
||||||
vk::ShaderStageFlagBits::eCompute, zero_exec).mode ==
|
vk::ShaderStageFlagBits::eCompute, zero_exec)
|
||||||
ShaderSubgroupMode::FlattenedMasks,
|
.mode == ShaderSubgroupMode::FlattenedMasks,
|
||||||
"compile-time uniform-zero EXEC write did not stay on the mask-free path");
|
"compile-time uniform-zero EXEC write did not stay on the mask-free path");
|
||||||
|
|
||||||
ShaderRecompiler::IR::Program selective_exec = safe;
|
ShaderRecompiler::IR::Program selective_exec = safe;
|
||||||
@@ -600,8 +594,8 @@ void TestNativeSubgroupPolicy() {
|
|||||||
ds_partial.blocks.emplace_back().instructions.emplace_back().op =
|
ds_partial.blocks.emplace_back().instructions.emplace_back().op =
|
||||||
ShaderRecompiler::IR::Opcode::DsAppend;
|
ShaderRecompiler::IR::Opcode::DsAppend;
|
||||||
Check(ConfigureShaderSubgroup(ShaderSubgroupCapabilities {context},
|
Check(ConfigureShaderSubgroup(ShaderSubgroupCapabilities {context},
|
||||||
vk::ShaderStageFlagBits::eCompute, ds_partial).mode ==
|
vk::ShaderStageFlagBits::eCompute, ds_partial)
|
||||||
ShaderSubgroupMode::Unsupported,
|
.mode == ShaderSubgroupMode::Unsupported,
|
||||||
"partial wave64 DS append bypassed the exact subgroup requirement");
|
"partial wave64 DS append bypassed the exact subgroup requirement");
|
||||||
context.max_subgroup_size = 64;
|
context.max_subgroup_size = 64;
|
||||||
const auto controlled =
|
const auto controlled =
|
||||||
@@ -614,17 +608,18 @@ void TestNativeSubgroupPolicy() {
|
|||||||
cross_lane.wave_size = 32;
|
cross_lane.wave_size = 32;
|
||||||
cross_lane.lane_mask_mode = ShaderLaneMaskMode::PerInvocation;
|
cross_lane.lane_mask_mode = ShaderLaneMaskMode::PerInvocation;
|
||||||
Check(ConfigureShaderSubgroup(ShaderSubgroupCapabilities {context},
|
Check(ConfigureShaderSubgroup(ShaderSubgroupCapabilities {context},
|
||||||
vk::ShaderStageFlagBits::eFragment, cross_lane).mode ==
|
vk::ShaderStageFlagBits::eFragment, cross_lane)
|
||||||
ShaderSubgroupMode::Unsupported,
|
.mode == ShaderSubgroupMode::Unsupported,
|
||||||
"inverse graphics mismatch was accepted as one guest wave");
|
"inverse graphics mismatch was accepted as one guest wave");
|
||||||
cross_lane_compute.wave_size = 32;
|
cross_lane_compute.wave_size = 32;
|
||||||
Check(ConfigureShaderSubgroup(ShaderSubgroupCapabilities {context},
|
Check(ConfigureShaderSubgroup(ShaderSubgroupCapabilities {context},
|
||||||
vk::ShaderStageFlagBits::eCompute, cross_lane_compute).mode ==
|
vk::ShaderStageFlagBits::eCompute, cross_lane_compute)
|
||||||
ShaderSubgroupMode::Unsupported,
|
.mode == ShaderSubgroupMode::Unsupported,
|
||||||
"inverse cross-lane compute mismatch was accepted");
|
"inverse cross-lane compute mismatch was accepted");
|
||||||
}
|
}
|
||||||
|
|
||||||
std::array<uint32_t, 64> ImageTestUserData(Prospero::ImageType type = Prospero::ImageType::kColor2D) {
|
std::array<uint32_t, 64>
|
||||||
|
ImageTestUserData(Prospero::ImageType type = Prospero::ImageType::kColor2D) {
|
||||||
std::array<uint32_t, 64> data {};
|
std::array<uint32_t, 64> data {};
|
||||||
for (uint32_t start = 0; start + 3u < data.size(); start += 4u) {
|
for (uint32_t start = 0; start + 3u < data.size(); start += 4u) {
|
||||||
data[start] = 0x1000u + start * 0x100u;
|
data[start] = 0x1000u + start * 0x100u;
|
||||||
@@ -3525,10 +3520,8 @@ void TestNewShaderRecompilerImageViewDimensions() {
|
|||||||
"SPIR-V binary does not contain sampled 2D-array image type");
|
"SPIR-V binary does not contain sampled 2D-array image type");
|
||||||
Check(SpirvContainsTypeImage(result.spirv, SpirvDim3D, 0, 1),
|
Check(SpirvContainsTypeImage(result.spirv, SpirvDim3D, 0, 1),
|
||||||
"SPIR-V binary does not contain sampled 3D image type");
|
"SPIR-V binary does not contain sampled 3D image type");
|
||||||
Check(SpirvContainsCapability(result.spirv, 43),
|
Check(SpirvContainsCapability(result.spirv, 43), "SPIR-V binary does not request Sampled1D");
|
||||||
"SPIR-V binary does not request Sampled1D");
|
Check(SpirvContainsCapability(result.spirv, 44), "SPIR-V binary does not request Image1D");
|
||||||
Check(SpirvContainsCapability(result.spirv, 44),
|
|
||||||
"SPIR-V binary does not request Image1D");
|
|
||||||
Check(SpirvContainsOpcode(result.spirv, 95),
|
Check(SpirvContainsOpcode(result.spirv, 95),
|
||||||
"SPIR-V binary does not contain array image fetch");
|
"SPIR-V binary does not contain array image fetch");
|
||||||
CheckSpirvBinaryValidates(result.spirv);
|
CheckSpirvBinaryValidates(result.spirv);
|
||||||
@@ -4116,8 +4109,7 @@ void TestNewShaderRecompilerVintrpLowering() {
|
|||||||
options.pixel_input_info = &flat_ps_info;
|
options.pixel_input_info = &flat_ps_info;
|
||||||
|
|
||||||
ShaderRecompiler::CompileResult flat_result;
|
ShaderRecompiler::CompileResult flat_result;
|
||||||
Check(ShaderRecompiler::TryRecompile(flat_shader, options, flat_result, &error),
|
Check(ShaderRecompiler::TryRecompile(flat_shader, options, flat_result, &error), error.c_str());
|
||||||
error.c_str());
|
|
||||||
Check(SpirvHasDecorationValueWithDecoration(flat_result.spirv, 30u, 0u, 14u),
|
Check(SpirvHasDecorationValueWithDecoration(flat_result.spirv, 30u, 0u, 14u),
|
||||||
"flat VINTRP input did not emit a Flat decoration");
|
"flat VINTRP input did not emit a Flat decoration");
|
||||||
Check(!SpirvHasDecorationValueWithDecoration(flat_result.spirv, 30u, 0u, 13u),
|
Check(!SpirvHasDecorationValueWithDecoration(flat_result.spirv, 30u, 0u, 13u),
|
||||||
@@ -6027,8 +6019,8 @@ void TestRenderTargetReverseFloat16ExportMapping() {
|
|||||||
format.export_mapping.ApplyMask(0xfu) == 0xfu,
|
format.export_mapping.ApplyMask(0xfu) == 0xfu,
|
||||||
"reverse RGBA16F render-target export or write-mask mapping is "
|
"reverse RGBA16F render-target export or write-mask mapping is "
|
||||||
"incorrect");
|
"incorrect");
|
||||||
const auto legacy_alt = TextureGetRenderTargetFormat(
|
const auto legacy_alt =
|
||||||
Prospero::GpuEnumValue(Prospero::ChannelLayout::k8_8_8_8),
|
TextureGetRenderTargetFormat(Prospero::GpuEnumValue(Prospero::ChannelLayout::k8_8_8_8),
|
||||||
Prospero::GpuEnumValue(Prospero::ChannelType::kUNorm),
|
Prospero::GpuEnumValue(Prospero::ChannelType::kUNorm),
|
||||||
Prospero::GpuEnumValue(Prospero::ChannelOrder::kAlt));
|
Prospero::GpuEnumValue(Prospero::ChannelOrder::kAlt));
|
||||||
Check(legacy_alt.format == vk::Format::eB8G8R8A8Unorm && legacy_alt.export_mapping.IsIdentity(),
|
Check(legacy_alt.format == vk::Format::eB8G8R8A8Unorm && legacy_alt.export_mapping.IsIdentity(),
|
||||||
@@ -6060,8 +6052,7 @@ void TestRenderTargetReverseFloat16ExportMapping() {
|
|||||||
CheckSpirvBinaryValidates(reversed_result.spirv);
|
CheckSpirvBinaryValidates(reversed_result.spirv);
|
||||||
|
|
||||||
HW::PixelShaderInfo regs {};
|
HW::PixelShaderInfo regs {};
|
||||||
Check(ShaderGetIdPS(regs, identity_info, false) !=
|
Check(ShaderGetIdPS(regs, identity_info, false) != ShaderGetIdPS(regs, reversed_info, false),
|
||||||
ShaderGetIdPS(regs, reversed_info, false),
|
|
||||||
"pixel shader cache identity omitted the render-target export mapping");
|
"pixel shader cache identity omitted the render-target export mapping");
|
||||||
|
|
||||||
regs.ps_regs.data_addr = reinterpret_cast<uint64_t>(shader);
|
regs.ps_regs.data_addr = reinterpret_cast<uint64_t>(shader);
|
||||||
@@ -7006,8 +6997,7 @@ void TestNewShaderRecompilerFlatAddressProvenanceBoundaries() {
|
|||||||
options.flat_memory_base = 0;
|
options.flat_memory_base = 0;
|
||||||
ShaderRecompiler::CompileResult result;
|
ShaderRecompiler::CompileResult result;
|
||||||
std::string error;
|
std::string error;
|
||||||
const bool compiled =
|
const bool compiled = ShaderRecompiler::TryRecompile(segmented_shader, options, result, &error);
|
||||||
ShaderRecompiler::TryRecompile(segmented_shader, options, result, &error);
|
|
||||||
Check(compiled, error.c_str());
|
Check(compiled, error.c_str());
|
||||||
Check(result.program.info.addresses.size() == 2,
|
Check(result.program.info.addresses.size() == 2,
|
||||||
"segmented address resources were not tracked independently");
|
"segmented address resources were not tracked independently");
|
||||||
|
|||||||
Reference in New Issue
Block a user