mirror of
https://github.com/KytyPS5/KytyPS5.git
synced 2026-10-21 03:16:54 +02:00
Compare commits
5 Commits
| Author | SHA1 | Date | |
|---|---|---|---|
| cdb64bfd4f | |||
| e5dcfdb87b | |||
| 984010227f | |||
| 57c97ebd86 | |||
| dc3cd2d2c9 |
@@ -876,64 +876,15 @@ void CommandProcessor::DrawIndexOffset(uint32_t index_offset, uint32_t index_cou
|
||||
}
|
||||
|
||||
void CommandProcessor::DrawIndirect(uint32_t data_offset, uint32_t draw_initiator, bool indexed) {
|
||||
EXIT_NOT_IMPLEMENTED((draw_initiator & ~0x20u) != 2u);
|
||||
EXIT_NOT_IMPLEMENTED(m_draw_indirect_args_base_addr == 0);
|
||||
|
||||
const auto* args_addr =
|
||||
reinterpret_cast<const void*>(m_draw_indirect_args_base_addr + data_offset);
|
||||
|
||||
if (!indexed) {
|
||||
DrawIndirectArgs args {};
|
||||
std::memcpy(&args, args_addr, sizeof(args));
|
||||
m_num_instances = args.instance_count;
|
||||
DrawIndexAuto({.vertex_count = args.vertex_count_per_instance,
|
||||
.instance_count = args.instance_count,
|
||||
.first_vertex = args.start_vertex_location,
|
||||
.first_instance = args.start_instance_location,
|
||||
.offset_source = DrawOffsetSource::IndirectArgs});
|
||||
return;
|
||||
}
|
||||
|
||||
DrawIndexedIndirectArgs args {};
|
||||
std::memcpy(&args, args_addr, sizeof(args));
|
||||
|
||||
uint64_t index_size = 0;
|
||||
switch (m_index_type_and_size) {
|
||||
case 0: index_size = 2; break;
|
||||
case 1: index_size = 4; break;
|
||||
case 2: index_size = 1; break;
|
||||
default: EXIT("unknown index_type_and_size: %u\n", m_index_type_and_size);
|
||||
}
|
||||
|
||||
auto* index_addr = reinterpret_cast<const void*>(
|
||||
m_index_base_addr + static_cast<uint64_t>(args.start_index_location) * index_size);
|
||||
|
||||
const uint32_t index_count =
|
||||
(m_index_buffer_size != 0 ? std::min(args.index_count_per_instance, m_index_buffer_size)
|
||||
: args.index_count_per_instance);
|
||||
if (GraphicsRunDebugDumpEnabled() && index_count != args.index_count_per_instance) {
|
||||
static std::atomic<uint32_t> log_count {0};
|
||||
if (log_count.fetch_add(1, std::memory_order_relaxed) < 64) {
|
||||
LOGF("\t DrawIndexIndirect: clamped index_count from %" PRIu32 " to %" PRIu32
|
||||
" using INDEX_BUFFER_SIZE\n",
|
||||
args.index_count_per_instance, index_count);
|
||||
}
|
||||
}
|
||||
|
||||
m_num_instances = args.instance_count;
|
||||
DrawIndex({.index_count = index_count,
|
||||
.index_addr = index_addr,
|
||||
.instance_count = args.instance_count,
|
||||
.base_vertex = static_cast<int32_t>(args.base_vertex_location),
|
||||
.first_instance = args.start_instance_location,
|
||||
.offset_source = DrawOffsetSource::IndirectArgs});
|
||||
const auto args_size = indexed ? sizeof(DrawIndexedIndirectArgs) : sizeof(DrawIndirectArgs);
|
||||
DrawIndirectMulti(data_offset, 1, nullptr, args_size, draw_initiator, indexed);
|
||||
}
|
||||
|
||||
void CommandProcessor::DrawIndirectMulti(uint32_t data_offset, uint32_t max_count_or_count,
|
||||
const volatile uint32_t* count_addr,
|
||||
uint32_t stride_in_bytes, uint32_t draw_initiator,
|
||||
bool indexed) {
|
||||
EXIT_NOT_IMPLEMENTED((draw_initiator & ~0x20u) != 2u);
|
||||
EXIT_NOT_IMPLEMENTED((draw_initiator & ~0x20u) != (indexed ? 0u : 2u));
|
||||
EXIT_NOT_IMPLEMENTED(m_draw_indirect_args_base_addr == 0);
|
||||
|
||||
uint32_t draw_count = max_count_or_count;
|
||||
|
||||
@@ -315,8 +315,6 @@ void RenderExecutor::ResolveRenderDepthTarget(CommandBuffer& buffer, RenderDepth
|
||||
r.depth_compare_op = static_cast<vk::CompareOp>(dc.zfunc);
|
||||
|
||||
r.depth_bounds_test_enable = dc.depth_bounds_enable;
|
||||
r.depth_min_bounds = hw.GetDepthBoundsMin();
|
||||
r.depth_max_bounds = hw.GetDepthBoundsMax();
|
||||
|
||||
r.stencil_clear_enable =
|
||||
has_stencil && rc.stencil_clear_enable && !z.depth_view.stencil_write_disable;
|
||||
|
||||
@@ -29,8 +29,6 @@ struct RenderDepthInfo {
|
||||
bool depth_write_enable = false;
|
||||
vk::CompareOp depth_compare_op = vk::CompareOp::eNever;
|
||||
bool depth_bounds_test_enable = false;
|
||||
float depth_min_bounds = 0.0f;
|
||||
float depth_max_bounds = 0.0f;
|
||||
bool stencil_clear_enable = false;
|
||||
uint8_t stencil_clear_value = 0;
|
||||
bool stencil_test_enable = false;
|
||||
|
||||
@@ -390,7 +390,28 @@ inline constexpr std::array<VideoOutFormatPolicy, 7> VIDEO_OUT_FORMAT_POLICIES {
|
||||
const auto encoded = static_cast<float>(value & 0xffu) / 255.0f;
|
||||
return encoded <= 0.04045f ? encoded / 12.92f : std::pow((encoded + 0.055f) / 1.055f, 2.4f);
|
||||
};
|
||||
const auto float16 = [](uint32_t value) {
|
||||
const auto sign = (value & 0x8000u) << 16u;
|
||||
const auto exponent = (value >> 10u) & 0x1fu;
|
||||
const auto mantissa = value & 0x3ffu;
|
||||
if (exponent == 0) {
|
||||
return std::copysign(static_cast<float>(mantissa) * 0x1p-24f,
|
||||
std::bit_cast<float>(sign));
|
||||
}
|
||||
return std::bit_cast<float>(sign | ((exponent == 31 ? 255u : exponent + 112u) << 23u) |
|
||||
(mantissa << 13u));
|
||||
};
|
||||
switch (format) {
|
||||
case vk::Format::eR16G16Sfloat:
|
||||
next.float32[1] = float16(packed >> 16u);
|
||||
[[fallthrough]];
|
||||
case vk::Format::eR16Sfloat: next.float32[0] = float16(packed); break;
|
||||
case vk::Format::eR16G16Unorm:
|
||||
next.float32[1] = static_cast<float>(packed >> 16u) / 65535.0f;
|
||||
[[fallthrough]];
|
||||
case vk::Format::eR16Unorm:
|
||||
next.float32[0] = static_cast<float>(packed & 0xffffu) / 65535.0f;
|
||||
break;
|
||||
// A single-plane float target carries its clear as raw float bits, the same encoding the
|
||||
// depth decoder below uses. Without this the clear is discarded and the target keeps stale
|
||||
// contents.
|
||||
@@ -443,6 +464,10 @@ inline constexpr std::array<VideoOutFormatPolicy, 7> VIDEO_OUT_FORMAT_POLICIES {
|
||||
// Keep this separate: the first register word alone cannot describe a 64-bit color.
|
||||
[[nodiscard]] inline bool DecodeColorDwordFill(vk::Format format, uint32_t packed,
|
||||
vk::ClearColorValue& clear) {
|
||||
if ((format == vk::Format::eR16Sfloat || format == vk::Format::eR16Unorm) &&
|
||||
(packed & 0xffffu) != (packed >> 16u)) {
|
||||
return false;
|
||||
}
|
||||
if (format == vk::Format::eR16G16B16A16Sfloat) {
|
||||
if (packed != 0) {
|
||||
return false;
|
||||
|
||||
@@ -807,30 +807,52 @@ void RenderExecutor::PrepareBindings(const ShaderStageRuntime& runtime,
|
||||
}
|
||||
}
|
||||
|
||||
void RenderExecutor::FindBuffers(PreparedBindings& prepared) {
|
||||
void RenderExecutor::FindBuffers(std::span<PreparedBindings* const> stages) {
|
||||
KYTY_PROFILER_FUNCTION();
|
||||
EXIT_IF(prepared.runtime == nullptr || !*prepared.runtime);
|
||||
const auto& program = *prepared.runtime->program;
|
||||
const auto& snapshot = *prepared.runtime->resources;
|
||||
auto& cache = m_context.GetBufferCache();
|
||||
|
||||
prepared.buffer_sources.clear();
|
||||
const auto& layout = program.bindings;
|
||||
if (layout.memory_offset_count == 0) {
|
||||
return;
|
||||
}
|
||||
const auto& resources = layout.descriptors.front().resources;
|
||||
prepared.buffer_sources.reserve(resources.size());
|
||||
for (const auto resource: resources) {
|
||||
auto descriptor = DecodeNativeDescriptor<ShaderBufferResource>(snapshot.buffers[resource]);
|
||||
const auto address = descriptor.Base48();
|
||||
const auto requested_size = descriptor.GetSize();
|
||||
if (address == 0 || requested_size == 0) {
|
||||
prepared.buffer_sources.push_back({});
|
||||
auto& cache = m_context.GetBufferCache();
|
||||
for (auto* stage: stages) {
|
||||
auto& prepared = *stage;
|
||||
EXIT_IF(prepared.runtime == nullptr || !*prepared.runtime);
|
||||
const auto& program = *prepared.runtime->program;
|
||||
const auto& snapshot = *prepared.runtime->resources;
|
||||
prepared.buffer_sources.clear();
|
||||
const auto& layout = program.bindings;
|
||||
if (layout.memory_offset_count == 0) {
|
||||
continue;
|
||||
}
|
||||
const auto size = Libs::LibKernel::Memory::ClampRangeSize(address, requested_size);
|
||||
prepared.buffer_sources.push_back({address, size, cache.FindBuffer(address, size)});
|
||||
const auto& resources = layout.descriptors.front().resources;
|
||||
prepared.buffer_sources.reserve(resources.size());
|
||||
for (const auto resource: resources) {
|
||||
const auto descriptor = DecodeNativeDescriptor<ShaderBufferResource>(snapshot.buffers[resource]);
|
||||
const auto address = descriptor.Base48();
|
||||
auto size = descriptor.GetSize();
|
||||
if (address == 0 || size == 0) {
|
||||
prepared.buffer_sources.push_back({});
|
||||
continue;
|
||||
}
|
||||
if (descriptor.NumRecords() == UINT32_MAX) {
|
||||
if (program.info.buffers[resource].written) {
|
||||
// Sentinel write ranges end before tables captured by any bound stage.
|
||||
for (const auto* reader: stages) {
|
||||
for (const auto [read_address, read_size]: reader->runtime->resources->specialization_reads) {
|
||||
if (read_size == 0) continue;
|
||||
if (read_address > address) {
|
||||
size = std::min(size, read_address - address);
|
||||
} else if (address - read_address < read_size) {
|
||||
EXIT("scalar resource reads overlap a shader buffer write\n");
|
||||
}
|
||||
}
|
||||
}
|
||||
} else {
|
||||
const auto& graphics = m_context.GetGraphics();
|
||||
const auto limit = uint64_t {graphics.GetPhysicalDeviceProperties().limits.maxStorageBufferRange} -
|
||||
(graphics.StorageMinAlignment() - 1);
|
||||
size = std::min({size, uint64_t {256} * 1024 * 1024, limit});
|
||||
}
|
||||
}
|
||||
size = Libs::LibKernel::Memory::ClampRangeSize(address, size);
|
||||
prepared.buffer_sources.push_back({address, size, cache.FindBuffer(address, size)});
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -919,8 +941,8 @@ void RenderExecutor::RebindImages(PreparedBindings& prepared) {
|
||||
void RenderExecutor::PrepareGraphicsBindings(std::span<PreparedBindings* const> stages,
|
||||
std::span<RenderColorInfo> colors) {
|
||||
bool uses_dma = false;
|
||||
FindBuffers(stages);
|
||||
for (auto* stage: stages) {
|
||||
FindBuffers(*stage);
|
||||
uses_dma |= stage->runtime->program->info.uses_dma;
|
||||
}
|
||||
if (uses_dma) {
|
||||
|
||||
@@ -831,9 +831,6 @@ PipelineCache::Pipeline& PipelineCache::GetGraphicsPipeline(
|
||||
if (static_params.sample_shading_enable && !m_graphics.sample_rate_shading_enabled) {
|
||||
EXIT("Pipeline: sample-rate shading is required but unsupported by the host\n");
|
||||
}
|
||||
static_params.depth_bounds_test_enable = depth.depth_bounds_test_enable;
|
||||
static_params.depth_min_bounds = depth.depth_min_bounds;
|
||||
static_params.depth_max_bounds = depth.depth_max_bounds;
|
||||
const bool rect_list = Prospero::IsRectList(command.GetUserConfig().GetPrimType());
|
||||
static_params.cull_back = !rect_list && mc.cull_back;
|
||||
static_params.cull_front = !rect_list && mc.cull_front;
|
||||
|
||||
@@ -38,9 +38,6 @@ struct PipelineStaticParameters {
|
||||
bool primitive_restart_enable = false;
|
||||
uint32_t samples = 1;
|
||||
bool sample_shading_enable = false;
|
||||
bool depth_bounds_test_enable = false;
|
||||
float depth_min_bounds = 0.0f;
|
||||
float depth_max_bounds = 0.0f;
|
||||
uint32_t color_mask[RENDER_COLOR_ATTACHMENTS_MAX] = {};
|
||||
bool cull_front = false;
|
||||
bool cull_back = false;
|
||||
@@ -64,7 +61,7 @@ struct PipelineStaticParameters {
|
||||
static_assert(std::is_trivially_copyable_v<PipelineStaticParameters>);
|
||||
static_assert(std::is_standard_layout_v<PipelineStaticParameters>);
|
||||
static_assert(alignof(PipelineStaticParameters) == 1);
|
||||
static_assert(sizeof(PipelineStaticParameters) == 125);
|
||||
static_assert(sizeof(PipelineStaticParameters) == 116);
|
||||
|
||||
struct PipelineRenderingState {
|
||||
std::array<vk::Format, RENDER_COLOR_ATTACHMENTS_MAX> color_formats {};
|
||||
|
||||
@@ -471,14 +471,6 @@ void CreatePipelineInternal(GraphicContext& graphics, PipelineCache::Pipeline& p
|
||||
EXIT_NOT_IMPLEMENTED(pipeline.pipeline_layout == nullptr);
|
||||
|
||||
vk::PipelineDepthStencilStateCreateInfo depth_stencil_info {};
|
||||
depth_stencil_info.depthBoundsTestEnable =
|
||||
#if defined(__APPLE__)
|
||||
VK_FALSE; // MoltenVK lacks the depthBounds feature; depth-bounds testing is disabled
|
||||
#else
|
||||
(static_params.depth_bounds_test_enable ? VK_TRUE : VK_FALSE);
|
||||
#endif
|
||||
depth_stencil_info.minDepthBounds = static_params.depth_min_bounds;
|
||||
depth_stencil_info.maxDepthBounds = static_params.depth_max_bounds;
|
||||
|
||||
std::vector<vk::DynamicState> dynamic_states {
|
||||
vk::DynamicState::eViewportWithCount,
|
||||
@@ -497,6 +489,8 @@ void CreatePipelineInternal(GraphicContext& graphics, PipelineCache::Pipeline& p
|
||||
vk::DynamicState::eBlendConstants,
|
||||
};
|
||||
#if !defined(__APPLE__)
|
||||
dynamic_states.push_back(vk::DynamicState::eDepthBoundsTestEnable);
|
||||
dynamic_states.push_back(vk::DynamicState::eDepthBounds);
|
||||
if (rendering.color_count != 0) {
|
||||
dynamic_states.push_back(vk::DynamicState::eColorWriteEnableEXT);
|
||||
}
|
||||
|
||||
@@ -161,7 +161,7 @@ public:
|
||||
uint32_t mode);
|
||||
|
||||
void PrepareBindings(const ShaderStageRuntime& runtime, PreparedBindings& prepared);
|
||||
void FindBuffers(PreparedBindings& bindings);
|
||||
void FindBuffers(std::span<PreparedBindings* const> stages);
|
||||
void RebindBuffers(PreparedBindings& bindings);
|
||||
void RebindImages(PreparedBindings& bindings);
|
||||
void CommitBindings(CommandBuffer& buffer, vk::PipelineBindPoint pipeline_bind_point,
|
||||
|
||||
@@ -342,7 +342,8 @@ void RenderExecutor::DispatchDirect(uint64_t submit_id, CommandBuffer& buffer,
|
||||
std::copy(std::begin(input_info.dispatch_threads_num), std::end(input_info.dispatch_threads_num),
|
||||
bindings.shader_data.begin() + program.bindings.dispatch_thread_dword);
|
||||
}
|
||||
FindBuffers(bindings);
|
||||
PreparedBindings* descriptor_stage = &bindings;
|
||||
FindBuffers(std::span {&descriptor_stage, 1u});
|
||||
if (program.info.uses_dma) {
|
||||
m_context.PrepareBda();
|
||||
}
|
||||
@@ -351,7 +352,6 @@ void RenderExecutor::DispatchDirect(uint64_t submit_id, CommandBuffer& buffer,
|
||||
RebindBuffers(bindings);
|
||||
|
||||
auto vk_buffer = buffer.Handle();
|
||||
PreparedBindings* descriptor_stage = &bindings;
|
||||
CommitBindings(buffer, vk::PipelineBindPoint::eCompute, pipeline,
|
||||
std::span {&descriptor_stage, 1u});
|
||||
bool has_storage_writes = bindings.shared_memory.buffer != nullptr ||
|
||||
@@ -457,7 +457,8 @@ void RenderExecutor::DispatchIndirect(uint64_t submit_id, CommandBuffer& buffer,
|
||||
auto& pipeline = m_context.GetPipelineCache().GetComputePipeline(input_info, compute_program);
|
||||
auto& bindings = m_compute_bindings;
|
||||
PrepareBindings(input_info.stage, bindings);
|
||||
FindBuffers(bindings);
|
||||
PreparedBindings* descriptor_stage = &bindings;
|
||||
FindBuffers(std::span {&descriptor_stage, 1u});
|
||||
const auto& program = *input_info.stage.program;
|
||||
if (program.info.uses_dma) {
|
||||
m_context.PrepareBda();
|
||||
@@ -469,7 +470,6 @@ void RenderExecutor::DispatchIndirect(uint64_t submit_id, CommandBuffer& buffer,
|
||||
args_addr, sizeof(vk::DispatchIndirectCommand), false);
|
||||
EXIT_IF(args_buffer == nullptr || (args_offset & 3u) != 0);
|
||||
RebindBuffers(bindings);
|
||||
PreparedBindings* descriptor_stage = &bindings;
|
||||
CommitBindings(buffer, vk::PipelineBindPoint::eCompute, pipeline,
|
||||
std::span {&descriptor_stage, 1u});
|
||||
const auto vk_buffer = buffer.Handle();
|
||||
|
||||
@@ -379,6 +379,12 @@ static void SetGraphicsDynamicParams(const CommandBuffer& buffer, vk::CommandBuf
|
||||
vk_buffer.setDepthTestEnable(depth.depth_test_enable ? VK_TRUE : VK_FALSE);
|
||||
vk_buffer.setDepthWriteEnable(depth.depth_write_enable ? VK_TRUE : VK_FALSE);
|
||||
vk_buffer.setDepthCompareOp(depth.depth_compare_op);
|
||||
#if !defined(__APPLE__)
|
||||
vk_buffer.setDepthBoundsTestEnable(depth.depth_bounds_test_enable ? VK_TRUE : VK_FALSE);
|
||||
if (depth.depth_bounds_test_enable) {
|
||||
vk_buffer.setDepthBounds(ctx.GetDepthBoundsMin(), ctx.GetDepthBoundsMax());
|
||||
}
|
||||
#endif
|
||||
|
||||
const auto& mode = ctx.GetModeControl();
|
||||
const auto& poly_offset = ctx.GetPolyOffset();
|
||||
|
||||
+17
-5
@@ -2529,7 +2529,6 @@ static uint64_t decode_indirect_modifier_patch_offsets(uint64_t modifier, bool i
|
||||
if (indexed && (low & 0x2u) != 0) {
|
||||
base_vtx_loc |= static_cast<uint64_t>(sgpr_base + extract_modifier_bits(low, 14u, 5u))
|
||||
<< 16u;
|
||||
base_vtx_loc |= 1ull << 59u;
|
||||
}
|
||||
|
||||
return base_vtx_loc | (start_inst_loc << 32u);
|
||||
@@ -2787,13 +2786,15 @@ uint32_t* KYTY_SYSV_ABI AgcDcbDrawIndexIndirect(CommandBuffer* buf, uint32_t dat
|
||||
|
||||
EXIT_NOT_IMPLEMENTED(cmd == nullptr);
|
||||
|
||||
const auto patch_offsets = decode_indirect_modifier_patch_offsets(modifier, true);
|
||||
// Single indexed draws store the index-offset enable in word 3, bit 28.
|
||||
const auto patch_offsets = decode_indirect_modifier_patch_offsets(modifier, true) |
|
||||
((modifier & 0x2ull) << 59u);
|
||||
|
||||
cmd[0] = KYTY_PM4(5, Pm4::IT_DRAW_INDEX_INDIRECT, 0u);
|
||||
cmd[1] = data_offset_in_bytes;
|
||||
cmd[2] = static_cast<uint32_t>(patch_offsets);
|
||||
cmd[3] = static_cast<uint32_t>(patch_offsets >> 32u);
|
||||
cmd[4] = decode_indirect_draw_initiator(modifier);
|
||||
cmd[4] = decode_draw_index_initiator(modifier);
|
||||
|
||||
return cmd;
|
||||
}
|
||||
@@ -2914,19 +2915,30 @@ uint32_t* KYTY_SYSV_ABI AgcDcbDrawIndexIndirectMulti(CommandBuffer* buf,
|
||||
|
||||
EXIT_NOT_IMPLEMENTED(cmd == nullptr);
|
||||
|
||||
const auto low = static_cast<uint32_t>(modifier);
|
||||
const auto sgpr_base = indirect_modifier_sgpr_base(low);
|
||||
const auto patch_offsets = decode_indirect_modifier_patch_offsets(modifier, true);
|
||||
const auto count_vaddr = reinterpret_cast<uint64_t>(count_addr);
|
||||
|
||||
uint32_t draw_index_location = 0x280u;
|
||||
if ((low & 0x8u) != 0) {
|
||||
draw_index_location = sgpr_base + extract_modifier_bits(low, 24u, 5u);
|
||||
}
|
||||
// Multi indexed draws store the index-offset enable in word 4, bit 28.
|
||||
const auto draw_control = draw_index_location | ((low & 0x10u) << 23u) |
|
||||
((low & 0x2u) << 27u) | ((count_indirect & 0x1u) << 30u) |
|
||||
((low & 0x8u) << 28u);
|
||||
|
||||
cmd[0] = KYTY_PM4(10, Pm4::IT_DRAW_INDEX_INDIRECT_MULTI, 0u);
|
||||
cmd[1] = data_offset_in_bytes;
|
||||
cmd[2] = static_cast<uint32_t>(patch_offsets);
|
||||
cmd[3] = static_cast<uint32_t>(patch_offsets >> 32u);
|
||||
cmd[4] = (count_indirect & 1u) << 30u;
|
||||
cmd[4] = draw_control;
|
||||
cmd[5] = max_count_or_count;
|
||||
cmd[6] = static_cast<uint32_t>(count_vaddr) & ~0x3u;
|
||||
cmd[7] = static_cast<uint32_t>(count_vaddr >> 32u);
|
||||
cmd[8] = stride_in_bytes;
|
||||
cmd[9] = decode_indirect_draw_initiator(modifier);
|
||||
cmd[9] = decode_draw_index_initiator(modifier);
|
||||
|
||||
return cmd;
|
||||
}
|
||||
|
||||
+55
-23
@@ -599,34 +599,66 @@ static int KYTY_SYSV_ABI HttpUriBuild(char* out, size_t* require, size_t prepare
|
||||
return HTTP_ERROR_INVALID_VALUE;
|
||||
}
|
||||
|
||||
std::string uri;
|
||||
if (src_element->scheme != nullptr) {
|
||||
uri.append(src_element->scheme);
|
||||
uri.push_back(':');
|
||||
}
|
||||
constexpr uint32_t BUILD_WITH_SCHEME = 0x01;
|
||||
constexpr uint32_t BUILD_WITH_HOSTNAME = 0x02;
|
||||
constexpr uint32_t BUILD_WITH_PORT = 0x04;
|
||||
constexpr uint32_t BUILD_WITH_PATH = 0x08;
|
||||
constexpr uint32_t BUILD_WITH_USERNAME = 0x10;
|
||||
constexpr uint32_t BUILD_WITH_PASSWORD = 0x20;
|
||||
constexpr uint32_t BUILD_WITH_QUERY = 0x40;
|
||||
constexpr uint32_t BUILD_WITH_FRAGMENT = 0x80;
|
||||
constexpr uint32_t BUILD_WITH_ALL = 0xff;
|
||||
|
||||
if (src_element->opaque == 0) {
|
||||
if (src_element->hostname != nullptr) {
|
||||
// No component bits preserves the legacy full-URI build.
|
||||
const uint32_t parts = (option & BUILD_WITH_ALL) == 0 ? BUILD_WITH_ALL : option;
|
||||
const bool hierarchical = src_element->opaque == 0 && src_element->hostname != nullptr;
|
||||
const bool with_hostname = (parts & BUILD_WITH_HOSTNAME) != 0 && hierarchical;
|
||||
|
||||
std::string uri;
|
||||
if ((parts & BUILD_WITH_SCHEME) != 0) {
|
||||
if (src_element->scheme != nullptr) {
|
||||
uri.append(src_element->scheme);
|
||||
uri.push_back(':');
|
||||
}
|
||||
// Scheme-only builds include the authority marker.
|
||||
if (hierarchical) {
|
||||
uri.append("//");
|
||||
if (src_element->username != nullptr) {
|
||||
uri.append(src_element->username);
|
||||
if (src_element->password != nullptr) {
|
||||
uri.push_back(':');
|
||||
uri.append(src_element->password);
|
||||
}
|
||||
uri.push_back('@');
|
||||
}
|
||||
uri.append(src_element->hostname);
|
||||
if (src_element->port != 0) {
|
||||
uri.push_back(':');
|
||||
uri.append(std::to_string(src_element->port));
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
AppendUriPart(&uri, src_element->path);
|
||||
AppendUriPart(&uri, src_element->query);
|
||||
AppendUriPart(&uri, src_element->fragment);
|
||||
const bool with_username = (parts & BUILD_WITH_USERNAME) != 0 && src_element->username != nullptr;
|
||||
const bool with_password = (parts & BUILD_WITH_PASSWORD) != 0 && src_element->password != nullptr;
|
||||
if (with_username) {
|
||||
uri.append(src_element->username);
|
||||
}
|
||||
if (with_password) {
|
||||
if (with_username) {
|
||||
uri.push_back(':');
|
||||
}
|
||||
uri.append(src_element->password);
|
||||
}
|
||||
if (with_hostname) {
|
||||
if (with_username || with_password) {
|
||||
uri.push_back('@');
|
||||
}
|
||||
uri.append(src_element->hostname);
|
||||
}
|
||||
if ((parts & BUILD_WITH_PORT) != 0 && hierarchical && src_element->port != 0) {
|
||||
if (with_hostname) {
|
||||
uri.push_back(':');
|
||||
}
|
||||
uri.append(std::to_string(src_element->port));
|
||||
}
|
||||
|
||||
if ((parts & BUILD_WITH_PATH) != 0) {
|
||||
AppendUriPart(&uri, src_element->path);
|
||||
}
|
||||
if ((parts & BUILD_WITH_QUERY) != 0) {
|
||||
AppendUriPart(&uri, src_element->query);
|
||||
}
|
||||
if ((parts & BUILD_WITH_FRAGMENT) != 0) {
|
||||
AppendUriPart(&uri, src_element->fragment);
|
||||
}
|
||||
|
||||
const auto needed = uri.size() + 1;
|
||||
if (require != nullptr) {
|
||||
|
||||
@@ -6,6 +6,7 @@
|
||||
#include <cstdint>
|
||||
#include <cstdio>
|
||||
#include <cstring>
|
||||
#include <string>
|
||||
|
||||
namespace Libs::LibHttp {
|
||||
void InitNet_1_Http(Loader::SymbolDatabase *symbols);
|
||||
@@ -40,14 +41,14 @@ int failures = 0;
|
||||
using HttpUriParse = int(KYTY_SYSV_ABI *)(SceHttpUriElement *, const char *,
|
||||
void *, size_t *, size_t);
|
||||
|
||||
HttpUriParse GetHttpUriParse() {
|
||||
Loader::SymbolDatabase symbols;
|
||||
Libs::LibHttp::InitNet_1_Http(&symbols);
|
||||
const auto *record =
|
||||
symbols.FindByNid("IWalAn-guFs", Loader::SymbolType::Func);
|
||||
using HttpUriBuild = int(KYTY_SYSV_ABI *)(char *, size_t *, size_t,
|
||||
const SceHttpUriElement *, uint32_t);
|
||||
|
||||
template <typename T>
|
||||
T GetHttpFunction(const Loader::SymbolDatabase &symbols, const char *nid) {
|
||||
const auto *record = symbols.FindByNid(nid, Loader::SymbolType::Func);
|
||||
CHECK(record != nullptr);
|
||||
return record != nullptr ? reinterpret_cast<HttpUriParse>(record->vaddr)
|
||||
: nullptr;
|
||||
return record != nullptr ? reinterpret_cast<T>(record->vaddr) : nullptr;
|
||||
}
|
||||
|
||||
template <size_t N>
|
||||
@@ -117,13 +118,61 @@ void TestPresentQuery(HttpUriParse parse) {
|
||||
}
|
||||
}
|
||||
|
||||
std::string BuildWith(HttpUriBuild build, const SceHttpUriElement &element,
|
||||
uint32_t option) {
|
||||
size_t required = 0;
|
||||
CHECK(build(nullptr, &required, 0, &element, option) == 0);
|
||||
std::array<char, 256> out{};
|
||||
size_t filled = 0;
|
||||
CHECK(required <= out.size());
|
||||
CHECK(build(out.data(), &filled, required, &element, option) == 0);
|
||||
CHECK(filled == required);
|
||||
return std::string(out.data());
|
||||
}
|
||||
|
||||
void TestBuildHonoursOption(HttpUriParse parse, HttpUriBuild build) {
|
||||
constexpr char url[] =
|
||||
"https://user:pw@gssdk1.gamesci.com.cn:8443/VersionServerImpl?x=1#frag";
|
||||
size_t required = 0;
|
||||
CHECK(parse(nullptr, url, nullptr, &required, 0) == 0);
|
||||
std::array<char, 256> pool{};
|
||||
SceHttpUriElement element{};
|
||||
CHECK(parse(&element, url, pool.data(), &required, required) == 0);
|
||||
|
||||
constexpr uint32_t scheme = 0x01, hostname = 0x02, port = 0x04, path = 0x08,
|
||||
username = 0x10, password = 0x20, query = 0x40,
|
||||
fragment = 0x80;
|
||||
|
||||
CHECK(BuildWith(build, element, scheme) == "https://");
|
||||
CHECK(BuildWith(build, element, hostname) == "gssdk1.gamesci.com.cn");
|
||||
CHECK(BuildWith(build, element, port) == "8443");
|
||||
CHECK(BuildWith(build, element, path) == "/VersionServerImpl");
|
||||
CHECK(BuildWith(build, element, username) == "user");
|
||||
CHECK(BuildWith(build, element, password) == "pw");
|
||||
CHECK(BuildWith(build, element, query) == "?x=1");
|
||||
CHECK(BuildWith(build, element, fragment) == "#frag");
|
||||
CHECK(BuildWith(build, element, scheme | hostname) ==
|
||||
"https://gssdk1.gamesci.com.cn");
|
||||
CHECK(BuildWith(build, element, hostname | path) ==
|
||||
"gssdk1.gamesci.com.cn/VersionServerImpl");
|
||||
CHECK(BuildWith(build, element, hostname | port) ==
|
||||
"gssdk1.gamesci.com.cn:8443");
|
||||
|
||||
CHECK(BuildWith(build, element, 0xff) == url);
|
||||
CHECK(BuildWith(build, element, 0) == url);
|
||||
}
|
||||
|
||||
} // namespace
|
||||
|
||||
int main() {
|
||||
const auto parse = GetHttpUriParse();
|
||||
if (parse == nullptr) {
|
||||
Loader::SymbolDatabase symbols;
|
||||
Libs::LibHttp::InitNet_1_Http(&symbols);
|
||||
const auto parse = GetHttpFunction<HttpUriParse>(symbols, "IWalAn-guFs");
|
||||
const auto build = GetHttpFunction<HttpUriBuild>(symbols, "5LZA+KPISVA");
|
||||
if (parse == nullptr || build == nullptr) {
|
||||
return 1;
|
||||
}
|
||||
TestBuildHonoursOption(parse, build);
|
||||
TestAbsentQuery(parse);
|
||||
TestEmptyUri(parse);
|
||||
TestPresentQuery(parse);
|
||||
|
||||
@@ -10161,6 +10161,46 @@ public:
|
||||
|
||||
void CheckRenderExecutorColorMetadataClear() {
|
||||
constexpr const char *name = "RenderExecutorColorMetadataClear";
|
||||
struct PackedCase {
|
||||
vk::Format format;
|
||||
uint32_t word;
|
||||
std::array<float, 4> channels;
|
||||
};
|
||||
const std::array packed_cases{
|
||||
PackedCase{vk::Format::eR16Sfloat, 0x7bff7bffu, {65504.0f}},
|
||||
PackedCase{vk::Format::eR16G16Sfloat, 0xbc003c00u, {1.0f, -1.0f}},
|
||||
PackedCase{vk::Format::eR16Sfloat, 0xffff0001u, {0x1p-24f}},
|
||||
PackedCase{vk::Format::eR16Sfloat, 0x00008001u, {-0x1p-24f}},
|
||||
PackedCase{vk::Format::eR16Sfloat, 0x000003ffu, {1023.0f * 0x1p-24f}},
|
||||
PackedCase{vk::Format::eR16Sfloat, 0x00000400u, {0x1p-14f}},
|
||||
PackedCase{vk::Format::eR16G16Sfloat, 0x80000000u, {0.0f, -0.0f}},
|
||||
PackedCase{vk::Format::eR16G16Sfloat, 0xfc007c00u,
|
||||
{std::numeric_limits<float>::infinity(),
|
||||
-std::numeric_limits<float>::infinity()}},
|
||||
PackedCase{vk::Format::eR16Unorm, 0x0000ffffu, {1.0f}},
|
||||
PackedCase{vk::Format::eR16G16Unorm, 0x0000ffffu, {1.0f, 0.0f}},
|
||||
PackedCase{vk::Format::eR16G16Unorm, 0x80000001u,
|
||||
{1.0f / 65535.0f, 32768.0f / 65535.0f}},
|
||||
};
|
||||
for (const auto &test : packed_cases) {
|
||||
vk::ClearColorValue clear{};
|
||||
Require(name, "packed 16-bit clear",
|
||||
DecodePackedColorClear(test.format, test.word, clear) &&
|
||||
std::bit_cast<std::array<uint32_t, 4>>(clear.float32) ==
|
||||
std::bit_cast<std::array<uint32_t, 4>>(test.channels),
|
||||
"16-bit clear lost channel order, sign, range or unused-channel zeros");
|
||||
}
|
||||
vk::ClearColorValue clear{};
|
||||
Require(name, "half NaN clear",
|
||||
DecodePackedColorClear(vk::Format::eR16Sfloat, 0x7e00u, clear) &&
|
||||
std::isnan(clear.float32[0]),
|
||||
"half NaN did not remain NaN");
|
||||
for (const auto format : {vk::Format::eR16Sfloat, vk::Format::eR16Unorm}) {
|
||||
Require(name, "R16 DWORD fill",
|
||||
!DecodeColorDwordFill(format, 0xbc003c00u, clear) &&
|
||||
DecodeColorDwordFill(format, 0x3c003c00u, clear),
|
||||
"alternating R16 pixels were treated as a uniform clear");
|
||||
}
|
||||
constexpr uintptr_t base = 0x0000000204100000ull;
|
||||
constexpr uint64_t allocation_size = 0x200000;
|
||||
constexpr uint64_t allocation_alignment = 0x10000;
|
||||
@@ -10170,6 +10210,9 @@ public:
|
||||
bool reuse_unorm = false;
|
||||
bool cmask = false;
|
||||
bool reuse_mips = false;
|
||||
Prospero::ChannelLayout layout = Prospero::ChannelLayout::k16_16_16_16;
|
||||
Prospero::ChannelType type = Prospero::ChannelType::kFloat;
|
||||
uint32_t clear_word = 0;
|
||||
};
|
||||
constexpr std::array cases{
|
||||
FillCase{0x40404040u, {0, 0x3c000000u}},
|
||||
@@ -10177,6 +10220,16 @@ public:
|
||||
FillCase{0x40404040u, {0, 0x3c000000u}, true},
|
||||
FillCase{0, {0x804020ffu, 0}, false, true},
|
||||
FillCase{0x40404040u, {0, 0x3c000000u}, false, false, true},
|
||||
FillCase{.fill = 0x20202020u, .texel = {0x7bff7bffu},
|
||||
.layout = Prospero::ChannelLayout::k16, .clear_word = 0x7bff7bffu},
|
||||
FillCase{.fill = 0x20202020u, .texel = {0xbc003c00u},
|
||||
.layout = Prospero::ChannelLayout::k16_16, .clear_word = 0xbc003c00u},
|
||||
FillCase{.fill = 0x20202020u, .texel = {0x80008000u},
|
||||
.layout = Prospero::ChannelLayout::k16,
|
||||
.type = Prospero::ChannelType::kUNorm, .clear_word = 0x80008000u},
|
||||
FillCase{.fill = 0x20202020u, .texel = {0x0000ffffu},
|
||||
.layout = Prospero::ChannelLayout::k16_16,
|
||||
.type = Prospero::ChannelType::kUNorm, .clear_word = 0x0000ffffu},
|
||||
};
|
||||
EnsureRuntimeContext();
|
||||
// Astro's generic metadata fill, through S_ENDPGM; trailing debug data is omitted.
|
||||
@@ -10214,10 +10267,6 @@ public:
|
||||
{.type = Prospero::ShaderBinaryType::kCs,
|
||||
.code_size_bytes = sizeof(native_fill)});
|
||||
}
|
||||
TileSizeAlign dcc_size{};
|
||||
(void)TileGetDccSize(512, 256, 1, 8, 1,
|
||||
Prospero::TileMode::kRenderTarget, dcc_size, 0);
|
||||
|
||||
int64_t direct_offset = -1;
|
||||
Require(name, "direct allocation",
|
||||
Libs::LibKernel::Memory::KernelAllocateDirectMemory(
|
||||
@@ -10234,11 +10283,19 @@ public:
|
||||
std::memset(mapped, 0, allocation_size);
|
||||
|
||||
for (const auto &fill_case : cases) {
|
||||
const auto layout = fill_case.cmask ? Prospero::ChannelLayout::k8_8_8_8 : fill_case.layout;
|
||||
const auto type = fill_case.cmask ? Prospero::ChannelType::kUNorm : fill_case.type;
|
||||
const auto bytes_per_pixel = TextureGetRenderTargetFormat(
|
||||
layout, type, Prospero::ChannelOrder::kStandard).bytes_per_element;
|
||||
const uint32_t probe_width = bytes_per_pixel == 2 ? 2 : 1;
|
||||
TileSizeAlign dcc_size{};
|
||||
(void)TileGetDccSize(512, 256, 1, bytes_per_pixel, 1,
|
||||
Prospero::TileMode::kRenderTarget, dcc_size, 0);
|
||||
const uint64_t dcc_address = base + (fill_case.reuse_mips ? 0x160000 : 0x100000);
|
||||
const uint32_t selected_layer = fill_case.cmask ? 1 : 0;
|
||||
const uint64_t metadata_size = fill_case.cmask ? 0x2000 : dcc_size.size;
|
||||
const std::vector<u32> expected(
|
||||
fill_case.texel.begin(), fill_case.texel.begin() + (fill_case.cmask ? 1 : 2));
|
||||
fill_case.texel.begin(), fill_case.texel.begin() + std::max(bytes_per_pixel / 4u, 1u));
|
||||
RenderContext context(m_runtime_context);
|
||||
HW::Context registers{};
|
||||
HW::UserConfig user_config{};
|
||||
@@ -10251,10 +10308,8 @@ public:
|
||||
registers.SetColorInfo(
|
||||
0, {.cmask_fast_clear_enable = !fill_case.reuse_unorm,
|
||||
.dcc_compression_enable = !fill_case.cmask,
|
||||
.format = fill_case.cmask ? Prospero::ChannelLayout::k8_8_8_8
|
||||
: Prospero::ChannelLayout::k16_16_16_16,
|
||||
.channel_type = fill_case.cmask ? Prospero::ChannelType::kUNorm
|
||||
: Prospero::ChannelType::kFloat,
|
||||
.format = layout,
|
||||
.channel_type = type,
|
||||
.channel_order = Prospero::ChannelOrder::kStandard});
|
||||
registers.SetColorView(0, {.base_array_slice_index = selected_layer,
|
||||
.last_array_slice_index = selected_layer});
|
||||
@@ -10265,7 +10320,7 @@ public:
|
||||
.metadata_pipe_aligned = true});
|
||||
registers.SetColorDccAddr(0, {.addr = dcc_address});
|
||||
registers.SetColorCmask(0, {.addr = dcc_address});
|
||||
registers.SetColorClearWord0(0, {.word0 = fill_case.cmask ? expected[0] : 0});
|
||||
registers.SetColorClearWord0(0, {.word0 = fill_case.cmask ? expected[0] : fill_case.clear_word});
|
||||
registers.SetColorClearWord1(0, {.word1 = 0});
|
||||
registers.SetRenderTargetMask(0x0f);
|
||||
scheduler.Begin(registers, user_config, shaders);
|
||||
@@ -10354,12 +10409,13 @@ public:
|
||||
!texture_cache.IsMeta(dcc_address),
|
||||
"color metadata did not retain its type and full native allocation");
|
||||
const auto read_texel = [&] {
|
||||
return ReadCachedTexel(name, context, color.image_id, {}, {1, 1, 1}, selected_layer);
|
||||
return ReadCachedTexel(name, context, color.image_id, {}, {probe_width, 1, 1}, selected_layer);
|
||||
};
|
||||
Require(name, "GPU color clear value",
|
||||
read_texel() == expected &&
|
||||
ReadCachedTexel(name, context, color.image_id, {511, 255, 0},
|
||||
{1, 1, 1}, selected_layer) == expected,
|
||||
ReadCachedTexel(name, context, color.image_id,
|
||||
{static_cast<int32_t>(512 - probe_width), 255, 0},
|
||||
{probe_width, 1, 1}, selected_layer) == expected,
|
||||
"native metadata did not materialize the expected color");
|
||||
if (fill_case.reuse_mips) {
|
||||
const auto &image = texture_cache.GetImage(color.image_id);
|
||||
@@ -10405,6 +10461,9 @@ public:
|
||||
painted = {0x0000ffffu, 0xffffffffu};
|
||||
} else if (fill_case.cmask) {
|
||||
painted = {0xffff00ffu};
|
||||
} else if (bytes_per_pixel <= 4) {
|
||||
const uint32_t one = type == Prospero::ChannelType::kFloat ? 0x3c00u : 0xffffu;
|
||||
painted = {bytes_per_pixel == 2 ? one | (one << 16u) : one};
|
||||
}
|
||||
const auto retains_painted_texel = [&] {
|
||||
const auto texels = ReadCachedTexel(name, context, color.image_id, {},
|
||||
@@ -11711,7 +11770,8 @@ public:
|
||||
value.dword_count = 4;
|
||||
PreparedBindings buffer_bindings;
|
||||
executor.PrepareBindings(buffer_runtime, buffer_bindings);
|
||||
executor.FindBuffers(buffer_bindings);
|
||||
std::array<PreparedBindings *, 1> buffer_stages{&buffer_bindings};
|
||||
executor.FindBuffers(buffer_stages);
|
||||
const auto original_id = buffer_bindings.buffer_sources[0].id;
|
||||
|
||||
auto &buffer_cache = resources.GetBufferCache();
|
||||
@@ -11744,7 +11804,8 @@ public:
|
||||
ShaderStageRuntime runtime{&buffer_program, &snapshot};
|
||||
PreparedBindings bindings;
|
||||
executor.PrepareBindings(runtime, bindings);
|
||||
executor.FindBuffers(bindings);
|
||||
std::array<PreparedBindings *, 1> stages{&bindings};
|
||||
executor.FindBuffers(stages);
|
||||
executor.RebindBuffers(bindings);
|
||||
const auto &owner = resources.GetBufferCache().GetBuffer(bindings.buffer_sources[0].id);
|
||||
Require(name, "halfword buffer base and exact tail",
|
||||
@@ -11755,6 +11816,74 @@ public:
|
||||
"native binding lost the byte adjustment or rounded the final halfword range");
|
||||
}
|
||||
|
||||
{
|
||||
constexpr uint64_t address = base + allocation_size - 0x4000 + 12;
|
||||
constexpr uint64_t mapped_tail = base + allocation_size - address;
|
||||
auto writer = make_buffer_program(ShaderType::Vertex, {.written = true});
|
||||
ShaderRecompiler::IR::CompiledShaderInfo reader{};
|
||||
reader.stage = ShaderType::Pixel;
|
||||
ShaderRecompiler::IR::ResourceSnapshot writer_snapshot, reader_snapshot;
|
||||
ShaderBufferResource descriptor{};
|
||||
descriptor.UpdateAddress48(address);
|
||||
descriptor.fields[2] = UINT32_MAX;
|
||||
auto &value = writer_snapshot.buffers.emplace_back();
|
||||
std::copy_n(descriptor.fields, 4, value.dwords.begin());
|
||||
value.dword_count = 4;
|
||||
ShaderStageRuntime writer_runtime{&writer, &writer_snapshot};
|
||||
ShaderStageRuntime reader_runtime{&reader, &reader_snapshot};
|
||||
PreparedBindings writer_bindings, reader_bindings;
|
||||
const auto check_range = [&](uint64_t expected, const char *description) {
|
||||
executor.PrepareBindings(writer_runtime, writer_bindings);
|
||||
executor.PrepareBindings(reader_runtime, reader_bindings);
|
||||
std::array<PreparedBindings *, 2> stages{&reader_bindings, &writer_bindings};
|
||||
executor.FindBuffers(stages);
|
||||
executor.RebindBuffers(writer_bindings);
|
||||
const auto &source = writer_bindings.buffer_sources[0];
|
||||
const auto &binding = writer_bindings.buffers[0];
|
||||
const auto &owner = resources.GetBufferCache().GetBuffer(source.id);
|
||||
Require(name, description, source.address == address && source.size == expected &&
|
||||
binding.buffer == owner.Handle() && binding.offset <= owner.Offset(address) &&
|
||||
binding.range == expected + owner.Offset(address) - binding.offset,
|
||||
"descriptor discovery or rebind changed the write boundary or base adjustment");
|
||||
};
|
||||
writer_snapshot.specialization_reads = {{address + 0x500, 4},
|
||||
{address - 0x40, 0x40}, {address + 0x80, 0}};
|
||||
reader_snapshot.specialization_reads = {{address + 0x300, 4}, {address + 0x100, 4}};
|
||||
check_range(0x100, "unbounded write stops before nearest cross-stage read");
|
||||
reader_snapshot.specialization_reads.clear();
|
||||
check_range(0x500, "unbounded write stops before same-stage read");
|
||||
writer_snapshot.specialization_reads = {{address - 0x40, 0x40},
|
||||
{address + mapped_tail + 0x100, 4}};
|
||||
check_range(mapped_tail, "earlier and unmapped reads preserve mapped tail");
|
||||
writer_snapshot.specialization_reads.clear();
|
||||
check_range(mapped_tail, "unbounded write without reads preserves mapped tail");
|
||||
reader_snapshot.specialization_reads = {{address + 0x100, 4}};
|
||||
value.dwords[2] = 0x300;
|
||||
check_range(0x300, "finite write preserves its declared range");
|
||||
value.dwords[2] = UINT32_MAX;
|
||||
writer.info.buffers[0].written = false;
|
||||
writer.info.buffers[0].read = true;
|
||||
check_range(mapped_tail, "read-only sentinel preserves mapped tail");
|
||||
auto &graphics = context.GetGraphics();
|
||||
auto &storage_limit = graphics.physical_device_properties.limits.maxStorageBufferRange;
|
||||
const auto original_limit = storage_limit;
|
||||
storage_limit = 0x2000;
|
||||
value.dwords[1] |= 16u << 16u;
|
||||
check_range(storage_limit - (graphics.StorageMinAlignment() - 1),
|
||||
"read-only structured sentinel reserves native alignment prefix");
|
||||
Require(name, "read-only native range fits device", writer_bindings.buffers[0].range <= storage_limit,
|
||||
"read-only cap omitted the native descriptor alignment prefix");
|
||||
value.dwords[2] = 0x300; // A finite 12 KiB descriptor remains finite despite the mock limit.
|
||||
std::array<PreparedBindings *, 1> stages{&writer_bindings};
|
||||
executor.FindBuffers(stages);
|
||||
Require(name, "finite read exceeds mock cap unchanged", writer_bindings.buffer_sources[0].size == 0x3000,
|
||||
"read-only window changed a finite descriptor's declared range");
|
||||
storage_limit = original_limit;
|
||||
check_range(0x3000, "finite structured read preserves native range");
|
||||
value.dwords[2] = UINT32_MAX;
|
||||
check_range(mapped_tail, "read-only structured sentinel preserves smaller mapping");
|
||||
}
|
||||
|
||||
for (const auto format : {Prospero::BufferFormat::k8UNorm,
|
||||
Prospero::BufferFormat::k16UNorm}) {
|
||||
const uint64_t alias_address = base + 0xe00000 +
|
||||
@@ -14462,7 +14591,8 @@ public:
|
||||
plane_dependency.memoryBarrierCount = 1;
|
||||
plane_dependency.pMemoryBarriers = &plane_barrier;
|
||||
const auto write_plane = [&] {
|
||||
executor.FindBuffers(plane_bindings);
|
||||
std::array<PreparedBindings *, 1> stages{&plane_bindings};
|
||||
executor.FindBuffers(stages);
|
||||
executor.RebindBuffers(plane_bindings);
|
||||
const auto &plane_buffer = plane_bindings.buffers[0];
|
||||
const vk::BufferCopy plane_copy{0, plane_buffer.offset, plane_size};
|
||||
@@ -15619,6 +15749,12 @@ public:
|
||||
cmd.setDepthTestEnable(depth.depth_test_enable);
|
||||
cmd.setDepthWriteEnable(depth.depth_write_enable);
|
||||
cmd.setDepthCompareOp(depth.depth_compare_op);
|
||||
#if !defined(__APPLE__)
|
||||
cmd.setDepthBoundsTestEnable(depth.depth_bounds_test_enable);
|
||||
if (depth.depth_bounds_test_enable) {
|
||||
cmd.setDepthBounds(registers.GetDepthBoundsMin(), registers.GetDepthBoundsMax());
|
||||
}
|
||||
#endif
|
||||
cmd.setDepthBiasEnable(false);
|
||||
cmd.setStencilTestEnable(depth.stencil_test_enable);
|
||||
if (depth.stencil_test_enable) {
|
||||
@@ -15675,6 +15811,26 @@ public:
|
||||
"depth feedback lost a prior write or the read-only draw changed depth");
|
||||
}
|
||||
}
|
||||
depth.depth_test_enable = false;
|
||||
for (uint32_t pass = 0; pass < 3; pass++) {
|
||||
depth.depth_bounds_test_enable = pass != 2;
|
||||
registers.SetDepthBoundsMin(pass == 0 ? 0.5f : 0.625f);
|
||||
registers.SetDepthBoundsMax(pass == 0 ? 0.5625f : 0.6875f);
|
||||
Require(name, "dynamic depth bounds pipeline reuse",
|
||||
pipeline(true, 2, 2).pipeline == filled.pipeline,
|
||||
"changing depth bounds or their enable state created another pipeline");
|
||||
draw(filled);
|
||||
const auto bounded_pixels = read_color();
|
||||
for (size_t index = 0; index < stored_depth.size(); index++) {
|
||||
const auto z = std::bit_cast<float>(stored_depth[index]);
|
||||
const bool passes = !depth.depth_bounds_test_enable ||
|
||||
(z >= registers.GetDepthBoundsMin() &&
|
||||
z <= registers.GetDepthBoundsMax());
|
||||
Require(name, "dynamic stored-depth bounds",
|
||||
bounded_pixels[index * 4] == (passes ? stored_depth[index] : 0u),
|
||||
"depth bounds did not filter stored depth independently of the depth test");
|
||||
}
|
||||
}
|
||||
} else {
|
||||
auto &wireframe = pipeline(true, 1, 1);
|
||||
Require(name, "pipeline cache", filled.pipeline != wireframe.pipeline &&
|
||||
@@ -17876,6 +18032,7 @@ private:
|
||||
"vertex layer output is not supported");
|
||||
m_rasterization_supported = available_features.fillModeNonSolid &&
|
||||
available_features.tessellationShader &&
|
||||
available_features.depthBounds &&
|
||||
available_depth_clip.depthClipEnable &&
|
||||
available_clip_control.depthClipControl &&
|
||||
available_color_write.colorWriteEnable &&
|
||||
@@ -17958,6 +18115,7 @@ private:
|
||||
device_features.shaderFloat64 = available_features.shaderFloat64;
|
||||
device_features.fillModeNonSolid = m_rasterization_supported;
|
||||
device_features.tessellationShader = m_rasterization_supported;
|
||||
device_features.depthBounds = m_rasterization_supported;
|
||||
device_info.pEnabledFeatures = &device_features;
|
||||
std::vector<const char *> device_extensions{
|
||||
VK_KHR_PUSH_DESCRIPTOR_EXTENSION_NAME,
|
||||
@@ -39789,6 +39947,64 @@ void CheckPm4DrawIndirectMultiPacket(RenderContext &renderer) {
|
||||
packet_mismatch &&
|
||||
!Gen5::AgcIsInternalDataPacket(0xc0017904u, invalid_payload),
|
||||
"indirect multi-draw packet differs from the expected stream");
|
||||
|
||||
constexpr uint32_t data_offset = 0x5c08;
|
||||
constexpr uint32_t stride = 32;
|
||||
std::array<uint32_t, data_offset / 4 + stride / 4 + 5> arguments{};
|
||||
const std::array<uint32_t, 5> first{0, 1, 3, 0xfffffffeu, 9};
|
||||
const std::array<uint32_t, 5> second{0, 2, 5, 7, 11};
|
||||
std::copy(first.begin(), first.end(), arguments.begin() + data_offset / 4);
|
||||
std::copy(second.begin(), second.end(), arguments.begin() + (data_offset + stride) / 4);
|
||||
std::array<uint32_t, 8> indices{};
|
||||
processor.BufferInit();
|
||||
processor.SetDrawIndirectArgsBaseAddress(reinterpret_cast<uint64_t>(arguments.data()));
|
||||
processor.SetIndexBaseAddress(reinterpret_cast<uint64_t>(indices.data()));
|
||||
processor.SetIndexType(0);
|
||||
std::array<uint32_t, 5> captured{0xc0032500u, data_offset, 0x94, 0x95, 0};
|
||||
Require("Pm4DrawIndirectMulti", "captured indexed single execution",
|
||||
execute(captured.data(), captured.size()),
|
||||
"indexed indirect draw rejected native source-select zero");
|
||||
// Zero primitive counts still traverse CP argument decoding and renderer entry,
|
||||
// while avoiding unrelated shader and attachment requirements.
|
||||
for (const bool indexed : {false, true}) {
|
||||
for (const bool multi : {false, true}) {
|
||||
for (const uint64_t draw_modifier : {0ull, 0x100ull, 1ull << 32u, (1ull << 32u) | 0x100ull}) {
|
||||
packet.fill(0);
|
||||
dcb.cursor_up = packet.data();
|
||||
auto *buffer = reinterpret_cast<Gen5::CommandBuffer *>(&dcb);
|
||||
alignas(4) uint32_t draw_count = 3;
|
||||
if (multi) {
|
||||
emitted = indexed ? Gen5::AgcDcbDrawIndexIndirectMulti(buffer, data_offset, 1, 2,
|
||||
&draw_count, stride, draw_modifier)
|
||||
: Gen5::AgcDcbDrawIndirectMulti(buffer, data_offset, 1, 2,
|
||||
&draw_count, stride, draw_modifier);
|
||||
} else {
|
||||
emitted = indexed ? Gen5::AgcDcbDrawIndexIndirect(buffer, data_offset, draw_modifier)
|
||||
: Gen5::AgcDcbDrawIndirect(buffer, data_offset, draw_modifier);
|
||||
}
|
||||
const uint32_t begin = multi && !indexed ? 3 : 0;
|
||||
const uint32_t words = multi ? (indexed ? 10 : 16) : 5;
|
||||
const uint32_t opcode = multi ? (indexed ? 0xc0083800u : 0xc0082c00u)
|
||||
: (indexed ? 0xc0032500u : 0xc0032400u);
|
||||
const uint32_t initiator = (indexed ? 0 : 2) |
|
||||
(draw_modifier == 0x100 ? 0x20 : 0);
|
||||
Require("Pm4DrawIndirectMulti", "indexed/auto initiator emission",
|
||||
emitted == packet.data() && dcb.cursor_up == packet.data() + words &&
|
||||
packet[begin] == opcode && packet[begin + 1] == data_offset &&
|
||||
packet[begin + (multi ? 9 : 4)] == initiator,
|
||||
"indirect opcode, source select, or modifier bits differ from the native packet");
|
||||
Require("Pm4DrawIndirectMulti", "single/multi argument execution",
|
||||
execute(packet.data(), words),
|
||||
"indirect draw failed shared argument decoding or padded/count-limited traversal");
|
||||
if (multi) {
|
||||
draw_count = 0;
|
||||
Require("Pm4DrawIndirectMulti", "zero indirect count execution",
|
||||
execute(packet.data(), words), "zero indirect draw count did not complete");
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
renderer.GetCommandScheduler().Finish();
|
||||
std::printf("[host] %-32s ok\n", "Pm4DrawIndirectMulti");
|
||||
}
|
||||
|
||||
|
||||
Reference in New Issue
Block a user